OLD | NEW |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // +build darwin freebsd linux openbsd plan9 | |
6 | |
7 package time | 5 package time |
8 | 6 |
9 import "sync" | 7 import "sync" |
10 | 8 |
11 // Parsed representation | 9 // A Location maps time instants to the zone in use at that time. |
12 type zone struct { | 10 // Typically, the Location represents the collection of time offsets |
13 » utcoff int | 11 // in use in a geographical area, such as CEST and CET for central Europe. |
14 » isdst bool | 12 type Location struct { |
15 » name string | 13 » name string |
| 14 » zone []zone |
| 15 » tx []zoneTrans |
| 16 |
| 17 » // Most lookups will be for the current time. |
| 18 » // To avoid the binary search through tx, keep a |
| 19 » // static one-element cache that gives the correct |
| 20 » // zone for the time when the Location was created. |
| 21 » // if cacheStart <= t <= cacheEnd, |
| 22 » // lookup can return cacheZone. |
| 23 » // The units for cacheStart and cacheEnd are seconds |
| 24 » // since January 1, 1970 UTC, to match the argument |
| 25 » // to lookup. |
| 26 » cacheStart int64 |
| 27 » cacheEnd int64 |
| 28 » cacheZone *zone |
16 } | 29 } |
17 | 30 |
18 type zonetime struct { | 31 // A zone represents a single time zone such as CEST or CET. |
19 » time int32 // transition time, in seconds since 1970 GMT | 32 type zone struct { |
20 » zone *zone // the zone that goes into effect at that time | 33 » name string // abbreviated name, "CET" |
| 34 » offset int // seconds east of UTC |
| 35 » isDST bool // is this zone Daylight Savings Time? |
| 36 } |
| 37 |
| 38 // A zoneTrans represents a single time zone transition. |
| 39 type zoneTrans struct { |
| 40 » when int64 // transition time, in seconds since 1970 GMT |
| 41 » index uint8 // the index of the zone that goes into effect at tha
t time |
21 isstd, isutc bool // ignored - no idea what these mean | 42 isstd, isutc bool // ignored - no idea what these mean |
22 } | 43 } |
23 | 44 |
24 var zones []zonetime | 45 // UTC represents Universal Coordinated Time (UTC). |
25 var onceSetupZone sync.Once | 46 var UTC *Location = &utcLoc |
26 | 47 |
27 // Look up the correct time zone (daylight savings or not) for the given unix ti
me, in the current location. | 48 // utcLoc is separate so that get can refer to &utcLoc |
28 func lookupTimezone(sec int64) (zone string, offset int) { | 49 // and ensure that it never returns a nil *Location, |
29 » onceSetupZone.Do(setupZone) | 50 // even if a badly behaved client has changed UTC. |
30 » if len(zones) == 0 { | 51 var utcLoc = Location{name: "UTC"} |
31 » » return "UTC", 0 | 52 |
| 53 // Local represents the system's local time zone. |
| 54 var Local *Location = &localLoc |
| 55 |
| 56 // localLoc is separate so that initLocal can initialize |
| 57 // it even if a client has changed Local. |
| 58 var localLoc Location |
| 59 var localOnce sync.Once |
| 60 |
| 61 func (l *Location) get() *Location { |
| 62 » if l == nil { |
| 63 » » return &utcLoc |
| 64 » } |
| 65 » if l == &localLoc { |
| 66 » » localOnce.Do(initLocal) |
| 67 » } |
| 68 » return l |
| 69 } |
| 70 |
| 71 // String returns a descriptive name for the time zone information, |
| 72 // corresponding to the argument to LoadLocation. |
| 73 func (l *Location) String() string { |
| 74 » return l.get().name |
| 75 } |
| 76 |
| 77 // FixedZone returns a Location that always uses |
| 78 // the given zone name and offset (seconds east of UTC). |
| 79 func FixedZone(name string, offset int) *Location { |
| 80 » l := &Location{ |
| 81 » » name: name, |
| 82 » » zone: []zone{{name, offset, false}}, |
| 83 » » tx: []zoneTrans{{-1 << 63, 0, false, false}}, |
| 84 » » cacheStart: -1 << 63, |
| 85 » » cacheEnd: 1<<63 - 1, |
| 86 » } |
| 87 » l.cacheZone = &l.zone[0] |
| 88 » return l |
| 89 } |
| 90 |
| 91 // lookup returns information about the time zone in use at an |
| 92 // instant in time expressed as seconds since January 1, 1970 00:00:00 UTC. |
| 93 // |
| 94 // The returned information gives the name of the zone (such as "CET"), |
| 95 // the start and end times bracketing sec when that zone is in effect, |
| 96 // the offset in seconds east of UTC (such as -5*60*60), and whether |
| 97 // the daylight savings is being observed at that time. |
| 98 func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start
, end int64) { |
| 99 » l = l.get() |
| 100 |
| 101 » if len(l.tx) == 0 { |
| 102 » » name = "UTC" |
| 103 » » offset = 0 |
| 104 » » isDST = false |
| 105 » » start = -1 << 63 |
| 106 » » end = 1<<63 - 1 |
| 107 » » return |
32 } | 108 } |
33 | 109 |
34 » // Binary search for entry with largest time <= sec | 110 » if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.ca
cheEnd { |
35 » tz := zones | 111 » » name = zone.name |
36 » for len(tz) > 1 { | 112 » » offset = zone.offset |
37 » » m := len(tz) / 2 | 113 » » isDST = zone.isDST |
38 » » if sec < int64(tz[m].time) { | 114 » » start = l.cacheStart |
39 » » » tz = tz[0:m] | 115 » » end = l.cacheEnd |
| 116 » » return |
| 117 » } |
| 118 |
| 119 » // Binary search for entry with largest time <= sec. |
| 120 » // Not using sort.Search to avoid dependencies. |
| 121 » tx := l.tx |
| 122 » end = 1<<63 - 1 |
| 123 » for len(tx) > 1 { |
| 124 » » m := len(tx) / 2 |
| 125 » » lim := tx[m].when |
| 126 » » if sec < lim { |
| 127 » » » end = lim |
| 128 » » » tx = tx[0:m] |
40 } else { | 129 } else { |
41 » » » tz = tz[m:] | 130 » » » tx = tx[m:] |
42 } | 131 } |
43 } | 132 } |
44 » z := tz[0].zone | 133 » zone := &l.zone[tx[0].index] |
45 » return z.name, z.utcoff | 134 » name = zone.name |
| 135 » offset = zone.offset |
| 136 » isDST = zone.isDST |
| 137 » start = tx[0].when |
| 138 » // end = maintained during the search |
| 139 » return |
46 } | 140 } |
47 | 141 |
48 // lookupByName returns the time offset for the | 142 // lookupName returns information about the time zone with |
49 // time zone with the given abbreviation. It only considers | 143 // the given name (such as "EST"). |
50 // time zones that apply to the current system. | 144 func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) { |
51 // For example, for a system configured as being in New York, | 145 » l = l.get() |
52 // it only recognizes "EST" and "EDT". | 146 » for i := range l.zone { |
53 // For a system in San Francisco, "PST" and "PDT". | 147 » » zone := &l.zone[i] |
54 // For a system in Sydney, "EST" and "EDT", though they have | 148 » » if zone.name == name { |
55 // different meanings than they do in New York. | 149 » » » return zone.offset, zone.isDST, true |
56 func lookupByName(name string) (off int, found bool) { | |
57 » onceSetupZone.Do(setupZone) | |
58 » for _, z := range zones { | |
59 » » if name == z.zone.name { | |
60 » » » return z.zone.utcoff, true | |
61 } | 150 } |
62 } | 151 } |
63 » return 0, false | 152 » return |
64 } | 153 } |
| 154 |
| 155 // lookupOffset returns information about the time zone with |
| 156 // the given offset (such as -5*60*60). |
| 157 func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) { |
| 158 l = l.get() |
| 159 for i := range l.zone { |
| 160 zone := &l.zone[i] |
| 161 if zone.offset == offset { |
| 162 return zone.name, zone.isDST, true |
| 163 } |
| 164 } |
| 165 return |
| 166 } |
| 167 |
| 168 // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment |
| 169 // syntax too, but I don't feel like implementing it today. |
| 170 |
| 171 // NOTE(rsc): Using the IANA names below means ensuring we have access |
| 172 // to the database. Probably we will ship the files in $GOROOT/lib/zoneinfo/ |
| 173 // and only look there if there are no system files available (such as on Window
s). |
| 174 // The files total 200 kB. |
| 175 |
| 176 // LoadLocation returns the Location with the given name. |
| 177 // |
| 178 // If the name is "" or "UTC", LoadLocation returns UTC. |
| 179 // If the name is "Local", LoadLocation returns Local. |
| 180 // |
| 181 // Otherwise, the name is taken to be a location name corresponding to a file |
| 182 // in the IANA Time Zone database, such as "America/New_York". |
| 183 func LoadLocation(name string) (*Location, error) { |
| 184 if name == "" || name == "UTC" { |
| 185 return UTC, nil |
| 186 } |
| 187 if name == "Local" { |
| 188 return Local, nil |
| 189 } |
| 190 return loadLocation(name) |
| 191 } |
OLD | NEW |