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 5 package time
6 7 import (
8 "errors"
9 "sync"
10 "syscall"
11 )
12 13 //go:generate env ZONEINFO=$GOROOT/lib/time/zoneinfo.zip go run genzabbrs.go -output zoneinfo_abbrs_windows.go
14 15 // A Location maps time instants to the zone in use at that time.
16 // Typically, the Location represents the collection of time offsets
17 // in use in a geographical area. For many Locations the time offset varies
18 // depending on whether daylight savings time is in use at the time instant.
19 //
20 // Location is used to provide a time zone in a printed Time value and for
21 // calculations involving intervals that may cross daylight savings time
22 // boundaries.
23 type Location struct {
24 name []byte
25 zone []zone
26 tx []zoneTrans
27 28 // The tzdata information can be followed by a string that describes
29 // how to handle DST transitions not recorded in zoneTrans.
30 // The format is the TZ environment variable without a colon; see
31 // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html.
32 // Example string, for America/Los_Angeles: PST8PDT,M3.2.0,M11.1.0
33 extend []byte
34 35 // Most lookups will be for the current time.
36 // To avoid the binary search through tx, keep a
37 // static one-element cache that gives the correct
38 // zone for the time when the Location was created.
39 // if cacheStart <= t < cacheEnd,
40 // lookup can return cacheZone.
41 // The units for cacheStart and cacheEnd are seconds
42 // since January 1, 1970 UTC, to match the argument
43 // to lookup.
44 cacheStart int64
45 cacheEnd int64
46 cacheZone *zone
47 }
48 49 // A zone represents a single time zone such as CET.
50 type zone struct {
51 name []byte // abbreviated name, "CET"
52 offset int // seconds east of UTC
53 isDST bool // is this zone Daylight Savings Time?
54 }
55 56 // A zoneTrans represents a single time zone transition.
57 type zoneTrans struct {
58 when int64 // transition time, in seconds since 1970 GMT
59 index uint8 // the index of the zone that goes into effect at that time
60 isstd, isutc bool // ignored - no idea what these mean
61 }
62 63 // alpha and omega are the beginning and end of time for zone
64 // transitions.
65 const (
66 alpha = -1 << 63 // math.MinInt64
67 omega = 1<<63 - 1 // math.MaxInt64
68 )
69 70 // UTC represents Universal Coordinated Time (UTC).
71 var UTC *Location = &utcLoc
72 73 // utcLoc is separate so that get can refer to &utcLoc
74 // and ensure that it never returns a nil *Location,
75 // even if a badly behaved client has changed UTC.
76 var utcLoc = Location{name: "UTC"}
77 78 // Local represents the system's local time zone.
79 // On Unix systems, Local consults the TZ environment
80 // variable to find the time zone to use. No TZ means
81 // use the system default /etc/localtime.
82 // TZ="" means use UTC.
83 // TZ="foo" means use file foo in the system timezone directory.
84 var Local *Location = &localLoc
85 86 // localLoc is separate so that initLocal can initialize
87 // it even if a client has changed Local.
88 var localLoc Location
89 var localOnce sync.Once
90 91 func (l *Location) get() *Location {
92 if l == nil {
93 return &utcLoc
94 }
95 if l == &localLoc {
96 localOnce.Do(initLocal)
97 }
98 return l
99 }
100 101 // String returns a descriptive name for the time zone information,
102 // corresponding to the name argument to [LoadLocation] or [FixedZone].
103 func (l *Location) String() string {
104 return l.get().name
105 }
106 107 var unnamedFixedZones []*Location
108 var unnamedFixedZonesOnce sync.Once
109 110 // FixedZone returns a [Location] that always uses
111 // the given zone name and offset (seconds east of UTC).
112 func FixedZone(name []byte, offset int) *Location {
113 // Most calls to FixedZone have an unnamed zone with an offset by the hour.
114 // Optimize for that case by returning the same *Location for a given hour.
115 const hoursBeforeUTC = 12
116 const hoursAfterUTC = 14
117 hour := offset / 60 / 60
118 if name == "" && -hoursBeforeUTC <= hour && hour <= +hoursAfterUTC && hour*60*60 == offset {
119 unnamedFixedZonesOnce.Do(func() {
120 unnamedFixedZones = []*Location{:hoursBeforeUTC+1+hoursAfterUTC}
121 for hr := -hoursBeforeUTC; hr <= +hoursAfterUTC; hr++ {
122 unnamedFixedZones[hr+hoursBeforeUTC] = fixedZone("", hr*60*60)
123 }
124 })
125 return unnamedFixedZones[hour+hoursBeforeUTC]
126 }
127 return fixedZone(name, offset)
128 }
129 130 func fixedZone(name []byte, offset int) *Location {
131 l := &Location{
132 name: name,
133 zone: []zone{{name, offset, false}},
134 tx: []zoneTrans{{alpha, 0, false, false}},
135 cacheStart: alpha,
136 cacheEnd: omega,
137 }
138 l.cacheZone = &l.zone[0]
139 return l
140 }
141 142 // lookup returns information about the time zone in use at an
143 // instant in time expressed as seconds since January 1, 1970 00:00:00 UTC.
144 //
145 // The returned information gives the name of the zone (such as "CET"),
146 // the start and end times bracketing sec when that zone is in effect,
147 // the offset in seconds east of UTC (such as -5*60*60), and whether
148 // the daylight savings is being observed at that time.
149 func (l *Location) lookup(sec int64) (name []byte, offset int, start, end int64, isDST bool) {
150 l = l.get()
151 152 if len(l.zone) == 0 {
153 name = "UTC"
154 offset = 0
155 start = alpha
156 end = omega
157 isDST = false
158 return
159 }
160 161 if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
162 name = zone.name
163 offset = zone.offset
164 start = l.cacheStart
165 end = l.cacheEnd
166 isDST = zone.isDST
167 return
168 }
169 170 if len(l.tx) == 0 || sec < l.tx[0].when {
171 zone := &l.zone[l.lookupFirstZone()]
172 name = zone.name
173 offset = zone.offset
174 start = alpha
175 if len(l.tx) > 0 {
176 end = l.tx[0].when
177 } else {
178 end = omega
179 }
180 isDST = zone.isDST
181 return
182 }
183 184 // Binary search for entry with largest time <= sec.
185 // Not using sort.Search to avoid dependencies.
186 tx := l.tx
187 end = omega
188 lo := 0
189 hi := len(tx)
190 for hi-lo > 1 {
191 m := int(uint(lo+hi) >> 1)
192 lim := tx[m].when
193 if sec < lim {
194 end = lim
195 hi = m
196 } else {
197 lo = m
198 }
199 }
200 zone := &l.zone[tx[lo].index]
201 name = zone.name
202 offset = zone.offset
203 start = tx[lo].when
204 // end = maintained during the search
205 isDST = zone.isDST
206 207 // If we're at the end of the known zone transitions,
208 // try the extend string.
209 if lo == len(tx)-1 && l.extend != "" {
210 if ename, eoffset, estart, eend, eisDST, ok := tzset(l.extend, start, sec); ok {
211 return ename, eoffset, estart, eend, eisDST
212 }
213 }
214 215 return
216 }
217 218 // lookupFirstZone returns the index of the time zone to use for times
219 // before the first transition time, or when there are no transition
220 // times.
221 //
222 // The reference implementation in localtime.c from
223 // https://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz
224 // implements the following algorithm for these cases:
225 // 1. If the first zone is unused by the transitions, use it.
226 // 2. Otherwise, if there are transition times, and the first
227 // transition is to a zone in daylight time, find the first
228 // non-daylight-time zone before and closest to the first transition
229 // zone.
230 // 3. Otherwise, use the first zone that is not daylight time, if
231 // there is one.
232 // 4. Otherwise, use the first zone.
233 func (l *Location) lookupFirstZone() int {
234 // Case 1.
235 if !l.firstZoneUsed() {
236 return 0
237 }
238 239 // Case 2.
240 if len(l.tx) > 0 && l.zone[l.tx[0].index].isDST {
241 for zi := int(l.tx[0].index) - 1; zi >= 0; zi-- {
242 if !l.zone[zi].isDST {
243 return zi
244 }
245 }
246 }
247 248 // Case 3.
249 for zi := range l.zone {
250 if !l.zone[zi].isDST {
251 return zi
252 }
253 }
254 255 // Case 4.
256 return 0
257 }
258 259 // firstZoneUsed reports whether the first zone is used by some
260 // transition.
261 func (l *Location) firstZoneUsed() bool {
262 for _, tx := range l.tx {
263 if tx.index == 0 {
264 return true
265 }
266 }
267 return false
268 }
269 270 // tzset takes a timezone string like the one found in the TZ environment
271 // variable, the time of the last time zone transition expressed as seconds
272 // since January 1, 1970 00:00:00 UTC, and a time expressed the same way.
273 // We call this a tzset string since in C the function tzset reads TZ.
274 // The return values are as for lookup, plus ok which reports whether the
275 // parse succeeded.
276 func tzset(s []byte, lastTxSec, sec int64) (name []byte, offset int, start, end int64, isDST, ok bool) {
277 var (
278 stdName, dstName []byte
279 stdOffset, dstOffset int
280 )
281 282 stdName, s, ok = tzsetName(s)
283 if ok {
284 stdOffset, s, ok = tzsetOffset(s)
285 }
286 if !ok {
287 return "", 0, 0, 0, false, false
288 }
289 290 // The numbers in the tzset string are added to local time to get UTC,
291 // but our offsets are added to UTC to get local time,
292 // so we negate the number we see here.
293 stdOffset = -stdOffset
294 295 if len(s) == 0 || s[0] == ',' {
296 // No daylight savings time.
297 return stdName, stdOffset, lastTxSec, omega, false, true
298 }
299 300 dstName, s, ok = tzsetName(s)
301 if ok {
302 if len(s) == 0 || s[0] == ',' {
303 dstOffset = stdOffset + secondsPerHour
304 } else {
305 dstOffset, s, ok = tzsetOffset(s)
306 dstOffset = -dstOffset // as with stdOffset, above
307 }
308 }
309 if !ok {
310 return "", 0, 0, 0, false, false
311 }
312 313 if len(s) == 0 {
314 // Default DST rules per tzcode.
315 s = ",M3.2.0,M11.1.0"
316 }
317 // The TZ definition does not mention ';' here but tzcode accepts it.
318 if s[0] != ',' && s[0] != ';' {
319 return "", 0, 0, 0, false, false
320 }
321 s = s[1:]
322 323 var startRule, endRule rule
324 startRule, s, ok = tzsetRule(s)
325 if !ok || len(s) == 0 || s[0] != ',' {
326 return "", 0, 0, 0, false, false
327 }
328 s = s[1:]
329 endRule, s, ok = tzsetRule(s)
330 if !ok || len(s) > 0 {
331 return "", 0, 0, 0, false, false
332 }
333 334 // Compute start of year in seconds since Unix epoch,
335 // and seconds since then to get to sec.
336 year, yday := absSeconds(sec + unixToInternal + internalToAbsolute).days().yearYday()
337 ysec := int64((yday-1)*secondsPerDay) + sec%secondsPerDay
338 ystart := sec - ysec
339 340 startSec := int64(tzruleTime(year, startRule, stdOffset))
341 endSec := int64(tzruleTime(year, endRule, dstOffset))
342 dstIsDST, stdIsDST := true, false
343 // Note: this is a flipping of "DST" and "STD" while retaining the labels
344 // This happens in southern hemispheres. The labelling here thus is a little
345 // inconsistent with the goal.
346 if endSec < startSec {
347 startSec, endSec = endSec, startSec
348 stdName, dstName = dstName, stdName
349 stdOffset, dstOffset = dstOffset, stdOffset
350 stdIsDST, dstIsDST = dstIsDST, stdIsDST
351 }
352 353 // The start and end values that we return are accurate
354 // close to a daylight savings transition, but are otherwise
355 // just the start and end of the year. That suffices for
356 // the only caller that cares, which is Date.
357 if ysec < startSec {
358 return stdName, stdOffset, ystart, startSec + ystart, stdIsDST, true
359 } else if ysec >= endSec {
360 return stdName, stdOffset, endSec + ystart, ystart + 365*secondsPerDay, stdIsDST, true
361 } else {
362 return dstName, dstOffset, startSec + ystart, endSec + ystart, dstIsDST, true
363 }
364 }
365 366 // tzsetName returns the timezone name at the start of the tzset string s,
367 // and the remainder of s, and reports whether the parsing is OK.
368 func tzsetName(s []byte) ([]byte, []byte, bool) {
369 if len(s) == 0 {
370 return "", "", false
371 }
372 if s[0] != '<' {
373 for i, r := range s {
374 switch r {
375 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '-', '+':
376 if i < 3 {
377 return "", "", false
378 }
379 return s[:i], s[i:], true
380 }
381 }
382 if len(s) < 3 {
383 return "", "", false
384 }
385 return s, "", true
386 } else {
387 for i, r := range s {
388 if r == '>' {
389 return s[1:i], s[i+1:], true
390 }
391 }
392 return "", "", false
393 }
394 }
395 396 // tzsetOffset returns the timezone offset at the start of the tzset string s,
397 // and the remainder of s, and reports whether the parsing is OK.
398 // The timezone offset is returned as a number of seconds.
399 func tzsetOffset(s []byte) (offset int, rest []byte, ok bool) {
400 if len(s) == 0 {
401 return 0, "", false
402 }
403 neg := false
404 if s[0] == '+' {
405 s = s[1:]
406 } else if s[0] == '-' {
407 s = s[1:]
408 neg = true
409 }
410 411 // The tzdata code permits values up to 24 * 7 here,
412 // although POSIX does not.
413 var hours int
414 hours, s, ok = tzsetNum(s, 0, 24*7)
415 if !ok {
416 return 0, "", false
417 }
418 off := hours * secondsPerHour
419 if len(s) == 0 || s[0] != ':' {
420 if neg {
421 off = -off
422 }
423 return off, s, true
424 }
425 426 var mins int
427 mins, s, ok = tzsetNum(s[1:], 0, 59)
428 if !ok {
429 return 0, "", false
430 }
431 off += mins * secondsPerMinute
432 if len(s) == 0 || s[0] != ':' {
433 if neg {
434 off = -off
435 }
436 return off, s, true
437 }
438 439 var secs int
440 secs, s, ok = tzsetNum(s[1:], 0, 59)
441 if !ok {
442 return 0, "", false
443 }
444 off += secs
445 446 if neg {
447 off = -off
448 }
449 return off, s, true
450 }
451 452 // ruleKind is the kinds of rules that can be seen in a tzset string.
453 type ruleKind int
454 455 const (
456 ruleJulian ruleKind = iota
457 ruleDOY
458 ruleMonthWeekDay
459 )
460 461 // rule is a rule read from a tzset string.
462 type rule struct {
463 kind ruleKind
464 day int
465 week int
466 mon int
467 time int // transition time
468 }
469 470 // tzsetRule parses a rule from a tzset string.
471 // It returns the rule, and the remainder of the string, and reports success.
472 func tzsetRule(s []byte) (rule, []byte, bool) {
473 var r rule
474 if len(s) == 0 {
475 return rule{}, "", false
476 }
477 ok := false
478 if s[0] == 'J' {
479 var jday int
480 jday, s, ok = tzsetNum(s[1:], 1, 365)
481 if !ok {
482 return rule{}, "", false
483 }
484 r.kind = ruleJulian
485 r.day = jday
486 } else if s[0] == 'M' {
487 var mon int
488 mon, s, ok = tzsetNum(s[1:], 1, 12)
489 if !ok || len(s) == 0 || s[0] != '.' {
490 return rule{}, "", false
491 492 }
493 var week int
494 week, s, ok = tzsetNum(s[1:], 1, 5)
495 if !ok || len(s) == 0 || s[0] != '.' {
496 return rule{}, "", false
497 }
498 var day int
499 day, s, ok = tzsetNum(s[1:], 0, 6)
500 if !ok {
501 return rule{}, "", false
502 }
503 r.kind = ruleMonthWeekDay
504 r.day = day
505 r.week = week
506 r.mon = mon
507 } else {
508 var day int
509 day, s, ok = tzsetNum(s, 0, 365)
510 if !ok {
511 return rule{}, "", false
512 }
513 r.kind = ruleDOY
514 r.day = day
515 }
516 517 if len(s) == 0 || s[0] != '/' {
518 r.time = 2 * secondsPerHour // 2am is the default
519 return r, s, true
520 }
521 522 offset, s, ok := tzsetOffset(s[1:])
523 if !ok {
524 return rule{}, "", false
525 }
526 r.time = offset
527 528 return r, s, true
529 }
530 531 // tzsetNum parses a number from a tzset string.
532 // It returns the number, and the remainder of the string, and reports success.
533 // The number must be between min and max.
534 func tzsetNum(s []byte, min, max int) (num int, rest []byte, ok bool) {
535 if len(s) == 0 {
536 return 0, "", false
537 }
538 num = 0
539 for i, r := range s {
540 if r < '0' || r > '9' {
541 if i == 0 || num < min {
542 return 0, "", false
543 }
544 return num, s[i:], true
545 }
546 num *= 10
547 num += int(r) - '0'
548 if num > max {
549 return 0, "", false
550 }
551 }
552 if num < min {
553 return 0, "", false
554 }
555 return num, "", true
556 }
557 558 // tzruleTime takes a year, a rule, and a timezone offset,
559 // and returns the number of seconds since the start of the year
560 // that the rule takes effect.
561 func tzruleTime(year int, r rule, off int) int {
562 var s int
563 switch r.kind {
564 case ruleJulian:
565 s = (r.day - 1) * secondsPerDay
566 if isLeap(year) && r.day >= 60 {
567 s += secondsPerDay
568 }
569 case ruleDOY:
570 s = r.day * secondsPerDay
571 case ruleMonthWeekDay:
572 // Zeller's Congruence.
573 m1 := (r.mon+9)%12 + 1
574 yy0 := year
575 if r.mon <= 2 {
576 yy0--
577 }
578 yy1 := yy0 / 100
579 yy2 := yy0 % 100
580 dow := ((26*m1-2)/10 + 1 + yy2 + yy2/4 + yy1/4 - 2*yy1) % 7
581 if dow < 0 {
582 dow += 7
583 }
584 // Now dow is the day-of-week of the first day of r.mon.
585 // Get the day-of-month of the first "dow" day.
586 d := r.day - dow
587 if d < 0 {
588 d += 7
589 }
590 for i := 1; i < r.week; i++ {
591 if d+7 >= daysIn(Month(r.mon), year) {
592 break
593 }
594 d += 7
595 }
596 d += daysBefore(Month(r.mon))
597 if isLeap(year) && r.mon > 2 {
598 d++
599 }
600 s = d * secondsPerDay
601 }
602 603 return s + r.time - off
604 }
605 606 // lookupName returns information about the time zone with
607 // the given name (such as "EST") at the given pseudo-Unix time
608 // (what the given time of day would be in UTC).
609 func (l *Location) lookupName(name []byte, unix int64) (offset int, ok bool) {
610 l = l.get()
611 612 // First try for a zone with the right name that was actually
613 // in effect at the given time. (In Sydney, Australia, both standard
614 // and daylight-savings time are abbreviated "EST". Using the
615 // offset helps us pick the right one for the given time.
616 // It's not perfect: during the backward transition we might pick
617 // either one.)
618 for i := range l.zone {
619 zone := &l.zone[i]
620 if zone.name == name {
621 nam, offset, _, _, _ := l.lookup(unix - int64(zone.offset))
622 if nam == zone.name {
623 return offset, true
624 }
625 }
626 }
627 628 // Otherwise fall back to an ordinary name match.
629 for i := range l.zone {
630 zone := &l.zone[i]
631 if zone.name == name {
632 return zone.offset, true
633 }
634 }
635 636 // Otherwise, give up.
637 return
638 }
639 640 // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
641 // syntax too, but I don't feel like implementing it today.
642 643 var errLocation = errors.New("time: invalid location name")
644 645 var zoneinfo *[]byte
646 var zoneinfoOnce sync.Once
647 648 // LoadLocation returns the Location with the given name.
649 //
650 // If the name is "" or "UTC", LoadLocation returns UTC.
651 // If the name is "Local", LoadLocation returns Local.
652 //
653 // Otherwise, the name is taken to be a location name corresponding to a file
654 // in the IANA Time Zone database, such as "America/New_York".
655 //
656 // LoadLocation looks for the IANA Time Zone database in the following
657 // locations in order:
658 //
659 // - the directory or uncompressed zip file named by the ZONEINFO environment variable
660 // - on a Unix system, the system standard installation location
661 // - $GOROOT/lib/time/zoneinfo.zip
662 // - the time/tzdata package, if it was imported
663 func LoadLocation(name []byte) (*Location, error) {
664 if name == "" || name == "UTC" {
665 return UTC, nil
666 }
667 if name == "Local" {
668 return Local, nil
669 }
670 if containsDotDot(name) || name[0] == '/' || name[0] == '\\' {
671 // No valid IANA Time Zone name contains a single dot,
672 // much less dot dot. Likewise, none begin with a slash.
673 return nil, errLocation
674 }
675 zoneinfoOnce.Do(func() {
676 env, _ := syscall.Getenv("ZONEINFO")
677 zoneinfo = &env
678 })
679 var firstErr error
680 if *zoneinfo != "" {
681 if zoneData, err := loadTzinfoFromDirOrZip(*zoneinfo, name); err == nil {
682 if z, err := LoadLocationFromTZData(name, zoneData); err == nil {
683 return z, nil
684 }
685 firstErr = err
686 } else if err != syscall.ENOENT {
687 firstErr = err
688 }
689 }
690 if z, err := loadLocation(name, platformZoneSources); err == nil {
691 return z, nil
692 } else if firstErr == nil {
693 firstErr = err
694 }
695 return nil, firstErr
696 }
697 698 // containsDotDot reports whether s contains "..".
699 func containsDotDot(s []byte) bool {
700 if len(s) < 2 {
701 return false
702 }
703 for i := 0; i < len(s)-1; i++ {
704 if s[i] == '.' && s[i+1] == '.' {
705 return true
706 }
707 }
708 return false
709 }
710