zoneinfo_plan9.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Copyright 2011 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. // Parse Plan 9 timezone(2) files.
  5. package time
  6. import (
  7. "errors"
  8. "runtime"
  9. "syscall"
  10. )
  11. func isSpace(r rune) bool {
  12. return r == ' ' || r == '\t' || r == '\n'
  13. }
  14. // Copied from strings to avoid a dependency.
  15. func fields(s string) []string {
  16. // First count the fields.
  17. n := 0
  18. inField := false
  19. for _, rune := range s {
  20. wasInField := inField
  21. inField = !isSpace(rune)
  22. if inField && !wasInField {
  23. n++
  24. }
  25. }
  26. // Now create them.
  27. a := make([]string, n)
  28. na := 0
  29. fieldStart := -1 // Set to -1 when looking for start of field.
  30. for i, rune := range s {
  31. if isSpace(rune) {
  32. if fieldStart >= 0 {
  33. a[na] = s[fieldStart:i]
  34. na++
  35. fieldStart = -1
  36. }
  37. } else if fieldStart == -1 {
  38. fieldStart = i
  39. }
  40. }
  41. if fieldStart >= 0 { // Last field might end at EOF.
  42. a[na] = s[fieldStart:]
  43. }
  44. return a
  45. }
  46. func loadZoneDataPlan9(s string) (l *Location, err error) {
  47. f := fields(s)
  48. if len(f) < 4 {
  49. if len(f) == 2 && f[0] == "GMT" {
  50. return UTC, nil
  51. }
  52. return nil, badData
  53. }
  54. var zones [2]zone
  55. // standard timezone offset
  56. o, err := atoi(f[1])
  57. if err != nil {
  58. return nil, badData
  59. }
  60. zones[0] = zone{name: f[0], offset: o, isDST: false}
  61. // alternate timezone offset
  62. o, err = atoi(f[3])
  63. if err != nil {
  64. return nil, badData
  65. }
  66. zones[1] = zone{name: f[2], offset: o, isDST: true}
  67. // transition time pairs
  68. var tx []zoneTrans
  69. f = f[4:]
  70. for i := 0; i < len(f); i++ {
  71. zi := 0
  72. if i%2 == 0 {
  73. zi = 1
  74. }
  75. t, err := atoi(f[i])
  76. if err != nil {
  77. return nil, badData
  78. }
  79. t -= zones[0].offset
  80. tx = append(tx, zoneTrans{when: int64(t), index: uint8(zi)})
  81. }
  82. // Committed to succeed.
  83. l = &Location{zone: zones[:], tx: tx}
  84. // Fill in the cache with information about right now,
  85. // since that will be the most common lookup.
  86. sec, _ := now()
  87. for i := range tx {
  88. if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
  89. l.cacheStart = tx[i].when
  90. l.cacheEnd = omega
  91. if i+1 < len(tx) {
  92. l.cacheEnd = tx[i+1].when
  93. }
  94. l.cacheZone = &l.zone[tx[i].index]
  95. }
  96. }
  97. return l, nil
  98. }
  99. func loadZoneFilePlan9(name string) (*Location, error) {
  100. b, err := readFile(name)
  101. if err != nil {
  102. return nil, err
  103. }
  104. return loadZoneDataPlan9(string(b))
  105. }
  106. func initTestingZone() {
  107. z, err := loadLocation("America/Los_Angeles")
  108. if err != nil {
  109. panic("cannot load America/Los_Angeles for testing: " + err.Error())
  110. }
  111. z.name = "Local"
  112. localLoc = *z
  113. }
  114. func initLocal() {
  115. t, ok := syscall.Getenv("timezone")
  116. if ok {
  117. if z, err := loadZoneDataPlan9(t); err == nil {
  118. localLoc = *z
  119. return
  120. }
  121. } else {
  122. if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
  123. localLoc = *z
  124. localLoc.name = "Local"
  125. return
  126. }
  127. }
  128. // Fall back to UTC.
  129. localLoc.name = "UTC"
  130. }
  131. func loadLocation(name string) (*Location, error) {
  132. if z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name); err == nil {
  133. z.name = name
  134. return z, nil
  135. }
  136. return nil, errors.New("unknown time zone " + name)
  137. }
  138. func forceZipFileForTesting(zipOnly bool) {
  139. // We only use the zip file anyway.
  140. }