date.go 3.8 KB

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