date.go 3.4 KB

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