summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2022-05-02 12:30:18 +0800
committerGitHub <noreply@github.com>2022-05-02 12:30:18 +0800
commiteed431e0fc2f61b13e7745857a41cb47d9f7f810 (patch)
tree8244e1b4749d177313e51a3d73686d16c0176451
parent773d4afa32a55349a7b178c4c76d182f9ed0221f (diff)
This closes #1219, fixes cell value reading issue, improves performance, and 1904 date system support
- Fix incorrect cell data types casting results when number formatting - Support set cell value on 1904 date system enabled, ref #1212 - Improve performance for set sheet row and the merging cells, fix performance impact when resolving #1129
-rw-r--r--cell.go23
-rw-r--r--cell_test.go2
-rw-r--r--date.go20
-rw-r--r--date_test.go33
-rw-r--r--merge.go7
-rw-r--r--numfmt.go7
-rw-r--r--rows_test.go5
-rw-r--r--stream.go6
8 files changed, 63 insertions, 40 deletions
diff --git a/cell.go b/cell.go
index 70832ce..80c03ef 100644
--- a/cell.go
+++ b/cell.go
@@ -211,9 +211,12 @@ func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error {
ws.Lock()
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
ws.Unlock()
-
+ date1904, wb := false, f.workbookReader()
+ if wb != nil && wb.WorkbookPr != nil {
+ date1904 = wb.WorkbookPr.Date1904
+ }
var isNum bool
- cellData.T, cellData.V, isNum, err = setCellTime(value)
+ cellData.T, cellData.V, isNum, err = setCellTime(value, date1904)
if err != nil {
return err
}
@@ -225,11 +228,11 @@ func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error {
// setCellTime prepares cell type and Excel time by given Go time.Time type
// timestamp.
-func setCellTime(value time.Time) (t string, b string, isNum bool, err error) {
+func setCellTime(value time.Time, date1904 bool) (t string, b string, isNum bool, err error) {
var excelTime float64
_, offset := value.In(value.Location()).Zone()
value = value.Add(time.Duration(offset) * time.Second)
- if excelTime, err = timeToExcelTime(value); err != nil {
+ if excelTime, err = timeToExcelTime(value, date1904); err != nil {
return
}
isNum = excelTime > 0
@@ -1122,8 +1125,7 @@ func (f *File) formattedValue(s int, v string, raw bool) string {
if wb != nil && wb.WorkbookPr != nil {
date1904 = wb.WorkbookPr.Date1904
}
- ok := builtInNumFmtFunc[numFmtID]
- if ok != nil {
+ if ok := builtInNumFmtFunc[numFmtID]; ok != nil {
return ok(v, builtInNumFmt[numFmtID], date1904)
}
if styleSheet == nil || styleSheet.NumFmts == nil {
@@ -1140,15 +1142,18 @@ func (f *File) formattedValue(s int, v string, raw bool) string {
// prepareCellStyle provides a function to prepare style index of cell in
// worksheet by given column index and style index.
func (f *File) prepareCellStyle(ws *xlsxWorksheet, col, row, style int) int {
- if ws.Cols != nil && style == 0 {
+ if style != 0 {
+ return style
+ }
+ if ws.Cols != nil {
for _, c := range ws.Cols.Col {
if c.Min <= col && col <= c.Max && c.Style != 0 {
return c.Style
}
}
}
- for rowIdx := range ws.SheetData.Row {
- if styleID := ws.SheetData.Row[rowIdx].S; style == 0 && styleID != 0 {
+ if row <= len(ws.SheetData.Row) {
+ if styleID := ws.SheetData.Row[row-1].S; styleID != 0 {
return styleID
}
}
diff --git a/cell_test.go b/cell_test.go
index 77179cc..8ed8e1f 100644
--- a/cell_test.go
+++ b/cell_test.go
@@ -192,7 +192,7 @@ func TestSetCellTime(t *testing.T) {
} {
timezone, err := time.LoadLocation(location)
assert.NoError(t, err)
- _, b, isNum, err := setCellTime(date.In(timezone))
+ _, b, isNum, err := setCellTime(date.In(timezone), false)
assert.NoError(t, err)
assert.Equal(t, true, isNum)
assert.Equal(t, expected, b)
diff --git a/date.go b/date.go
index 1574af7..3e81319 100644
--- a/date.go
+++ b/date.go
@@ -32,21 +32,19 @@ var (
)
// timeToExcelTime provides a function to convert time to Excel time.
-func timeToExcelTime(t time.Time) (float64, error) {
- // TODO in future this should probably also handle date1904 and like TimeFromExcelTime
-
- if t.Before(excelMinTime1900) {
+func timeToExcelTime(t time.Time, date1904 bool) (float64, error) {
+ date := excelMinTime1900
+ if date1904 {
+ date = excel1904Epoc
+ }
+ if t.Before(date) {
return 0, nil
}
-
- tt := t
- diff := t.Sub(excelMinTime1900)
- result := float64(0)
-
+ tt, diff, result := t, t.Sub(date), 0.0
for diff >= maxDuration {
result += float64(maxDuration / dayNanoseconds)
tt = tt.Add(-maxDuration)
- diff = tt.Sub(excelMinTime1900)
+ diff = tt.Sub(date)
}
rem := diff % dayNanoseconds
@@ -57,7 +55,7 @@ func timeToExcelTime(t time.Time) (float64, error) {
// Microsoft intentionally included this bug in Excel so that it would remain compatible with the spreadsheet
// program that had the majority market share at the time; Lotus 1-2-3.
// https://www.myonlinetraininghub.com/excel-date-and-time
- if t.After(excelBuggyPeriodStart) {
+ if !date1904 && t.After(excelBuggyPeriodStart) {
result++
}
return result, nil
diff --git a/date_test.go b/date_test.go
index cc21e58..4091e37 100644
--- a/date_test.go
+++ b/date_test.go
@@ -40,7 +40,7 @@ var excelTimeInputList = []dateTest{
func TestTimeToExcelTime(t *testing.T) {
for i, test := range trueExpectedDateList {
t.Run(fmt.Sprintf("TestData%d", i+1), func(t *testing.T) {
- excelTime, err := timeToExcelTime(test.GoValue)
+ excelTime, err := timeToExcelTime(test.GoValue, false)
assert.NoError(t, err)
assert.Equalf(t, test.ExcelValue, excelTime,
"Time: %s", test.GoValue.String())
@@ -55,7 +55,7 @@ func TestTimeToExcelTime_Timezone(t *testing.T) {
}
for i, test := range trueExpectedDateList {
t.Run(fmt.Sprintf("TestData%d", i+1), func(t *testing.T) {
- _, err := timeToExcelTime(test.GoValue.In(location))
+ _, err := timeToExcelTime(test.GoValue.In(location), false)
assert.NoError(t, err)
})
}
@@ -71,21 +71,34 @@ func TestTimeFromExcelTime(t *testing.T) {
for min := 0; min < 60; min++ {
for sec := 0; sec < 60; sec++ {
date := time.Date(2021, time.December, 30, hour, min, sec, 0, time.UTC)
- excelTime, err := timeToExcelTime(date)
+ // Test use 1900 date system
+ excel1900Time, err := timeToExcelTime(date, false)
assert.NoError(t, err)
- dateOut := timeFromExcelTime(excelTime, false)
- assert.EqualValues(t, hour, dateOut.Hour())
- assert.EqualValues(t, min, dateOut.Minute())
- assert.EqualValues(t, sec, dateOut.Second())
+ date1900Out := timeFromExcelTime(excel1900Time, false)
+ assert.EqualValues(t, hour, date1900Out.Hour())
+ assert.EqualValues(t, min, date1900Out.Minute())
+ assert.EqualValues(t, sec, date1900Out.Second())
+ // Test use 1904 date system
+ excel1904Time, err := timeToExcelTime(date, true)
+ assert.NoError(t, err)
+ date1904Out := timeFromExcelTime(excel1904Time, true)
+ assert.EqualValues(t, hour, date1904Out.Hour())
+ assert.EqualValues(t, min, date1904Out.Minute())
+ assert.EqualValues(t, sec, date1904Out.Second())
}
}
}
}
func TestTimeFromExcelTime_1904(t *testing.T) {
- _, _ = shiftJulianToNoon(1, -0.6)
- timeFromExcelTime(61, true)
- timeFromExcelTime(62, true)
+ julianDays, julianFraction := shiftJulianToNoon(1, -0.6)
+ assert.Equal(t, julianDays, 0.0)
+ assert.Equal(t, julianFraction, 0.9)
+ julianDays, julianFraction = shiftJulianToNoon(1, 0.1)
+ assert.Equal(t, julianDays, 1.0)
+ assert.Equal(t, julianFraction, 0.6)
+ assert.Equal(t, timeFromExcelTime(61, true), time.Date(1904, time.March, 2, 0, 0, 0, 0, time.UTC))
+ assert.Equal(t, timeFromExcelTime(62, true), time.Date(1904, time.March, 3, 0, 0, 0, 0, time.UTC))
}
func TestExcelDateToTime(t *testing.T) {
diff --git a/merge.go b/merge.go
index 376b68b..0f57826 100644
--- a/merge.go
+++ b/merge.go
@@ -11,9 +11,7 @@
package excelize
-import (
- "strings"
-)
+import "strings"
// Rect gets merged cell rectangle coordinates sequence.
func (mc *xlsxMergeCell) Rect() ([]int, error) {
@@ -70,8 +68,7 @@ func (f *File) MergeCell(sheet, hCell, vCell string) error {
ws.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref, rect: rect}}}
}
ws.MergeCells.Count = len(ws.MergeCells.Cells)
- styleID, _ := f.GetCellStyle(sheet, hCell)
- return f.SetCellStyle(sheet, hCell, vCell, styleID)
+ return err
}
// UnmergeCell provides a function to unmerge a given coordinate area.
diff --git a/numfmt.go b/numfmt.go
index 5503027..2052fd9 100644
--- a/numfmt.go
+++ b/numfmt.go
@@ -939,10 +939,11 @@ func (nf *numberFormat) textHandler() (result string) {
// getValueSectionType returns its applicable number format expression section
// based on the given value.
func (nf *numberFormat) getValueSectionType(value string) (float64, string) {
- number, err := strconv.ParseFloat(value, 64)
- if err != nil {
- return number, nfp.TokenSectionText
+ isNum, _ := isNumeric(value)
+ if !isNum {
+ return 0, nfp.TokenSectionText
}
+ number, _ := strconv.ParseFloat(value, 64)
if number > 0 {
return number, nfp.TokenSectionPositive
}
diff --git a/rows_test.go b/rows_test.go
index ae30838..014b2d8 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -928,6 +928,11 @@ func TestSetRowStyle(t *testing.T) {
cellStyleID, err := f.GetCellStyle("Sheet1", "B2")
assert.NoError(t, err)
assert.Equal(t, style2, cellStyleID)
+ // Test cell inheritance rows style
+ assert.NoError(t, f.SetCellValue("Sheet1", "C1", nil))
+ cellStyleID, err = f.GetCellStyle("Sheet1", "C1")
+ assert.NoError(t, err)
+ assert.Equal(t, style2, cellStyleID)
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetRowStyle.xlsx")))
}
diff --git a/stream.go b/stream.go
index c2eda68..e1a12be 100644
--- a/stream.go
+++ b/stream.go
@@ -440,7 +440,11 @@ func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) (err error) {
c.T, c.V = setCellDuration(val)
case time.Time:
var isNum bool
- c.T, c.V, isNum, err = setCellTime(val)
+ date1904, wb := false, sw.File.workbookReader()
+ if wb != nil && wb.WorkbookPr != nil {
+ date1904 = wb.WorkbookPr.Date1904
+ }
+ c.T, c.V, isNum, err = setCellTime(val, date1904)
if isNum && c.S == 0 {
style, _ := sw.File.NewStyle(&Style{NumFmt: 22})
c.S = style