Browse Source

Handle dates in further than time.Duration

From time.Duration "largest representable duration to approximately 290 years."
This enables to use multiple durations and avoid numeric overflow.
Mārtiņš 7 years ago
parent
commit
611de74506
4 changed files with 35 additions and 1 deletions
  1. 15 1
      cell.go
  2. 9 0
      cell_test.go
  3. 9 0
      date.go
  4. 2 0
      date_test.go

+ 15 - 1
cell.go

@@ -136,7 +136,21 @@ func TimeToUTCTime(t time.Time) time.Time {
 }
 
 func TimeToExcelTime(t time.Time) float64 {
-	return float64(t.UnixNano())/8.64e13 + 25569.0
+	// TODO in future this should probably also handle date1904 and like TimeFromExcelTime
+	var excelTime float64
+	var deltaDays int64
+	excelTime = 0
+	deltaDays = 290 * 364
+	// check if UnixNano would be out of int64 range
+	for t.Unix() > deltaDays*24*60*60 {
+		// reduce by aprox. 290 years, which is max for int64 nanoseconds
+		delta := time.Duration(deltaDays) * 24 * time.Hour
+		excelTime = excelTime + float64(deltaDays)
+		t = t.Add(-delta)
+	}
+	// finally add remainder of UnixNano to keep nano precision
+	// and 25569 which is days between 1900 and 1970
+	return excelTime + float64(t.UnixNano())/8.64e13 + 25569.0
 }
 
 // DateTimeOptions are additional options for exporting times

+ 9 - 0
cell_test.go

@@ -779,3 +779,12 @@ func (s *CellSuite) TestIs12HourtTime(c *C) {
 	c.Assert(is12HourTime("A/P"), Equals, true)
 	c.Assert(is12HourTime("x"), Equals, false)
 }
+
+
+func (s *CellSuite) TestTimeToExcelTime(c *C) {
+	c.Assert(0.0, Equals, TimeToExcelTime(time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)))
+	c.Assert(25569.0, Equals, TimeToExcelTime(time.Unix(0, 0)))
+	c.Assert(43269.0, Equals, TimeToExcelTime(time.Date(2018, 6, 18, 0, 0, 0, 0, time.UTC)))
+	c.Assert(401769.0, Equals, TimeToExcelTime(time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC)))
+}
+

+ 9 - 0
date.go

@@ -7,6 +7,7 @@ import (
 
 const MJD_0 float64 = 2400000.5
 const MJD_JD2000 float64 = 51544.5
+const MDD int64 = 106750	// Max Duration Days
 
 func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
 	switch {
@@ -98,7 +99,15 @@ func TimeFromExcelTime(excelTime float64, date1904 bool) time.Time {
 		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)
+	}	
+
+	// Duration is limited to aprox. 290 years
+	for intPart > MDD {
+		durationDays := time.Duration(MDD) * time.Hour * 24
+		date = date.Add(durationDays)
+		intPart = intPart - MDD
 	}
+
 	durationDays := time.Duration(intPart) * time.Hour * 24
 	durationPart := time.Duration(dayNanoSeconds * floatPart)
 	return date.Add(durationDays).Add(durationPart)

+ 2 - 0
date_test.go

@@ -50,6 +50,8 @@ func (d *DateSuite) TestTimeFromExcelTime(c *C) {
 	c.Assert(date, Equals, time.Date(1900, 3, 1, 0, 0, 0, 0, time.UTC))
 	date = TimeFromExcelTime(41275.0, false)
 	c.Assert(date, Equals, time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC))
+	date = TimeFromExcelTime(401769, false)
+	c.Assert(date, Equals, time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC))
 }
 
 func (d *DateSuite) TestTimeFromExcelTimeWithFractionalPart(c *C) {