zoneinfo_windows.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package time
  5. import (
  6. "errors"
  7. "internal/syscall/windows/registry"
  8. "runtime"
  9. "syscall"
  10. )
  11. var zoneSources = []string{
  12. runtime.GOROOT() + "/lib/time/zoneinfo.zip",
  13. }
  14. // TODO(rsc): Fall back to copy of zoneinfo files.
  15. // BUG(brainman,rsc): On Windows, the operating system does not provide complete
  16. // time zone information.
  17. // The implementation assumes that this year's rules for daylight savings
  18. // time apply to all previous and future years as well.
  19. // matchZoneKey checks if stdname and dstname match the corresponding key
  20. // values "MUI_Std" and MUI_Dlt" or "Std" and "Dlt" (the latter down-level
  21. // from Vista) in the kname key stored under the open registry key zones.
  22. func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (matched bool, err2 error) {
  23. k, err := registry.OpenKey(zones, kname, registry.READ)
  24. if err != nil {
  25. return false, err
  26. }
  27. defer k.Close()
  28. var std, dlt string
  29. if err = registry.LoadRegLoadMUIString(); err == nil {
  30. // Try MUI_Std and MUI_Dlt first, fallback to Std and Dlt if *any* error occurs
  31. std, err = k.GetMUIStringValue("MUI_Std")
  32. if err == nil {
  33. dlt, err = k.GetMUIStringValue("MUI_Dlt")
  34. }
  35. }
  36. if err != nil { // Fallback to Std and Dlt
  37. if std, _, err = k.GetStringValue("Std"); err != nil {
  38. return false, err
  39. }
  40. if dlt, _, err = k.GetStringValue("Dlt"); err != nil {
  41. return false, err
  42. }
  43. }
  44. if std != stdname {
  45. return false, nil
  46. }
  47. if dlt != dstname && dstname != stdname {
  48. return false, nil
  49. }
  50. return true, nil
  51. }
  52. // toEnglishName searches the registry for an English name of a time zone
  53. // whose zone names are stdname and dstname and returns the English name.
  54. func toEnglishName(stdname, dstname string) (string, error) {
  55. k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)
  56. if err != nil {
  57. return "", err
  58. }
  59. defer k.Close()
  60. names, err := k.ReadSubKeyNames()
  61. if err != nil {
  62. return "", err
  63. }
  64. for _, name := range names {
  65. matched, err := matchZoneKey(k, name, stdname, dstname)
  66. if err == nil && matched {
  67. return name, nil
  68. }
  69. }
  70. return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`)
  71. }
  72. // extractCAPS extracts capital letters from description desc.
  73. func extractCAPS(desc string) string {
  74. var short []rune
  75. for _, c := range desc {
  76. if 'A' <= c && c <= 'Z' {
  77. short = append(short, c)
  78. }
  79. }
  80. return string(short)
  81. }
  82. // abbrev returns the abbreviations to use for the given zone z.
  83. func abbrev(z *syscall.Timezoneinformation) (std, dst string) {
  84. stdName := syscall.UTF16ToString(z.StandardName[:])
  85. a, ok := abbrs[stdName]
  86. if !ok {
  87. dstName := syscall.UTF16ToString(z.DaylightName[:])
  88. // Perhaps stdName is not English. Try to convert it.
  89. englishName, err := toEnglishName(stdName, dstName)
  90. if err == nil {
  91. a, ok = abbrs[englishName]
  92. if ok {
  93. return a.std, a.dst
  94. }
  95. }
  96. // fallback to using capital letters
  97. return extractCAPS(stdName), extractCAPS(dstName)
  98. }
  99. return a.std, a.dst
  100. }
  101. // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
  102. // denoted by the system date+time d in the given year.
  103. // It is up to the caller to convert this local time into a UTC-based time.
  104. func pseudoUnix(year int, d *syscall.Systemtime) int64 {
  105. // Windows specifies daylight savings information in "day in month" format:
  106. // d.Month is month number (1-12)
  107. // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
  108. // d.Day is week within the month (1 to 5, where 5 is last week of the month)
  109. // d.Hour, d.Minute and d.Second are absolute time
  110. day := 1
  111. t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC)
  112. i := int(d.DayOfWeek) - int(t.Weekday())
  113. if i < 0 {
  114. i += 7
  115. }
  116. day += i
  117. if week := int(d.Day) - 1; week < 4 {
  118. day += week * 7
  119. } else {
  120. // "Last" instance of the day.
  121. day += 4 * 7
  122. if day > daysIn(Month(d.Month), year) {
  123. day -= 7
  124. }
  125. }
  126. return t.sec() + int64(day-1)*secondsPerDay + internalToUnix
  127. }
  128. func initLocalFromTZI(i *syscall.Timezoneinformation) {
  129. l := &localLoc
  130. l.name = "Local"
  131. nzone := 1
  132. if i.StandardDate.Month > 0 {
  133. nzone++
  134. }
  135. l.zone = make([]zone, nzone)
  136. stdname, dstname := abbrev(i)
  137. std := &l.zone[0]
  138. std.name = stdname
  139. if nzone == 1 {
  140. // No daylight savings.
  141. std.offset = -int(i.Bias) * 60
  142. l.cacheStart = alpha
  143. l.cacheEnd = omega
  144. l.cacheZone = std
  145. l.tx = make([]zoneTrans, 1)
  146. l.tx[0].when = l.cacheStart
  147. l.tx[0].index = 0
  148. return
  149. }
  150. // StandardBias must be ignored if StandardDate is not set,
  151. // so this computation is delayed until after the nzone==1
  152. // return above.
  153. std.offset = -int(i.Bias+i.StandardBias) * 60
  154. dst := &l.zone[1]
  155. dst.name = dstname
  156. dst.offset = -int(i.Bias+i.DaylightBias) * 60
  157. dst.isDST = true
  158. // Arrange so that d0 is first transition date, d1 second,
  159. // i0 is index of zone after first transition, i1 second.
  160. d0 := &i.StandardDate
  161. d1 := &i.DaylightDate
  162. i0 := 0
  163. i1 := 1
  164. if d0.Month > d1.Month {
  165. d0, d1 = d1, d0
  166. i0, i1 = i1, i0
  167. }
  168. // 2 tx per year, 100 years on each side of this year
  169. l.tx = make([]zoneTrans, 400)
  170. t := Now().UTC()
  171. year := t.Year()
  172. txi := 0
  173. for y := year - 100; y < year+100; y++ {
  174. tx := &l.tx[txi]
  175. tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
  176. tx.index = uint8(i0)
  177. txi++
  178. tx = &l.tx[txi]
  179. tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
  180. tx.index = uint8(i1)
  181. txi++
  182. }
  183. }
  184. var usPacific = syscall.Timezoneinformation{
  185. Bias: 8 * 60,
  186. StandardName: [32]uint16{
  187. 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
  188. },
  189. StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2},
  190. DaylightName: [32]uint16{
  191. 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
  192. },
  193. DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2},
  194. DaylightBias: -60,
  195. }
  196. var aus = syscall.Timezoneinformation{
  197. Bias: -10 * 60,
  198. StandardName: [32]uint16{
  199. 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
  200. },
  201. StandardDate: syscall.Systemtime{Month: 4, Day: 1, Hour: 3},
  202. DaylightName: [32]uint16{
  203. 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
  204. },
  205. DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2},
  206. DaylightBias: -60,
  207. }
  208. func initLocal() {
  209. var i syscall.Timezoneinformation
  210. if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
  211. localLoc.name = "UTC"
  212. return
  213. }
  214. initLocalFromTZI(&i)
  215. }