From f1d1a5dc2b7f1e6a10a9275b2a6e392638457ee7 Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 10 Oct 2024 22:44:38 +0800 Subject: [PATCH] This closes #2004, support apply number format for time and duration cell value - Add unit tests - Update dependencies modules --- cell.go | 4 ++-- cell_test.go | 25 ++++++++++++++++++++++++- date.go | 28 ++++++++++++++++++++++++++++ excelize.go | 13 ++++++++----- go.mod | 6 +++--- go.sum | 12 ++++++------ 6 files changed, 71 insertions(+), 17 deletions(-) diff --git a/cell.go b/cell.go index 302ea91..7f601d3 100644 --- a/cell.go +++ b/cell.go @@ -144,7 +144,7 @@ func (f *File) SetCellValue(sheet, cell string, value interface{}) error { if err != nil { return err } - err = f.setDefaultTimeStyle(sheet, cell, 21) + err = f.setDefaultTimeStyle(sheet, cell, getDurationNumFmt(v)) case time.Time: err = f.setCellTimeFunc(sheet, cell, v) case bool: @@ -256,7 +256,7 @@ func (f *File) setCellTimeFunc(sheet, cell string, value time.Time) error { return err } if isNum { - _ = f.setDefaultTimeStyle(sheet, cell, 22) + _ = f.setDefaultTimeStyle(sheet, cell, getTimeNumFmt(value)) } return err } diff --git a/cell_test.go b/cell_test.go index fa67173..5590a36 100644 --- a/cell_test.go +++ b/cell_test.go @@ -305,6 +305,29 @@ func TestSetCellValue(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expected, val) } + // Test set cell value with time duration + for val, expected := range map[time.Duration]string{ + time.Hour*21 + time.Minute*51 + time.Second*44: "21:51:44", + time.Hour*21 + time.Minute*50: "21:50", + time.Hour*24 + time.Minute*51 + time.Second*44: "24:51:44", + time.Hour*24 + time.Minute*50: "24:50:00", + } { + assert.NoError(t, f.SetCellValue("Sheet1", "A1", val)) + val, err := f.GetCellValue("Sheet1", "A1") + assert.NoError(t, err) + assert.Equal(t, expected, val) + } + // Test set cell value with time + for val, expected := range map[time.Time]string{ + time.Date(2024, time.October, 1, 0, 0, 0, 0, time.UTC): "Oct-24", + time.Date(2024, time.October, 10, 0, 0, 0, 0, time.UTC): "10-10-24", + time.Date(2024, time.October, 10, 12, 0, 0, 0, time.UTC): "10/10/24 12:00", + } { + assert.NoError(t, f.SetCellValue("Sheet1", "A1", val)) + val, err := f.GetCellValue("Sheet1", "A1") + assert.NoError(t, err) + assert.Equal(t, expected, val) + } } func TestSetCellValues(t *testing.T) { @@ -314,7 +337,7 @@ func TestSetCellValues(t *testing.T) { v, err := f.GetCellValue("Sheet1", "A1") assert.NoError(t, err) - assert.Equal(t, v, "12/31/10 00:00") + assert.Equal(t, v, "12-31-10") // Test date value lower than min date supported by Excel err = f.SetCellValue("Sheet1", "A1", time.Date(1600, time.December, 31, 0, 0, 0, 0, time.UTC)) diff --git a/date.go b/date.go index de39b9c..c26dd49 100644 --- a/date.go +++ b/date.go @@ -214,3 +214,31 @@ func formatYear(y int) int { } return y } + +// getDurationNumFmt returns most simplify numbers format code for time +// duration type cell value by given worksheet name, cell reference and number. +func getDurationNumFmt(d time.Duration) int { + if d >= time.Hour*24 { + return 46 + } + // Whole minutes + if d.Minutes() == float64(int(d.Minutes())) { + return 20 + } + return 21 +} + +// getTimeNumFmt returns most simplify numbers format code for time type cell +// value by given worksheet name, cell reference and number. +func getTimeNumFmt(t time.Time) int { + nextMonth := t.AddDate(0, 1, 0) + // Whole months + if t.Day() == 1 && nextMonth.Day() == 1 { + return 17 + } + // Whole days + if t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 && t.Nanosecond() == 0 { + return 14 + } + return 22 +} diff --git a/excelize.go b/excelize.go index c46984f..b53a171 100644 --- a/excelize.go +++ b/excelize.go @@ -242,15 +242,18 @@ func (f *File) xmlNewDecoder(rdr io.Reader) (ret *xml.Decoder) { // time.Time type cell value by given worksheet name, cell reference and // number format code. func (f *File) setDefaultTimeStyle(sheet, cell string, format int) error { - s, err := f.GetCellStyle(sheet, cell) + styleIdx, err := f.GetCellStyle(sheet, cell) if err != nil { return err } - if s == 0 { - style, _ := f.NewStyle(&Style{NumFmt: format}) - err = f.SetCellStyle(sheet, cell, cell, style) + if styleIdx == 0 { + styleIdx, _ = f.NewStyle(&Style{NumFmt: format}) + } else { + style, _ := f.GetStyle(styleIdx) + style.NumFmt = format + styleIdx, _ = f.NewStyle(style) } - return err + return f.SetCellStyle(sheet, cell, cell, styleIdx) } // workSheetReader provides a function to get the pointer to the structure diff --git a/go.mod b/go.mod index 7edba1b..22ba8e1 100644 --- a/go.mod +++ b/go.mod @@ -8,10 +8,10 @@ require ( github.com/stretchr/testify v1.8.4 github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.28.0 golang.org/x/image v0.18.0 - golang.org/x/net v0.29.0 - golang.org/x/text v0.18.0 + golang.org/x/net v0.30.0 + golang.org/x/text v0.19.0 ) require ( diff --git a/go.sum b/go.sum index de22a1d..33f90a0 100644 --- a/go.sum +++ b/go.sum @@ -15,14 +15,14 @@ github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7 github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A= github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=