summaryrefslogtreecommitdiff
path: root/date.go
diff options
context:
space:
mode:
Diffstat (limited to 'date.go')
-rw-r--r--date.go119
1 files changed, 119 insertions, 0 deletions
diff --git a/date.go b/date.go
new file mode 100644
index 0000000..a493866
--- /dev/null
+++ b/date.go
@@ -0,0 +1,119 @@
+package excelize
+
+import (
+ "math"
+ "time"
+)
+
+// timeLocationUTC defined the UTC time location.
+var timeLocationUTC, _ = time.LoadLocation("UTC")
+
+// timeToUTCTime provides function to convert time to UTC time.
+func timeToUTCTime(t time.Time) time.Time {
+ return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
+}
+
+// timeToExcelTime provides function to convert time to Excel time.
+func timeToExcelTime(t time.Time) float64 {
+ return float64(t.UnixNano())/8.64e13 + 25569.0
+}
+
+// shiftJulianToNoon provides function to process julian date to noon.
+func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
+ switch {
+ case -0.5 < julianFraction && julianFraction < 0.5:
+ julianFraction += 0.5
+ case julianFraction >= 0.5:
+ julianDays++
+ julianFraction -= 0.5
+ case julianFraction <= -0.5:
+ julianDays--
+ julianFraction += 1.5
+ }
+ return julianDays, julianFraction
+}
+
+// fractionOfADay provides function to return the integer values for hour,
+// minutes, seconds and nanoseconds that comprised a given fraction of a day.
+// values would round to 1 us.
+func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
+
+ const (
+ c1us = 1e3
+ c1s = 1e9
+ c1day = 24 * 60 * 60 * c1s
+ )
+
+ frac := int64(c1day*fraction + c1us/2)
+ nanoseconds = int((frac%c1s)/c1us) * c1us
+ frac /= c1s
+ seconds = int(frac % 60)
+ frac /= 60
+ minutes = int(frac % 60)
+ hours = int(frac / 60)
+ return
+}
+
+// julianDateToGregorianTime provides function to convert julian date to
+// gregorian time.
+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
+}
+
+// timeFromExcelTime provides function to 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(excelTime)
+ // Excel uses Julian dates prior to March 1st 1900, and Gregorian
+ // thereafter.
+ if intPart <= 61 {
+ const OFFSET1900 = 15018.0
+ const OFFSET1904 = 16480.0
+ const MJD0 float64 = 2400000.5
+ var date time.Time
+ if date1904 {
+ date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1904)
+ } else {
+ date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1900)
+ }
+ return date
+ }
+ var floatPart = 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)
+}