package xlsx import ( "math" "testing" "time" . "gopkg.in/check.v1" ) type CellSuite struct{} var _ = Suite(&CellSuite{}) // Test that we can set and get a Value from a Cell func (s *CellSuite) TestValueSet(c *C) { // Note, this test is fairly pointless, it serves mostly to // reinforce that this functionality is important, and should // the mechanics of this all change at some point, to remind // us not to lose this. cell := Cell{} cell.Value = "A string" c.Assert(cell.Value, Equals, "A string") } // Test that GetStyle correctly converts the xlsxStyle.Fonts. func (s *CellSuite) TestGetStyleWithFonts(c *C) { font := NewFont(10, "Calibra") style := NewStyle() style.Font = *font cell := &Cell{Value: "123", style: style} style = cell.GetStyle() c.Assert(style, NotNil) c.Assert(style.Font.Size, Equals, 10) c.Assert(style.Font.Name, Equals, "Calibra") } // Test that SetStyle correctly translates into a xlsxFont element func (s *CellSuite) TestSetStyleWithFonts(c *C) { file := NewFile() sheet, _ := file.AddSheet("Test") row := sheet.AddRow() cell := row.AddCell() font := NewFont(12, "Calibra") style := NewStyle() style.Font = *font cell.SetStyle(style) style = cell.GetStyle() xFont, _, _, _ := style.makeXLSXStyleElements() c.Assert(xFont.Sz.Val, Equals, "12") c.Assert(xFont.Name.Val, Equals, "Calibra") } // Test that GetStyle correctly converts the xlsxStyle.Fills. func (s *CellSuite) TestGetStyleWithFills(c *C) { fill := *NewFill("solid", "FF000000", "00FF0000") style := NewStyle() style.Fill = fill cell := &Cell{Value: "123", style: style} style = cell.GetStyle() _, xFill, _, _ := style.makeXLSXStyleElements() c.Assert(xFill.PatternFill.PatternType, Equals, "solid") c.Assert(xFill.PatternFill.BgColor.RGB, Equals, "00FF0000") c.Assert(xFill.PatternFill.FgColor.RGB, Equals, "FF000000") } // Test that SetStyle correctly updates xlsxStyle.Fills. func (s *CellSuite) TestSetStyleWithFills(c *C) { file := NewFile() sheet, _ := file.AddSheet("Test") row := sheet.AddRow() cell := row.AddCell() fill := NewFill("solid", "00FF0000", "FF000000") style := NewStyle() style.Fill = *fill cell.SetStyle(style) style = cell.GetStyle() _, xFill, _, _ := style.makeXLSXStyleElements() xPatternFill := xFill.PatternFill c.Assert(xPatternFill.PatternType, Equals, "solid") c.Assert(xPatternFill.FgColor.RGB, Equals, "00FF0000") c.Assert(xPatternFill.BgColor.RGB, Equals, "FF000000") } // Test that GetStyle correctly converts the xlsxStyle.Borders. func (s *CellSuite) TestGetStyleWithBorders(c *C) { border := *NewBorder("thin", "thin", "thin", "thin") style := NewStyle() style.Border = border cell := Cell{Value: "123", style: style} style = cell.GetStyle() _, _, xBorder, _ := style.makeXLSXStyleElements() c.Assert(xBorder.Left.Style, Equals, "thin") c.Assert(xBorder.Right.Style, Equals, "thin") c.Assert(xBorder.Top.Style, Equals, "thin") c.Assert(xBorder.Bottom.Style, Equals, "thin") } // We can return a string representation of the formatted data func (l *CellSuite) TestSetFloatWithFormat(c *C) { cell := Cell{} cell.SetFloatWithFormat(37947.75334343, "yyyy/mm/dd") c.Assert(cell.Value, Equals, "37947.75334343") c.Assert(cell.NumFmt, Equals, "yyyy/mm/dd") c.Assert(cell.Type(), Equals, CellTypeNumeric) } func (l *CellSuite) TestSetFloat(c *C) { cell := Cell{} cell.SetFloat(0) c.Assert(cell.Value, Equals, "0") cell.SetFloat(0.000005) c.Assert(cell.Value, Equals, "0.000005") cell.SetFloat(100.0) c.Assert(cell.Value, Equals, "100") cell.SetFloat(37947.75334343) c.Assert(cell.Value, Equals, "37947.75334343") } func (l *CellSuite) TestGeneralNumberHandling(c *C) { // If you go to Excel, make a new file, type 18.99 in a cell, and save, what you will get is a // cell where the format is General and the storage type is Number, that contains the value 18.989999999999998. // The correct way to format this should be 18.99. // 1.1 will get you the same, with a stored value of 1.1000000000000001. // Also, numbers greater than 1e11 and less than 1e-9 wil be shown as scientific notation. testCases := []struct { value string formattedValueOutput string noScientificValueOutput string }{ { value: "18.989999999999998", formattedValueOutput: "18.99", noScientificValueOutput: "18.99", }, { value: "1.1000000000000001", formattedValueOutput: "1.1", noScientificValueOutput: "1.1", }, { value: "0.0000000000000001", formattedValueOutput: "1E-16", noScientificValueOutput: "0.0000000000000001", }, { value: "0.000000000000008", formattedValueOutput: "8E-15", noScientificValueOutput: "0.000000000000008", }, { value: "1000000000000000000", formattedValueOutput: "1E+18", noScientificValueOutput: "1000000000000000000", }, { value: "1230000000000000000", formattedValueOutput: "1.23E+18", noScientificValueOutput: "1230000000000000000", }, { value: "12345678", formattedValueOutput: "12345678", noScientificValueOutput: "12345678", }, { value: "0", formattedValueOutput: "0", noScientificValueOutput: "0", }, { value: "-18.989999999999998", formattedValueOutput: "-18.99", noScientificValueOutput: "-18.99", }, { value: "-1.1000000000000001", formattedValueOutput: "-1.1", noScientificValueOutput: "-1.1", }, { value: "-0.0000000000000001", formattedValueOutput: "-1E-16", noScientificValueOutput: "-0.0000000000000001", }, { value: "-0.000000000000008", formattedValueOutput: "-8E-15", noScientificValueOutput: "-0.000000000000008", }, { value: "-1000000000000000000", formattedValueOutput: "-1E+18", noScientificValueOutput: "-1000000000000000000", }, { value: "-1230000000000000000", formattedValueOutput: "-1.23E+18", noScientificValueOutput: "-1230000000000000000", }, { value: "-12345678", formattedValueOutput: "-12345678", noScientificValueOutput: "-12345678", }, } for _, testCase := range testCases { cell := Cell{ cellType: CellTypeNumeric, NumFmt: builtInNumFmt[builtInNumFmtIndex_GENERAL], Value: testCase.value, } val, err := cell.FormattedValue() if err != nil { c.Fatal(err) } c.Assert(val, Equals, testCase.formattedValueOutput) val, err = cell.GeneralNumericWithoutScientific() if err != nil { c.Fatal(err) } c.Assert(val, Equals, testCase.noScientificValueOutput) } } // TestCellTypeFormatHandling tests all cell types other than numeric. Numeric cells are tested above since those // cells have so many edge cases. func (l *CellSuite) TestCellTypeFormatHandling(c *C) { testCases := []struct { cellType CellType numFmt string value string formattedValueOutput string expectError bool }{ // All of the string cell types, will return only the string format if there is no @ symbol in the format. { cellType: CellTypeInline, numFmt: `0;0;0;"Error"`, value: "asdf", formattedValueOutput: "Error", }, { cellType: CellTypeString, numFmt: `0;0;0;"Error"`, value: "asdf", formattedValueOutput: "Error", }, { cellType: CellTypeStringFormula, numFmt: `0;0;0;"Error"`, value: "asdf", formattedValueOutput: "Error", }, // Errors are returned as is regardless of what the format shows { cellType: CellTypeError, numFmt: `0;0;0;"Error"`, value: "#NAME?", formattedValueOutput: "#NAME?", }, { cellType: CellTypeError, numFmt: `"$"@`, value: "#######", formattedValueOutput: "#######", }, // Dates are returned as is regardless of what the format shows { cellType: CellTypeDate, numFmt: `"$"@`, value: "2017-10-24T15:29:30+00:00", formattedValueOutput: "2017-10-24T15:29:30+00:00", }, // Make sure the format used above would have done something for a string { cellType: CellTypeString, numFmt: `"$"@`, value: "#######", formattedValueOutput: "$#######", }, // For bool cells, 0 is false, 1 is true, anything else will error { cellType: CellTypeBool, numFmt: `"$"@`, value: "1", formattedValueOutput: "TRUE", }, { cellType: CellTypeBool, numFmt: `"$"@`, value: "0", formattedValueOutput: "FALSE", }, { cellType: CellTypeBool, numFmt: `"$"@`, value: "2", expectError: true, formattedValueOutput: "2", }, { cellType: CellTypeBool, numFmt: `"$"@`, value: "2", expectError: true, formattedValueOutput: "2", }, // Invalid cell type should cause an error { cellType: CellType(7), numFmt: `0`, value: "1.0", expectError: true, formattedValueOutput: "1.0", }, } for _, testCase := range testCases { cell := Cell{ cellType: testCase.cellType, NumFmt: testCase.numFmt, Value: testCase.value, } val, err := cell.FormattedValue() if err != nil != testCase.expectError { c.Fatal(err) } c.Assert(val, Equals, testCase.formattedValueOutput) } } func (s *CellSuite) TestIsTime(c *C) { cell := Cell{} isTime := cell.IsTime() c.Assert(isTime, Equals, false) cell.Value = "43221" c.Assert(isTime, Equals, false) cell.NumFmt = "d-mmm-yy" cell.Value = "43221" isTime = cell.IsTime() c.Assert(isTime, Equals, true) } func (s *CellSuite) TestGetTime(c *C) { cell := Cell{} cell.SetFloat(0) date, err := cell.GetTime(false) c.Assert(err, Equals, nil) c.Assert(date, Equals, time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)) cell.SetFloat(39813.0) date, err = cell.GetTime(true) c.Assert(err, Equals, nil) c.Assert(date, Equals, time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC)) cell.Value = "d" _, err = cell.GetTime(false) c.Assert(err, NotNil) } // FormattedValue returns an error for formatting errors func (l *CellSuite) TestFormattedValueErrorsOnBadFormat(c *C) { cell := Cell{Value: "Fudge Cake", cellType: CellTypeNumeric} cell.NumFmt = "#,##0 ;(#,##0)" value, err := cell.FormattedValue() c.Assert(value, Equals, "Fudge Cake") c.Assert(err, NotNil) c.Assert(err.Error(), Equals, "strconv.ParseFloat: parsing \"Fudge Cake\": invalid syntax") } // formattedValueChecker removes all the boilerplate for testing Cell.FormattedValue // after its change from returning one value (a string) to two values (string, error) // This allows all the old one-line asserts in the test to continue to be one // line, instead of multi-line with error checking. type formattedValueChecker struct { c *C } func (fvc *formattedValueChecker) Equals(cell Cell, expected string) { val, err := cell.FormattedValue() if err != nil { fvc.c.Error(err) } fvc.c.Assert(val, Equals, expected) } func cellsFormattedValueEquals(t *testing.T, cell *Cell, expected string) { val, err := cell.FormattedValue() if err != nil { t.Error(err) } if val != expected { t.Errorf("Expected cell.FormattedValue() to be %v, got %v", expected, val) } } // We can return a string representation of the formatted data func (l *CellSuite) TestFormattedValue(c *C) { cell := Cell{Value: "37947.7500001", cellType: CellTypeNumeric} negativeCell := Cell{Value: "-37947.7500001", cellType: CellTypeNumeric} smallCell := Cell{Value: "0.007", cellType: CellTypeNumeric} earlyCell := Cell{Value: "2.1", cellType: CellTypeNumeric} fvc := formattedValueChecker{c: c} cell.NumFmt = "general" fvc.Equals(cell, "37947.7500001") negativeCell.NumFmt = "general" fvc.Equals(negativeCell, "-37947.7500001") // TODO: This test is currently broken. For a string type cell, I // don't think FormattedValue() should be doing a numeric conversion on the value // before returning the string. cell.NumFmt = "0" fvc.Equals(cell, "37948") cell.NumFmt = "#,##0" // For the time being we're not doing // this comma formatting, so it'll fall back to the related // non-comma form. fvc.Equals(cell, "37948") cell.NumFmt = "#,##0.00;(#,##0.00)" fvc.Equals(cell, "37947.75") cell.NumFmt = "0.00" fvc.Equals(cell, "37947.75") cell.NumFmt = "#,##0.00" // For the time being we're not doing // this comma formatting, so it'll fall back to the related // non-comma form. fvc.Equals(cell, "37947.75") cell.NumFmt = "#,##0 ;(#,##0)" fvc.Equals(cell, "37948") negativeCell.NumFmt = "#,##0 ;(#,##0)" fvc.Equals(negativeCell, "(37948)") cell.NumFmt = "#,##0 ;[red](#,##0)" fvc.Equals(cell, "37948") negativeCell.NumFmt = "#,##0 ;[red](#,##0)" fvc.Equals(negativeCell, "(37948)") negativeCell.NumFmt = "#,##0.00;(#,##0.00)" fvc.Equals(negativeCell, "(37947.75)") cell.NumFmt = "0%" fvc.Equals(cell, "3794775%") cell.NumFmt = "0.00%" fvc.Equals(cell, "3794775.00%") cell.NumFmt = "0.00e+00" fvc.Equals(cell, "3.794775e+04") cell.NumFmt = "##0.0e+0" // This is wrong, but we'll use it for now. fvc.Equals(cell, "3.794775e+04") cell.NumFmt = "mm-dd-yy" fvc.Equals(cell, "11-22-03") cell.NumFmt = "d-mmm-yy" fvc.Equals(cell, "22-Nov-03") earlyCell.NumFmt = "d-mmm-yy" fvc.Equals(earlyCell, "1-Jan-00") cell.NumFmt = "d-mmm" fvc.Equals(cell, "22-Nov") earlyCell.NumFmt = "d-mmm" fvc.Equals(earlyCell, "1-Jan") cell.NumFmt = "mmm-yy" fvc.Equals(cell, "Nov-03") cell.NumFmt = "h:mm am/pm" fvc.Equals(cell, "6:00 pm") smallCell.NumFmt = "h:mm am/pm" fvc.Equals(smallCell, "12:10 am") cell.NumFmt = "h:mm:ss am/pm" fvc.Equals(cell, "6:00:00 pm") cell.NumFmt = "hh:mm:ss" fvc.Equals(cell, "18:00:00") smallCell.NumFmt = "h:mm:ss am/pm" fvc.Equals(smallCell, "12:10:04 am") cell.NumFmt = "h:mm" fvc.Equals(cell, "18:00") smallCell.NumFmt = "h:mm" fvc.Equals(smallCell, "00:10") smallCell.NumFmt = "hh:mm" fvc.Equals(smallCell, "00:10") cell.NumFmt = "h:mm:ss" fvc.Equals(cell, "18:00:00") cell.NumFmt = "hh:mm:ss" fvc.Equals(cell, "18:00:00") smallCell.NumFmt = "hh:mm:ss" fvc.Equals(smallCell, "00:10:04") smallCell.NumFmt = "h:mm:ss" fvc.Equals(smallCell, "00:10:04") cell.NumFmt = "m/d/yy h:mm" fvc.Equals(cell, "11/22/03 18:00") cell.NumFmt = "m/d/yy hh:mm" fvc.Equals(cell, "11/22/03 18:00") smallCell.NumFmt = "m/d/yy h:mm" fvc.Equals(smallCell, "12/30/99 00:10") smallCell.NumFmt = "m/d/yy hh:mm" fvc.Equals(smallCell, "12/30/99 00:10") earlyCell.NumFmt = "m/d/yy hh:mm" fvc.Equals(earlyCell, "1/1/00 02:24") earlyCell.NumFmt = "m/d/yy h:mm" fvc.Equals(earlyCell, "1/1/00 02:24") cell.NumFmt = "mm:ss" fvc.Equals(cell, "00:00") smallCell.NumFmt = "mm:ss" fvc.Equals(smallCell, "10:04") cell.NumFmt = "[hh]:mm:ss" fvc.Equals(cell, "18:00:00") cell.NumFmt = "[h]:mm:ss" fvc.Equals(cell, "18:00:00") smallCell.NumFmt = "[h]:mm:ss" fvc.Equals(smallCell, "10:04") const ( expect1 = "0000.0086" expect2 = "1004.8000" format = "mmss.0000" tlen = len(format) ) for i := 0; i < 3; i++ { tfmt := format[0 : tlen-i] cell.NumFmt = tfmt fvc.Equals(cell, expect1[0:tlen-i]) smallCell.NumFmt = tfmt fvc.Equals(smallCell, expect2[0:tlen-i]) } cell.NumFmt = "yyyy\\-mm\\-dd" fvc.Equals(cell, "2003\\-11\\-22") cell.NumFmt = "dd/mm/yyyy hh:mm:ss" fvc.Equals(cell, "22/11/2003 18:00:00") cell.NumFmt = "dd/mm/yy" fvc.Equals(cell, "22/11/03") earlyCell.NumFmt = "dd/mm/yy" fvc.Equals(earlyCell, "01/01/00") cell.NumFmt = "hh:mm:ss" fvc.Equals(cell, "18:00:00") smallCell.NumFmt = "hh:mm:ss" fvc.Equals(smallCell, "00:10:04") cell.NumFmt = "dd/mm/yy\\ hh:mm" fvc.Equals(cell, "22/11/03\\ 18:00") cell.NumFmt = "yyyy/mm/dd" fvc.Equals(cell, "2003/11/22") cell.NumFmt = "yy-mm-dd" fvc.Equals(cell, "03-11-22") cell.NumFmt = "d-mmm-yyyy" fvc.Equals(cell, "22-Nov-2003") earlyCell.NumFmt = "d-mmm-yyyy" fvc.Equals(earlyCell, "1-Jan-1900") cell.NumFmt = "m/d/yy" fvc.Equals(cell, "11/22/03") earlyCell.NumFmt = "m/d/yy" fvc.Equals(earlyCell, "1/1/00") cell.NumFmt = "m/d/yyyy" fvc.Equals(cell, "11/22/2003") earlyCell.NumFmt = "m/d/yyyy" fvc.Equals(earlyCell, "1/1/1900") cell.NumFmt = "dd-mmm-yyyy" fvc.Equals(cell, "22-Nov-2003") cell.NumFmt = "dd/mm/yyyy" fvc.Equals(cell, "22/11/2003") cell.NumFmt = "mm/dd/yy hh:mm am/pm" fvc.Equals(cell, "11/22/03 06:00 pm") cell.NumFmt = "mm/dd/yy h:mm am/pm" fvc.Equals(cell, "11/22/03 6:00 pm") cell.NumFmt = "mm/dd/yyyy hh:mm:ss" fvc.Equals(cell, "11/22/2003 18:00:00") smallCell.NumFmt = "mm/dd/yyyy hh:mm:ss" fvc.Equals(smallCell, "12/30/1899 00:10:04") cell.NumFmt = "yyyy-mm-dd hh:mm:ss" fvc.Equals(cell, "2003-11-22 18:00:00") smallCell.NumFmt = "yyyy-mm-dd hh:mm:ss" fvc.Equals(smallCell, "1899-12-30 00:10:04") cell.NumFmt = "mmmm d, yyyy" fvc.Equals(cell, "November 22, 2003") smallCell.NumFmt = "mmmm d, yyyy" fvc.Equals(smallCell, "December 30, 1899") cell.NumFmt = "dddd, mmmm dd, yyyy" fvc.Equals(cell, "Saturday, November 22, 2003") smallCell.NumFmt = "dddd, mmmm dd, yyyy" fvc.Equals(smallCell, "Saturday, December 30, 1899") } func (s *CellSuite) TestTimeToExcelTime(c *C) { c.Assert(0.0, Equals, TimeToExcelTime(time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC), false)) c.Assert(-1462.0, Equals, TimeToExcelTime(time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC), true)) c.Assert(25569.0, Equals, TimeToExcelTime(time.Unix(0, 0), false)) c.Assert(43269.0, Equals, TimeToExcelTime(time.Date(2018, 6, 18, 0, 0, 0, 0, time.UTC), false)) c.Assert(401769.0, Equals, TimeToExcelTime(time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC), false)) smallDate := time.Date(1899, 12, 30, 0, 0, 0, 1000, time.UTC) smallExcelTime := TimeToExcelTime(smallDate, false) c.Assert(true, Equals, 0.0 != smallExcelTime) roundTrippedDate := TimeFromExcelTime(smallExcelTime, false) c.Assert(roundTrippedDate, Equals, smallDate) } // test setters and getters func (s *CellSuite) TestSetterGetters(c *C) { cell := Cell{} cell.SetString("hello world") if val, err := cell.FormattedValue(); err != nil { c.Error(err) } else { c.Assert(val, Equals, "hello world") } c.Assert(cell.Type(), Equals, CellTypeString) cell.SetInt(1024) intValue, _ := cell.Int() c.Assert(intValue, Equals, 1024) c.Assert(cell.NumFmt, Equals, builtInNumFmt[builtInNumFmtIndex_GENERAL]) c.Assert(cell.Type(), Equals, CellTypeNumeric) cell.SetInt64(1024) int64Value, _ := cell.Int64() c.Assert(int64Value, Equals, int64(1024)) c.Assert(cell.NumFmt, Equals, builtInNumFmt[builtInNumFmtIndex_GENERAL]) c.Assert(cell.Type(), Equals, CellTypeNumeric) cell.SetFloat(1.024) float, _ := cell.Float() intValue, _ = cell.Int() // convert c.Assert(float, Equals, 1.024) c.Assert(intValue, Equals, 1) c.Assert(cell.NumFmt, Equals, builtInNumFmt[builtInNumFmtIndex_GENERAL]) c.Assert(cell.Type(), Equals, CellTypeNumeric) cell.SetFormula("10+20") c.Assert(cell.Formula(), Equals, "10+20") c.Assert(cell.Type(), Equals, CellTypeNumeric) cell.SetStringFormula("A1") c.Assert(cell.Formula(), Equals, "A1") c.Assert(cell.Type(), Equals, CellTypeStringFormula) } // TestOddInput is a regression test for #101. When the number format // was "@" (string), the input below caused a crash in strconv.ParseFloat. // The solution was to check if cell.Value was both a CellTypeString and // had a NumFmt of "general" or "@" and short-circuit FormattedValue() if so. func (s *CellSuite) TestOddInput(c *C) { cell := Cell{} odd := `[1],[12,"DATE NOT NULL DEFAULT '0000-00-00'"]` cell.Value = odd cell.NumFmt = "@" if val, err := cell.FormattedValue(); err != nil { c.Error(err) } else { c.Assert(val, Equals, odd) } } // TestBool tests basic Bool getting and setting booleans. func (s *CellSuite) TestBool(c *C) { cell := Cell{} cell.SetBool(true) c.Assert(cell.Value, Equals, "1") c.Assert(cell.Bool(), Equals, true) cell.SetBool(false) c.Assert(cell.Value, Equals, "0") c.Assert(cell.Bool(), Equals, false) } // TestStringBool tests calling Bool on a non CellTypeBool value. func (s *CellSuite) TestStringBool(c *C) { cell := Cell{} cell.SetInt(0) c.Assert(cell.Bool(), Equals, false) cell.SetInt(1) c.Assert(cell.Bool(), Equals, true) cell.SetString("") c.Assert(cell.Bool(), Equals, false) cell.SetString("0") c.Assert(cell.Bool(), Equals, true) } // TestSetValue tests whether SetValue handle properly for different type values. func (s *CellSuite) TestSetValue(c *C) { cell := Cell{} // int for _, i := range []interface{}{1, int8(1), int16(1), int32(1), int64(1)} { cell.SetValue(i) val, err := cell.Int64() c.Assert(err, IsNil) c.Assert(val, Equals, int64(1)) } // float for _, i := range []interface{}{1.11, float32(1.11), float64(1.11)} { cell.SetValue(i) val, err := cell.Float() c.Assert(err, IsNil) c.Assert(val, Equals, 1.11) } // In the naive implementation using go fmt "%v", this test would fail and the cell.Value would be "1e-06" for _, i := range []interface{}{0.000001, float32(0.000001), float64(0.000001)} { cell.SetValue(i) c.Assert(cell.Value, Equals, "0.000001") val, err := cell.Float() c.Assert(err, IsNil) c.Assert(val, Equals, 0.000001) } // time cell.SetValue(time.Unix(0, 0)) val, err := cell.Float() c.Assert(err, IsNil) c.Assert(math.Floor(val), Equals, 25569.0) // string and nil for _, i := range []interface{}{nil, "", []byte("")} { cell.SetValue(i) c.Assert(cell.Value, Equals, "") } // others cell.SetValue([]string{"test"}) c.Assert(cell.Value, Equals, "[test]") } func (s *CellSuite) TestSetDateWithOptions(c *C) { cell := Cell{} // time cell.SetDate(time.Unix(0, 0)) val, err := cell.Float() c.Assert(err, IsNil) c.Assert(math.Floor(val), Equals, 25569.0) // our test subject date2016UTC := time.Date(2016, 1, 1, 12, 0, 0, 0, time.UTC) // test ny timezone nyTZ, err := time.LoadLocation("America/New_York") c.Assert(err, IsNil) cell.SetDateWithOptions(date2016UTC, DateTimeOptions{ ExcelTimeFormat: "test_format1", Location: nyTZ, }) val, err = cell.Float() c.Assert(err, IsNil) c.Assert(val, Equals, TimeToExcelTime(time.Date(2016, 1, 1, 7, 0, 0, 0, time.UTC), false)) // test jp timezone jpTZ, err := time.LoadLocation("Asia/Tokyo") c.Assert(err, IsNil) cell.SetDateWithOptions(date2016UTC, DateTimeOptions{ ExcelTimeFormat: "test_format2", Location: jpTZ, }) val, err = cell.Float() c.Assert(err, IsNil) c.Assert(val, Equals, TimeToExcelTime(time.Date(2016, 1, 1, 21, 0, 0, 0, time.UTC), false)) } func (s *CellSuite) TestIsTimeFormat(c *C) { c.Assert(isTimeFormat("yy"), Equals, true) c.Assert(isTimeFormat("hh"), Equals, true) c.Assert(isTimeFormat("h"), Equals, true) c.Assert(isTimeFormat("am/pm"), Equals, true) c.Assert(isTimeFormat("AM/PM"), Equals, true) c.Assert(isTimeFormat("A/P"), Equals, true) c.Assert(isTimeFormat("a/p"), Equals, true) c.Assert(isTimeFormat("ss"), Equals, true) c.Assert(isTimeFormat("mm"), Equals, true) c.Assert(isTimeFormat(":"), Equals, false) c.Assert(isTimeFormat("z"), Equals, false) } func (s *CellSuite) TestIs12HourtTime(c *C) { c.Assert(is12HourTime("am/pm"), Equals, true) c.Assert(is12HourTime("AM/PM"), Equals, true) c.Assert(is12HourTime("a/p"), Equals, true) c.Assert(is12HourTime("A/P"), Equals, true) c.Assert(is12HourTime("x"), Equals, false) } func (s *CellSuite) TestFallbackTo(c *C) { testCases := []struct { cellType *CellType cellData string fallback CellType expectedReturn CellType }{ { cellType: CellTypeNumeric.Ptr(), cellData: `string`, fallback: CellTypeString, expectedReturn: CellTypeString, }, { cellType: nil, cellData: `string`, fallback: CellTypeNumeric, expectedReturn: CellTypeNumeric, }, { cellType: CellTypeNumeric.Ptr(), cellData: `300.24`, fallback: CellTypeString, expectedReturn: CellTypeNumeric, }, { cellType: CellTypeNumeric.Ptr(), cellData: `300`, fallback: CellTypeString, expectedReturn: CellTypeNumeric, }, } for _, testCase := range testCases { c.Assert(testCase.cellType.fallbackTo(testCase.cellData, testCase.fallback), Equals, testCase.expectedReturn) } }