-
-
Notifications
You must be signed in to change notification settings - Fork 22
/
timezone.go
201 lines (163 loc) · 5.49 KB
/
timezone.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// Package timezone provides utility for timezone.
package timezone
import (
"errors"
"fmt"
"strings"
"time"
)
var (
// ErrAmbiguousTzAbbreviations indicates ambiguous timezone abbreviations.
ErrAmbiguousTzAbbreviations = errors.New("Ambiguous timezone abbreviations")
errFormatInvalidTz = "Invalid timezone: %s"
errFormatInvalidTzAbbreviation = "Invalid timezone abbreviation: %s"
errFormatInvalidTzName = "Invalid timezone name: %s %s"
errFormatDoesNotHaveDST = "Does not have daylight savings: %s"
)
// Timezone represents a timezone information.
type Timezone struct {
tzInfos map[string]*TzInfo
tzAbbrInfos map[string][]*TzAbbreviationInfo
timezones map[string][]string
}
// New creates a new Timezone.
func New() *Timezone {
return &Timezone{
tzInfos: tzInfos,
tzAbbrInfos: tzAbbrInfos,
timezones: timezones,
}
}
// TzInfos returns the all tzInfos.
func (tz *Timezone) TzInfos() map[string]*TzInfo {
return tz.tzInfos
}
// TzAbbrInfos returns the all tzAbbrInfos.
func (tz *Timezone) TzAbbrInfos() map[string][]*TzAbbreviationInfo {
return tz.tzAbbrInfos
}
// Timezones returns the all timezones.
func (tz *Timezone) Timezones() map[string][]string {
return tz.timezones
}
// GetAllTimezones returns the timezones.
//
// Deprecated: Use Timezones.
func (tz *Timezone) GetAllTimezones() map[string][]string {
return tz.timezones
}
// GetTzAbbreviationInfo returns the slice of TzAbbreviationInfo with the given timezone abbreviation.
// Returns a ErrAmbiguousTzAbbreviations error if timezone abbreviation has more than one meaning.
func (tz *Timezone) GetTzAbbreviationInfo(abbr string) ([]*TzAbbreviationInfo, error) {
err := fmt.Errorf(errFormatInvalidTzAbbreviation, abbr)
if _, ok := tz.tzAbbrInfos[abbr]; !ok {
return nil, err
}
if len(tz.tzAbbrInfos[abbr]) > 1 {
return tz.tzAbbrInfos[abbr], ErrAmbiguousTzAbbreviations
}
return tz.tzAbbrInfos[abbr], nil
}
// GetTzAbbreviationInfoByTZName returns the TzAbbreviationInfo with the given timezone abbreviation and timezone name.
// Even if timezone abbreviation has more than one meanings, it can be identified by tzname.
func (tz *Timezone) GetTzAbbreviationInfoByTZName(abbr, tzname string) (*TzAbbreviationInfo, error) {
if _, ok := tz.tzAbbrInfos[abbr]; !ok {
return nil, fmt.Errorf(errFormatInvalidTzAbbreviation, abbr)
}
for _, tzi := range tz.tzAbbrInfos[abbr] {
names := strings.Split(tzi.Name(), "/")
if len(names) == 1 && names[0] == tzname {
return tzi, nil
}
if len(names) == 2 && (names[0] == tzname || names[1] == tzname) {
return tzi, nil
}
}
return nil, fmt.Errorf(errFormatInvalidTzName, abbr, tzname)
}
// GetTimezones returns the timezones with the given timezone abbreviation.
func (tz *Timezone) GetTimezones(abbr string) ([]string, error) {
if _, ok := tz.timezones[abbr]; !ok {
return []string{}, fmt.Errorf(errFormatInvalidTzAbbreviation, abbr)
}
return tz.timezones[abbr], nil
}
// FixedTimezone returns the time.Time with the given timezone set from the time.Location.
func (tz *Timezone) FixedTimezone(t time.Time, timezone string) (time.Time, error) {
var err error
var loc *time.Location
zone, offset := time.Now().In(time.Local).Zone()
if timezone != "" {
loc, err = time.LoadLocation(timezone)
if err != nil {
return time.Time{}, err
}
return t.In(loc), err
}
loc = time.FixedZone(zone, offset)
return t.In(loc), err
}
// GetTzInfo returns the TzInfo with the given timezone.
func (tz *Timezone) GetTzInfo(timezone string) (*TzInfo, error) {
tzInfo, ok := tz.tzInfos[timezone]
if !ok {
return nil, fmt.Errorf(errFormatInvalidTz, timezone)
}
return tzInfo, nil
}
// GetOffset returns the timezone offset with the given timezone abbreviation.
// If also given dst=true, returns the daylight savings timezone offset.
// Returns a ErrAmbiguousTzAbbreviations error if timezone abbreviation has more than one meaning.
//
// Deprecated: Use GetTzAbbreviationInfo or GetTzAbbreviationInfoByTZName
func (tz *Timezone) GetOffset(abbr string, dst ...bool) (int, error) {
tzAbbrInfos, ok := tz.tzAbbrInfos[abbr]
if !ok {
return 0, fmt.Errorf(errFormatInvalidTzAbbreviation, abbr)
}
if len(tz.tzAbbrInfos[abbr]) > 1 {
return 0, ErrAmbiguousTzAbbreviations
}
if len(dst) == 0 || !dst[0] {
return tzAbbrInfos[0].Offset(), nil
}
if dst[0] && !tzAbbrInfos[0].IsDST() {
return 0, fmt.Errorf(errFormatDoesNotHaveDST, abbr)
}
return tzAbbrInfos[0].Offset(), nil
}
// GetTimezoneAbbreviation returns the timezone abbreviation with the given timezone.
// If also given dst=true, returns the daylight savings timezone abbreviation.
func (tz *Timezone) GetTimezoneAbbreviation(timezone string, dst ...bool) (string, error) {
tzinfo, ok := tz.tzInfos[timezone]
if !ok {
return "", fmt.Errorf(errFormatInvalidTz, timezone)
}
if len(dst) == 0 || !dst[0] {
return tzinfo.ShortStandard(), nil
}
if dst[0] && !tzinfo.HasDST() {
return "", fmt.Errorf(errFormatDoesNotHaveDST, timezone)
}
return tzinfo.ShortDaylight(), nil
}
// IsDST returns whether a given time is daylight saving time or not.
func (tz *Timezone) IsDST(t time.Time) bool {
t1 := time.Date(t.Year(), time.January, 1, 0, 0, 0, 0, t.Location())
t2 := time.Date(t.Year(), time.July, 1, 0, 0, 0, 0, t.Location())
_, tOffset := t.Zone()
_, t1Offset := t1.Zone()
_, t2Offset := t2.Zone()
var dstOffset int
if t1Offset > t2Offset {
dstOffset = t1Offset
} else if t1Offset < t2Offset {
dstOffset = t2Offset
} else {
return false
}
if dstOffset == tOffset {
return true
}
return false
}