2 * Original script by Josh Fraser (http://www.onlineaspect.com)
3 * Continued by Jon Nylander, (jon at pageloom dot com)
4 * According to both of us, you are absolutely free to do whatever
5 * you want with this code.
7 * This code is maintained at bitbucket.org as jsTimezoneDetect.
10 var HEMISPHERE_SOUTH
= 'SOUTH';
11 var HEMISPHERE_NORTH
= 'NORTH';
12 var HEMISPHERE_UNKNOWN
= 'N/A';
16 * The keys in this dictionary are comma separated as such:
18 * First the offset compared to UTC time in minutes.
20 * Then a flag which is 0 if the timezone does not take daylight savings into account and 1 if it does.
22 * Thirdly an optional 's' signifies that the timezone is in the southern hemisphere, only interesting for timezones with DST.
24 * The values of the dictionary are TimeZone objects.
27 '-720,0' : new TimeZone('-12:00','Etc/GMT+12', false),
28 '-660,0' : new TimeZone('-11:00','Pacific/Pago_Pago', false),
29 '-600,1' : new TimeZone('-11:00','America/Adak',true),
30 '-660,1,s' : new TimeZone('-11:00','Pacific/Apia', true),
31 '-600,0' : new TimeZone('-10:00','Pacific/Honolulu', false),
32 '-570,0' : new TimeZone('-10:30','Pacific/Marquesas',false),
33 '-540,0' : new TimeZone('-09:00','Pacific/Gambier',false),
34 '-540,1' : new TimeZone('-09:00','America/Anchorage', true),
35 '-480,1' : new TimeZone('-08:00','America/Los_Angeles', true),
36 '-480,0' : new TimeZone('-08:00','Pacific/Pitcairn',false),
37 '-420,0' : new TimeZone('-07:00','America/Phoenix', false),
38 '-420,1' : new TimeZone('-07:00','America/Denver', true),
39 '-360,0' : new TimeZone('-06:00','America/Guatemala', false),
40 '-360,1' : new TimeZone('-06:00','America/Chicago', true),
41 '-360,1,s' : new TimeZone('-06:00','Pacific/Easter',true),
42 '-300,0' : new TimeZone('-05:00','America/Bogota', false),
43 '-300,1' : new TimeZone('-05:00','America/New_York', true),
44 '-270,0' : new TimeZone('-04:30','America/Caracas', false),
45 '-240,1' : new TimeZone('-04:00','America/Halifax', true),
46 '-240,0' : new TimeZone('-04:00','America/Santo_Domingo', false),
47 '-240,1,s' : new TimeZone('-04:00','America/Asuncion', true),
48 '-210,1' : new TimeZone('-03:30','America/St_Johns', true),
49 '-180,1' : new TimeZone('-03:00','America/Godthab', true),
50 '-180,0' : new TimeZone('-03:00','America/Argentina/Buenos_Aires', false),
51 '-180,1,s' : new TimeZone('-03:00','America/Montevideo', true),
52 '-120,0' : new TimeZone('-02:00','America/Noronha', false),
53 '-120,1' : new TimeZone('-02:00','Etc/GMT+2', true),
54 '-60,1' : new TimeZone('-01:00','Atlantic/Azores', true),
55 '-60,0' : new TimeZone('-01:00','Atlantic/Cape_Verde', false),
56 '0,0' : new TimeZone('00:00','Etc/UTC', false),
57 '0,1' : new TimeZone('00:00','Europe/London', true),
58 '60,1' : new TimeZone('+01:00','Europe/Berlin', true),
59 '60,0' : new TimeZone('+01:00','Africa/Lagos', false),
60 '60,1,s' : new TimeZone('+01:00','Africa/Windhoek',true),
61 '120,1' : new TimeZone('+02:00','Asia/Beirut', true),
62 '120,0' : new TimeZone('+02:00','Africa/Johannesburg', false),
63 '180,1' : new TimeZone('+03:00','Europe/Moscow', true),
64 '180,0' : new TimeZone('+03:00','Asia/Baghdad', false),
65 '210,1' : new TimeZone('+03:30','Asia/Tehran', true),
66 '240,0' : new TimeZone('+04:00','Asia/Dubai', false),
67 '240,1' : new TimeZone('+04:00','Asia/Yerevan', true),
68 '270,0' : new TimeZone('+04:30','Asia/Kabul', false),
69 '300,1' : new TimeZone('+05:00','Asia/Yekaterinburg', true),
70 '300,0' : new TimeZone('+05:00','Asia/Karachi', false),
71 '330,0' : new TimeZone('+05:30','Asia/Kolkata', false),
72 '345,0' : new TimeZone('+05:45','Asia/Kathmandu', false),
73 '360,0' : new TimeZone('+06:00','Asia/Dhaka', false),
74 '360,1' : new TimeZone('+06:00','Asia/Omsk', true),
75 '390,0' : new TimeZone('+06:30','Asia/Rangoon', false),
76 '420,1' : new TimeZone('+07:00','Asia/Krasnoyarsk', true),
77 '420,0' : new TimeZone('+07:00','Asia/Jakarta', false),
78 '480,0' : new TimeZone('+08:00','Asia/Shanghai', false),
79 '480,1' : new TimeZone('+08:00','Asia/Irkutsk', true),
80 '525,0' : new TimeZone('+08:45','Australia/Eucla', true),
81 '525,1,s' : new TimeZone('+08:45','Australia/Eucla', true),
82 '540,1' : new TimeZone('+09:00','Asia/Yakutsk', true),
83 '540,0' : new TimeZone('+09:00','Asia/Tokyo', false),
84 '570,0' : new TimeZone('+09:30','Australia/Darwin', false),
85 '570,1,s' : new TimeZone('+09:30','Australia/Adelaide', true),
86 '600,0' : new TimeZone('+10:00','Australia/Brisbane', false),
87 '600,1' : new TimeZone('+10:00','Asia/Vladivostok', true),
88 '600,1,s' : new TimeZone('+10:00','Australia/Sydney', true),
89 '630,1,s' : new TimeZone('+10:30','Australia/Lord_Howe', true),
90 '660,1' : new TimeZone('+11:00','Asia/Kamchatka', true),
91 '660,0' : new TimeZone('+11:00','Pacific/Noumea', false),
92 '690,0' : new TimeZone('+11:30','Pacific/Norfolk', false),
93 '720,1,s' : new TimeZone('+12:00','Pacific/Auckland', true),
94 '720,0' : new TimeZone('+12:00','Pacific/Tarawa', false),
95 '765,1,s' : new TimeZone('+12:45','Pacific/Chatham', true),
96 '780,0' : new TimeZone('+13:00','Pacific/Tongatapu', false),
97 '840,0' : new TimeZone('+14:00','Pacific/Kiritimati', false)
101 * This object contains information on when daylight savings starts for
102 * different timezones.
104 * The list is short for a reason. Often we do not have to be very specific
105 * to single out the correct timezone. But when we do, this list comes in
108 * Each value is a date denoting when daylight savings starts for that timezone.
110 olson
.dst_start_dates
= {
111 'America/Denver' : new Date(2011, 2, 13, 3, 0, 0, 0),
112 'America/Mazatlan' : new Date(2011, 3, 3, 3, 0, 0, 0),
113 'America/Chicago' : new Date(2011, 2, 13, 3, 0, 0, 0),
114 'America/Mexico_City' : new Date(2011, 3, 3, 3, 0, 0, 0),
115 'Atlantic/Stanley' : new Date(2011, 8, 4, 7, 0, 0, 0),
116 'America/Asuncion' : new Date(2011, 9, 2, 3, 0, 0, 0),
117 'America/Santiago' : new Date(2011, 9, 9, 3, 0, 0, 0),
118 'America/Campo_Grande' : new Date(2011, 9, 16, 5, 0, 0, 0),
119 'America/Montevideo' : new Date(2011, 9, 2, 3, 0, 0, 0),
120 'America/Sao_Paulo' : new Date(2011, 9, 16, 5, 0, 0, 0),
121 'America/Los_Angeles' : new Date(2011, 2, 13, 8, 0, 0, 0),
122 'America/Santa_Isabel' : new Date(2011, 3, 5, 8, 0, 0, 0),
123 'America/Havana' : new Date(2011, 2, 13, 2, 0, 0, 0),
124 'America/New_York' : new Date(2011, 2, 13, 7, 0, 0, 0),
125 'Asia/Gaza' : new Date(2011, 2, 26, 23, 0, 0, 0),
126 'Asia/Beirut' : new Date(2011, 2, 27, 1, 0, 0, 0),
127 'Europe/Minsk' : new Date(2011, 2, 27, 3, 0, 0, 0),
128 'Europe/Istanbul' : new Date(2011, 2, 27, 7, 0, 0, 0),
129 'Asia/Damascus' : new Date(2011, 3, 1, 2, 0, 0, 0),
130 'Asia/Jerusalem' : new Date(2011, 3, 1, 6, 0, 0, 0),
131 'Africa/Cairo' : new Date(2011, 3, 29, 4, 0, 0, 0),
132 'Asia/Yerevan' : new Date(2011, 2, 27, 4, 0, 0, 0),
133 'Asia/Baku' : new Date(2011, 2, 27, 8, 0, 0, 0),
134 'Pacific/Auckland' : new Date(2011, 8, 26, 7, 0, 0, 0),
135 'Pacific/Fiji' : new Date(2010, 11, 29, 23, 0, 0, 0),
136 'America/Halifax' : new Date(2011, 2, 13, 6, 0, 0, 0),
137 'America/Goose_Bay' : new Date(2011, 2, 13, 2, 1, 0, 0),
138 'America/Miquelon' : new Date(2011, 2, 13, 5, 0, 0, 0),
139 'America/Godthab' : new Date(2011, 2, 27, 1, 0, 0, 0)
143 * The keys in this object are timezones that we know may be ambiguous after
144 * a preliminary scan through the olson_tz object.
146 * The array of timezones to compare must be in the order that daylight savings
147 * starts for the regions.
149 olson
.ambiguity_list
= {
150 'America/Denver' : ['America/Denver','America/Mazatlan'],
151 'America/Chicago' : ['America/Chicago','America/Mexico_City'],
152 'America/Asuncion' : ['Atlantic/Stanley', 'America/Asuncion', 'America/Santiago','America/Campo_Grande'],
153 'America/Montevideo' : ['America/Montevideo', 'America/Sao_Paulo'],
154 'Asia/Beirut' : ['Asia/Gaza','Asia/Beirut', 'Europe/Minsk', 'Europe/Istanbul', 'Asia/Damascus', 'Asia/Jerusalem','Africa/Cairo'],
155 'Asia/Yerevan' : ['Asia/Yerevan', 'Asia/Baku'],
156 'Pacific/Auckland' : ['Pacific/Auckland', 'Pacific/Fiji'],
157 'America/Los_Angeles' : ['America/Los_Angeles', 'America/Santa_Isabel'],
158 'America/New_York' : ['America/Havana','America/New_York'],
159 'America/Halifax' : ['America/Goose_Bay','America/Halifax'],
160 'America/Godthab' : ['America/Miquelon', 'America/Godthab']
165 * A simple object containing information of utc_offset, which olson timezone key to use,
166 * and if the timezone cares about daylight savings or not.
169 * @param {string} offset - for example '-11:00'
170 * @param {string} olson_tz - the olson Identifier, such as "America/Denver"
171 * @param {boolean} uses_dst - flag for whether the time zone somehow cares about daylight savings.
173 function TimeZone(offset
, olson_tz
, uses_dst
) {
174 this.utc_offset
= offset
;
175 this.olson_tz
= olson_tz
;
176 this.uses_dst
= uses_dst
;
180 * Prints out the result.
181 * But before it does that, it calls this.ambiguity_check.
183 TimeZone
.prototype.display = function() {
184 this.ambiguity_check();
185 var response_text
= '<b>UTC-offset</b>: ' + this.utc_offset
+ '<br/>';
186 response_text
+= '<b>Zoneinfo key</b>: ' + this.olson_tz
+ '<br/>';
187 response_text
+= '<b>Zone uses DST</b>: ' + (this.uses_dst
? 'yes' : 'no') + '<br/>';
189 return response_text
;
193 * Checks if a timezone has possible ambiguities. I.e timezones that are similar.
195 * If the preliminary scan determines that we're in America/Denver. We double check
196 * here that we're really there and not in America/Mazatlan.
198 * This is done by checking known dates for when daylight savings start for different
201 TimeZone
.prototype.ambiguity_check = function() {
202 var local_ambiguity_list
= olson
.ambiguity_list
[this.olson_tz
];
204 if (typeof(local_ambiguity_list
) == 'undefined') {
208 var length
= local_ambiguity_list
.length
;
210 for (var i
= 0; i
< length
; i
++) {
211 var tz
= local_ambiguity_list
[i
]
213 if (date_is_dst(olson
.dst_start_dates
[tz
])) {
221 * Checks whether a given date is in daylight savings time.
223 * If the date supplied is after june, we assume that we're checking
224 * for southern hemisphere DST.
229 function date_is_dst(date
) {
230 var base_offset
= ( (date
.getMonth() > 5 ? get_june_offset() : get_january_offset()) )
232 var date_offset
= get_date_offset(date
);
234 return (base_offset
- date_offset
) != 0;
238 * Gets the offset in minutes from UTC for a certain date.
243 function get_date_offset(date
) {
244 return -date
.getTimezoneOffset();
248 * This function does some basic calculations to create information about
249 * the user's timezone.
251 * Returns a primitive object on the format
252 * {'utc_offset' : -9, 'dst': 1, hemisphere' : 'north'}
253 * where dst is 1 if the region uses daylight savings.
257 function get_timezone_info() {
258 var january_offset
= get_january_offset();
260 var june_offset
= get_june_offset();
262 var diff
= january_offset
- june_offset
;
265 return {'utc_offset' : january_offset
,
267 'hemisphere' : HEMISPHERE_NORTH
}
270 return {'utc_offset' : june_offset
,
272 'hemisphere' : HEMISPHERE_SOUTH
}
275 return {'utc_offset' : january_offset
,
277 'hemisphere' : HEMISPHERE_UNKNOWN
}
280 function get_january_offset() {
281 return get_date_offset(new Date(2011, 0, 1, 0, 0, 0, 0));
284 function get_june_offset() {
285 return get_date_offset(new Date(2011, 5, 1, 0, 0, 0, 0));
289 * Uses get_timezone_info() to formulate a key to use in the olson.timezones dictionary.
291 * Returns a primitive object on the format:
292 * {'timezone': TimeZone, 'key' : 'the key used to find the TimeZone object'}
296 function determine_timezone() {
297 var timezone_key_info
= get_timezone_info();
299 var hemisphere_suffix
= ''
301 if (timezone_key_info
.hemisphere
== HEMISPHERE_SOUTH
) {
302 hemisphere_suffix
= ',s';
305 var tz_key
= timezone_key_info
.utc_offset
+ ',' + timezone_key_info
.dst
+ hemisphere_suffix
307 return {'timezone' : olson
.timezones
[tz_key
], 'key' : tz_key
}