date.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /*
  2. Package excelize providing a set of functions that allow you to write to
  3. and read from XLSX files. Support reads and writes XLSX file generated by
  4. Microsoft Excel™ 2007 and later. Support save file without losing original
  5. charts of XLSX. This library needs Go version 1.8 or later.
  6. Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
  7. this source code is governed by a BSD-style license that can be found in
  8. the LICENSE file.
  9. */
  10. package excelize
  11. import (
  12. "math"
  13. "time"
  14. )
  15. // timeLocationUTC defined the UTC time location.
  16. var timeLocationUTC, _ = time.LoadLocation("UTC")
  17. // timeToUTCTime provides a function to convert time to UTC time.
  18. func timeToUTCTime(t time.Time) time.Time {
  19. return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
  20. }
  21. // timeToExcelTime provides a function to convert time to Excel time.
  22. func timeToExcelTime(t time.Time) float64 {
  23. // TODO in future this should probably also handle date1904 and like TimeFromExcelTime
  24. var excelTime float64
  25. var deltaDays int64
  26. excelTime = 0
  27. deltaDays = 290 * 364
  28. // check if UnixNano would be out of int64 range
  29. for t.Unix() > deltaDays*24*60*60 {
  30. // reduce by aprox. 290 years, which is max for int64 nanoseconds
  31. delta := time.Duration(deltaDays) * 24 * time.Hour
  32. excelTime = excelTime + float64(deltaDays)
  33. t = t.Add(-delta)
  34. }
  35. // finally add remainder of UnixNano to keep nano precision
  36. // and 25569 which is days between 1900 and 1970
  37. return excelTime + float64(t.UnixNano())/8.64e13 + 25569.0
  38. }
  39. // shiftJulianToNoon provides a function to process julian date to noon.
  40. func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
  41. switch {
  42. case -0.5 < julianFraction && julianFraction < 0.5:
  43. julianFraction += 0.5
  44. case julianFraction >= 0.5:
  45. julianDays++
  46. julianFraction -= 0.5
  47. case julianFraction <= -0.5:
  48. julianDays--
  49. julianFraction += 1.5
  50. }
  51. return julianDays, julianFraction
  52. }
  53. // fractionOfADay provides a function to return the integer values for hour,
  54. // minutes, seconds and nanoseconds that comprised a given fraction of a day.
  55. // values would round to 1 us.
  56. func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
  57. const (
  58. c1us = 1e3
  59. c1s = 1e9
  60. c1day = 24 * 60 * 60 * c1s
  61. )
  62. frac := int64(c1day*fraction + c1us/2)
  63. nanoseconds = int((frac%c1s)/c1us) * c1us
  64. frac /= c1s
  65. seconds = int(frac % 60)
  66. frac /= 60
  67. minutes = int(frac % 60)
  68. hours = int(frac / 60)
  69. return
  70. }
  71. // julianDateToGregorianTime provides a function to convert julian date to
  72. // gregorian time.
  73. func julianDateToGregorianTime(part1, part2 float64) time.Time {
  74. part1I, part1F := math.Modf(part1)
  75. part2I, part2F := math.Modf(part2)
  76. julianDays := part1I + part2I
  77. julianFraction := part1F + part2F
  78. julianDays, julianFraction = shiftJulianToNoon(julianDays, julianFraction)
  79. day, month, year := doTheFliegelAndVanFlandernAlgorithm(int(julianDays))
  80. hours, minutes, seconds, nanoseconds := fractionOfADay(julianFraction)
  81. return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
  82. }
  83. // By this point generations of programmers have repeated the algorithm sent
  84. // to the editor of "Communications of the ACM" in 1968 (published in CACM,
  85. // volume 11, number 10, October 1968, p.657). None of those programmers seems
  86. // to have found it necessary to explain the constants or variable names set
  87. // out by Henry F. Fliegel and Thomas C. Van Flandern. Maybe one day I'll buy
  88. // that jounal and expand an explanation here - that day is not today.
  89. func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
  90. l := jd + 68569
  91. n := (4 * l) / 146097
  92. l = l - (146097*n+3)/4
  93. i := (4000 * (l + 1)) / 1461001
  94. l = l - (1461*i)/4 + 31
  95. j := (80 * l) / 2447
  96. d := l - (2447*j)/80
  97. l = j / 11
  98. m := j + 2 - (12 * l)
  99. y := 100*(n-49) + i + l
  100. return d, m, y
  101. }
  102. // timeFromExcelTime provides a function to convert an excelTime
  103. // representation (stored as a floating point number) to a time.Time.
  104. func timeFromExcelTime(excelTime float64, date1904 bool) time.Time {
  105. const MDD int64 = 106750 // Max time.Duration Days, aprox. 290 years
  106. var date time.Time
  107. var intPart = int64(excelTime)
  108. // Excel uses Julian dates prior to March 1st 1900, and Gregorian
  109. // thereafter.
  110. if intPart <= 61 {
  111. const OFFSET1900 = 15018.0
  112. const OFFSET1904 = 16480.0
  113. const MJD0 float64 = 2400000.5
  114. var date time.Time
  115. if date1904 {
  116. date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1904)
  117. } else {
  118. date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1900)
  119. }
  120. return date
  121. }
  122. var floatPart = excelTime - float64(intPart)
  123. var dayNanoSeconds float64 = 24 * 60 * 60 * 1000 * 1000 * 1000
  124. if date1904 {
  125. date = time.Date(1904, 1, 1, 0, 0, 0, 0, time.UTC)
  126. } else {
  127. date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
  128. }
  129. // Duration is limited to aprox. 290 years
  130. for intPart > MDD {
  131. durationDays := time.Duration(MDD) * time.Hour * 24
  132. date = date.Add(durationDays)
  133. intPart = intPart - MDD
  134. }
  135. durationDays := time.Duration(intPart) * time.Hour * 24
  136. durationPart := time.Duration(dayNanoSeconds * floatPart)
  137. return date.Add(durationDays).Add(durationPart)
  138. }