date.go 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. package xlsx
  2. import (
  3. "math"
  4. "time"
  5. )
  6. const MJD_0 float64 = 2400000.5
  7. const MJD_JD2000 float64 = 51544.5
  8. func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
  9. switch {
  10. case -0.5 < julianFraction && julianFraction < 0.5:
  11. julianFraction += 0.5
  12. case julianFraction >= 0.5:
  13. julianDays += 1
  14. julianFraction -= 0.5
  15. case julianFraction <= -0.5:
  16. julianDays -= 1
  17. julianFraction += 1.5
  18. }
  19. return julianDays, julianFraction
  20. }
  21. // Return the integer values for hour, minutes, seconds and
  22. // nanoseconds that comprised a given fraction of a day.
  23. func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
  24. f := 5184000000000000 * fraction
  25. nanoseconds = int(math.Mod(f, 1000000000))
  26. f = f / 1000000000
  27. seconds = int(math.Mod(f, 3600))
  28. f = f / 3600
  29. minutes = int(math.Mod(f, 60))
  30. f = f / 60
  31. hours = int(f)
  32. return hours, minutes, seconds, nanoseconds
  33. }
  34. func julianDateToGregorianTime(part1, part2 float64) time.Time {
  35. part1I, part1F := math.Modf(part1)
  36. part2I, part2F := math.Modf(part2)
  37. julianDays := part1I + part2I
  38. julianFraction := part1F + part2F
  39. julianDays, julianFraction = shiftJulianToNoon(julianDays, julianFraction)
  40. day, month, year := doTheFliegelAndVanFlandernAlgorithm(int(julianDays))
  41. hours, minutes, seconds, nanoseconds := fractionOfADay(julianFraction)
  42. return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
  43. }
  44. // By this point generations of programmers have repeated the
  45. // algorithm sent to the editor of "Communications of the ACM" in 1968
  46. // (published in CACM, volume 11, number 10, October 1968, p.657).
  47. // None of those programmers seems to have found it necessary to
  48. // explain the constants or variable names set out by Henry F. Fliegel
  49. // and Thomas C. Van Flandern. Maybe one day I'll buy that jounal and
  50. // expand an explanation here - that day is not today.
  51. func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
  52. l := jd + 68569
  53. n := (4 * l) / 146097
  54. l = l - (146097*n+3)/4
  55. i := (4000 * (l + 1)) / 1461001
  56. l = l - (1461*i)/4 + 31
  57. j := (80 * l) / 2447
  58. d := l - (2447*j)/80
  59. l = j / 11
  60. m := j + 2 - (12 * l)
  61. y := 100*(n-49) + i + l
  62. return d, m, y
  63. }
  64. // Convert an excelTime representation (stored as a floating point number) to a time.Time.
  65. func TimeFromExcelTime(excelTime float64, date1904 bool) time.Time {
  66. var date time.Time
  67. var intPart int64 = int64(excelTime)
  68. // Excel uses Julian dates prior to March 1st 1900, and
  69. // Gregorian thereafter.
  70. if intPart <= 61 {
  71. const OFFSET1900 = 15018.0
  72. const OFFSET1904 = 16480.0
  73. var date time.Time
  74. if date1904 {
  75. date = julianDateToGregorianTime(MJD_0, excelTime+OFFSET1904)
  76. } else {
  77. date = julianDateToGregorianTime(MJD_0, excelTime+OFFSET1900)
  78. }
  79. return date
  80. }
  81. var floatPart float64 = excelTime - float64(intPart)
  82. var dayNanoSeconds float64 = 24 * 60 * 60 * 1000 * 1000 * 1000
  83. if date1904 {
  84. date = time.Date(1904, 1, 1, 0, 0, 0, 0, time.UTC)
  85. } else {
  86. date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
  87. }
  88. durationDays := time.Duration(intPart) * time.Hour * 24
  89. durationPart := time.Duration(dayNanoSeconds * floatPart)
  90. return date.Add(durationDays).Add(durationPart)
  91. }