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.
13 // BUG(brainman): The Windows implementation assumes that
14 // this year's rules for daylight savings time apply to all previous
15 // and future years as well.
17 // TODO(brainman): use GetDynamicTimeZoneInformation, whenever posible (Vista and up),
18 // to improve on situation described in the bug above.
24 month
, day
, dayofweek
int
25 hour
, minute
, second
int
30 // Populate zone struct with Windows supplied information. Returns true, if data is valid.
31 func (z
*zone
) populate(bias
, biasdelta
int32, d
*syscall
.Systemtime
, name
[]uint16) (dateisgood
bool) {
32 z
.name
= syscall
.UTF16ToString(name
)
34 z
.year
= int64(d
.Year
)
35 z
.month
= int(d
.Month
)
37 z
.dayofweek
= int(d
.DayOfWeek
)
39 z
.minute
= int(d
.Minute
)
40 z
.second
= int(d
.Second
)
41 dateisgood
= d
.Month
!= 0
43 z
.offset
+= int(biasdelta
)
45 z
.offset
= -z
.offset
* 60
49 // Pre-calculte cutoff time in seconds since the Unix epoch, if data is supplied in "absolute" format.
50 func (z
*zone
) preCalculateAbsSec() {
52 z
.abssec
= (&Time
{z
.year
, int(z
.month
), int(z
.day
), int(z
.hour
), int(z
.minute
), int(z
.second
), 0, 0, ""}).Seconds()
53 // Time given is in "local" time. Adjust it for "utc".
54 z
.abssec
-= int64(z
.prev
.offset
)
58 // Convert zone cutoff time to sec in number of seconds since the Unix epoch, given particualar year.
59 func (z
*zone
) cutoffSeconds(year
int64) int64 {
60 // Windows specifies daylight savings information in "day in month" format:
61 // z.month is month number (1-12)
62 // z.dayofweek is appropriate weekday (Sunday=0 to Saturday=6)
63 // z.day is week within the month (1 to 5, where 5 is last week of the month)
64 // z.hour, z.minute and z.second are absolute time
65 t
:= &Time
{year
, int(z
.month
), 1, int(z
.hour
), int(z
.minute
), int(z
.second
), 0, 0, ""}
66 t
= SecondsToUTC(t
.Seconds())
67 i
:= int(z
.dayofweek
) - t
.Weekday
72 if week
:= int(z
.day
) - 1; week
< 4 {
75 // "Last" instance of the day.
77 if t
.Day
> months(year
)[t
.Month
] {
81 // Result is in "local" time. Adjust it for "utc".
82 return t
.Seconds() - int64(z
.prev
.offset
)
85 // Is t before the cutoff for switching to z?
86 func (z
*zone
) isBeforeCutoff(t
*Time
) bool {
89 // "day in month" format used
90 coff
= z
.cutoffSeconds(t
.Year
)
92 // "absolute" format used
95 return t
.Seconds() < coff
98 type zoneinfo
struct {
99 disabled
bool // daylight saving time is not used localy
101 januaryIsStd
bool // is january 1 standard time?
105 // Pick zone (std or dst) t time belongs to.
106 func (zi
*zoneinfo
) pickZone(t
*Time
) *zone
{
109 if !zi
.dst
.isBeforeCutoff(t
) && zi
.std
.isBeforeCutoff(t
) {
110 // after switch to daylight time and before the switch back to standard
114 if zi
.std
.isBeforeCutoff(t
) ||
!zi
.dst
.isBeforeCutoff(t
) {
115 // before switch to standard time or after the switch back to daylight
123 var initError os
.Error
124 var onceSetupZone sync
.Once
127 var i syscall
.Timezoneinformation
128 if _
, e
:= syscall
.GetTimeZoneInformation(&i
); e
!= 0 {
129 initError
= os
.NewSyscallError("GetTimeZoneInformation", e
)
132 if !tz
.std
.populate(i
.Bias
, i
.StandardBias
, &i
.StandardDate
, i
.StandardName
[0:]) {
134 tz
.offsetIfDisabled
= tz
.std
.offset
137 tz
.std
.prev
= &tz
.dst
138 tz
.dst
.populate(i
.Bias
, i
.DaylightBias
, &i
.DaylightDate
, i
.DaylightName
[0:])
139 tz
.dst
.prev
= &tz
.std
140 tz
.std
.preCalculateAbsSec()
141 tz
.dst
.preCalculateAbsSec()
142 // Is january 1 standard time this year?
144 tz
.januaryIsStd
= tz
.dst
.cutoffSeconds(t
.Year
) < tz
.std
.cutoffSeconds(t
.Year
)
147 // Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
148 func lookupTimezone(sec
int64) (zone
string, offset
int) {
149 onceSetupZone
.Do(setupZone
)
150 if initError
!= nil {
154 return "", tz
.offsetIfDisabled
156 t
:= SecondsToUTC(sec
)
158 if tz
.std
.year
== 0 {
159 // "day in month" format used
162 // "absolute" format used
163 if tz
.std
.year
== t
.Year
{
164 // we have rule for the year in question
167 // we do not have any information for that year,
168 // will assume standard offset all year around
171 return z
.name
, z
.offset
174 // lookupByName returns the time offset for the
175 // time zone with the given abbreviation. It only considers
176 // time zones that apply to the current system.
177 func lookupByName(name
string) (off
int, found
bool) {
178 onceSetupZone
.Do(setupZone
)
179 if initError
!= nil {
183 return tz
.offsetIfDisabled
, false
187 return tz
.std
.offset
, true
189 return tz
.dst
.offset
, true