From dff67234df31a99837d7ec726fa085bb754f5769 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 25 Mar 2015 14:47:26 +0800 Subject: [PATCH] partly support time cell --- col.go | 30 +++++++++++++++- date.go | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ format.go | 10 +++--- st_xf_data.go | 17 --------- workbook.go | 25 +++++-------- worksheet.go | 15 -------- xf.go | 12 +++++++ 7 files changed, 151 insertions(+), 56 deletions(-) create mode 100644 date.go delete mode 100644 st_xf_data.go diff --git a/col.go b/col.go index ee1ff93..f4b7227 100644 --- a/col.go +++ b/col.go @@ -41,6 +41,17 @@ type XfRk struct { Rk RK } +func (xf *XfRk) String(wb *WorkBook) string { + switch wb.Xfs[21].formatNo() { + case 27: + if f, e := xf.Rk.Float(); e == nil { + t := TimeFromExcelTime(f, true) + return t.Format("2006.01") //TODO it should be international + } + } + return fmt.Sprintf("%s", xf.Rk.String()) +} + type RK uint32 func (rk RK) String() string { @@ -58,6 +69,23 @@ func (rk RK) String() string { } } +var ErrIsInt = fmt.Errorf("is int") + +func (rk RK) Float() (float64, error) { + multiplied := rk & 1 + isInt := rk & 2 + val := rk >> 2 + if isInt == 0 { + f := math.Float64frombits(uint64(val) << 34) + if multiplied != 0 { + f = f / 100 + } + return f, nil + } else { + return 0.0, ErrIsInt + } +} + type MulrkCol struct { Col Xfrks []XfRk @@ -72,7 +100,7 @@ func (c *MulrkCol) String(wb *WorkBook) []string { var res = make([]string, len(c.Xfrks)) for i := 0; i < len(c.Xfrks); i++ { xfrk := c.Xfrks[i] - res[i] = xfrk.Rk.String() + res[i] = xfrk.String(wb) } return res } diff --git a/date.go b/date.go new file mode 100644 index 0000000..6c0ef1a --- /dev/null +++ b/date.go @@ -0,0 +1,98 @@ +package xls + +import ( + "math" + "time" +) + +const MJD_0 float64 = 2400000.5 +const MJD_JD2000 float64 = 51544.5 + +func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) { + switch { + case -0.5 < julianFraction && julianFraction < 0.5: + julianFraction += 0.5 + case julianFraction >= 0.5: + julianDays += 1 + julianFraction -= 0.5 + case julianFraction <= -0.5: + julianDays -= 1 + julianFraction += 1.5 + } + return julianDays, julianFraction +} + +// Return the integer values for hour, minutes, seconds and +// nanoseconds that comprised a given fraction of a day. +func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) { + f := 5184000000000000 * fraction + nanoseconds = int(math.Mod(f, 1000000000)) + f = f / 1000000000 + seconds = int(math.Mod(f, 60)) + f = f / 3600 + minutes = int(math.Mod(f, 60)) + f = f / 60 + hours = int(f) + return hours, minutes, seconds, nanoseconds +} + +func julianDateToGregorianTime(part1, part2 float64) time.Time { + part1I, part1F := math.Modf(part1) + part2I, part2F := math.Modf(part2) + julianDays := part1I + part2I + julianFraction := part1F + part2F + julianDays, julianFraction = shiftJulianToNoon(julianDays, julianFraction) + day, month, year := doTheFliegelAndVanFlandernAlgorithm(int(julianDays)) + hours, minutes, seconds, nanoseconds := fractionOfADay(julianFraction) + return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC) +} + +// By this point generations of programmers have repeated the +// algorithm sent to the editor of "Communications of the ACM" in 1968 +// (published in CACM, volume 11, number 10, October 1968, p.657). +// None of those programmers seems to have found it necessary to +// explain the constants or variable names set out by Henry F. Fliegel +// and Thomas C. Van Flandern. Maybe one day I'll buy that jounal and +// expand an explanation here - that day is not today. +func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) { + l := jd + 68569 + n := (4 * l) / 146097 + l = l - (146097*n+3)/4 + i := (4000 * (l + 1)) / 1461001 + l = l - (1461*i)/4 + 31 + j := (80 * l) / 2447 + d := l - (2447*j)/80 + l = j / 11 + m := j + 2 - (12 * l) + y := 100*(n-49) + i + l + return d, m, y +} + +// Convert an excelTime representation (stored as a floating point number) to a time.Time. +func TimeFromExcelTime(excelTime float64, date1904 bool) time.Time { + var date time.Time + var intPart int64 = int64(excelTime) + // Excel uses Julian dates prior to March 1st 1900, and + // Gregorian thereafter. + if intPart <= 61 { + const OFFSET1900 = 15018.0 + const OFFSET1904 = 16480.0 + var date time.Time + if date1904 { + date = julianDateToGregorianTime(MJD_0+OFFSET1904, excelTime) + } else { + date = julianDateToGregorianTime(MJD_0+OFFSET1900, excelTime) + } + return date + } + var floatPart float64 = excelTime - float64(intPart) + var dayNanoSeconds float64 = 24 * 60 * 60 * 1000 * 1000 * 1000 + if date1904 { + date = time.Date(1904, 1, 1, 0, 0, 0, 0, time.UTC) + } else { + date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC) + } + durationDays := time.Duration(intPart) * time.Hour * 24 + durationPart := time.Duration(dayNanoSeconds * floatPart) + return date.Add(durationDays).Add(durationPart) +} diff --git a/format.go b/format.go index 547d8e5..35b576c 100644 --- a/format.go +++ b/format.go @@ -1,11 +1,9 @@ package xls -type FormatB struct { - Index uint16 - Size uint16 -} - type Format struct { - b *FormatB + Head struct { + Index uint16 + Size uint16 + } str string } diff --git a/st_xf_data.go b/st_xf_data.go deleted file mode 100644 index 3d49921..0000000 --- a/st_xf_data.go +++ /dev/null @@ -1,17 +0,0 @@ -package xls - -// type st_xf_data struct { -// Font uint16 -// Format uint16 -// Type uint16 -// Align byte -// Rotation byte -// Ident byte -// Usedattr byte -// Linestyle uint32 -// Linecolor uint32 -// Groundcolor uint16 -// } - -type st_xf_data interface { -} diff --git a/workbook.go b/workbook.go index 42484d8..4a2beb6 100644 --- a/workbook.go +++ b/workbook.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "io" + "log" "unicode/utf16" ) @@ -53,8 +54,8 @@ func (w *WorkBook) addFont(font *FontInfo, buf io.ReadSeeker) { w.Fonts = append(w.Fonts, Font{Info: font, Name: name}) } -func (w *WorkBook) addFormat(format *FormatB, buf io.ReadSeeker) { - w.Formats[format.Index] = &Format{b: format, str: w.get_string(buf, uint16(format.Size))} +func (w *WorkBook) addFormat(format *Format) { + w.Formats[format.Head.Index] = format } func (wb *WorkBook) parseBof(buf io.ReadSeeker, b *BOF, pre *BOF, offset_pre int) (after *BOF, offset int) { @@ -119,21 +120,11 @@ func (wb *WorkBook) parseBof(buf io.ReadSeeker, b *BOF, pre *BOF, offset_pre int binary.Read(buf_item, binary.LittleEndian, f) wb.addFont(f, buf_item) case 0x41E: //FORMAT - // var bts = make([]byte, b.Size) - // binary.Read(buf, binary.LittleEndian, bts) - // buf_item := bytes.NewReader(bts) - f := new(FormatB) - binary.Read(buf_item, binary.LittleEndian, f) - wb.addFormat(f, buf_item) - // case 0x5c: - // var bts = make([]byte, b.Size) - // binary.Read(buf_item, binary.LittleEndian, bts) - // if wb.Is5ver { - // wb.Author = wb.get_string_from_bytes(bts[1:], uint16(bts[1])) - // } else { - // size := binary.LittleEndian.Uint16(bts) - // wb.Author = wb.get_string_from_bytes(bts[2:], size) - // } + f := new(Format) + binary.Read(buf_item, binary.LittleEndian, &f.Head) + f.str = wb.get_string(buf_item, f.Head.Size) + wb.addFormat(f) + log.Println(f.Head.Index, f.str) } return } diff --git a/worksheet.go b/worksheet.go index f140b5d..3a96421 100644 --- a/worksheet.go +++ b/worksheet.go @@ -133,21 +133,6 @@ func (w *WorkSheet) parseBof(buf io.ReadSeeker, bof *BOF, pre *BOF) *BOF { } w.addRange(&hy.CellRange, &hy) - - // - // bts := make([]byte, 20) - // binary.Read(buf, binary.LittleEndian, bts) - - // - - // if flag&0x80 != 0 { - // binary.Read(buf, binary.LittleEndian, &count) - // var bts = make([]uint16, count) - // binary.Read(buf, binary.LittleEndian, &bts) - // runes := utf16.Decode(bts) - // hy.TargetFrame = string(runes) - // } - default: fmt.Printf("Unknow %X,%d\n", bof.Id, bof.Size) buf.Seek(int64(bof.Size), 1) diff --git a/xf.go b/xf.go index d5f57d3..8f4dd1e 100644 --- a/xf.go +++ b/xf.go @@ -11,6 +11,10 @@ type Xf5 struct { Linestyle uint16 } +func (x *Xf5) formatNo() uint16 { + return x.Format +} + type Xf8 struct { Font uint16 Format uint16 @@ -23,3 +27,11 @@ type Xf8 struct { Linecolor uint32 Groundcolor uint16 } + +func (x *Xf8) formatNo() uint16 { + return x.Format +} + +type st_xf_data interface { + formatNo() uint16 +}