Skip to content

Instantly share code, notes, and snippets.

@kimsterv
Created August 9, 2012 18:11
Show Gist options
  • Save kimsterv/3306655 to your computer and use it in GitHub Desktop.
Save kimsterv/3306655 to your computer and use it in GitHub Desktop.
timezone detection
/*
* Original script by Josh Fraser (http://www.onlineaspect.com)
* Continued and maintained by Jon Nylander at https://bitbucket.org/pellepim/jstimezonedetect
*
* Provided under the Do Whatever You Want With This Code License.
*/
/**
* Namespace to hold all the code for timezone detection.
*/
var jstz = (function () {
'use strict';
var HEMISPHERE_SOUTH = 's',
/**
* Gets the offset in minutes from UTC for a certain date.
* @param {Date} date
* @returns {Number}
*/
get_date_offset = function (date) {
var offset = -date.getTimezoneOffset();
return (offset !== null ? offset : 0);
},
get_january_offset = function () {
return get_date_offset(new Date(2010, 0, 1, 0, 0, 0, 0));
},
get_june_offset = function () {
return get_date_offset(new Date(2010, 5, 1, 0, 0, 0, 0));
},
/**
* Private method.
* Checks whether a given date is in daylight savings time.
* If the date supplied is after june, we assume that we're checking
* for southern hemisphere DST.
* @param {Date} date
* @returns {Boolean}
*/
date_is_dst = function (date) {
var base_offset = ((date.getMonth() > 5 ? get_june_offset()
: get_january_offset())),
date_offset = get_date_offset(date);
return (base_offset - date_offset) !== 0;
},
/**
* This function does some basic calculations to create information about
* the user's timezone.
*
* Returns a key that can be used to do lookups in jstz.olson.timezones.
*
* @returns {String}
*/
lookup_key = function () {
var january_offset = get_january_offset(),
june_offset = get_june_offset(),
diff = get_january_offset() - get_june_offset();
if (diff < 0) {
return january_offset + ",1";
} else if (diff > 0) {
return june_offset + ",1," + HEMISPHERE_SOUTH;
}
return january_offset + ",0";
},
/**
* Uses get_timezone_info() to formulate a key to use in the olson.timezones dictionary.
*
* Returns a primitive object on the format:
* {'timezone': TimeZone, 'key' : 'the key used to find the TimeZone object'}
*
* @returns Object
*/
determine_timezone = function () {
var key = lookup_key();
return new jstz.TimeZone(jstz.olson.timezones[key]);
};
return {
determine_timezone : determine_timezone,
date_is_dst : date_is_dst
};
}());
/**
* A simple object containing information of utc_offset, which olson timezone key to use,
* and if the timezone cares about daylight savings or not.
*
* @constructor
* @param {string} offset - for example '-11:00'
* @param {string} olson_tz - the olson Identifier, such as "America/Denver"
* @param {boolean} uses_dst - flag for whether the time zone somehow cares about daylight savings.
*/
jstz.TimeZone = (function () {
'use strict';
var timezone_name = null,
uses_dst = null,
utc_offset = null,
name = function () {
return timezone_name;
},
dst = function () {
return uses_dst;
},
offset = function () {
return utc_offset;
},
/**
* Checks if a timezone has possible ambiguities. I.e timezones that are similar.
*
* If the preliminary scan determines that we're in America/Denver. We double check
* here that we're really there and not in America/Mazatlan.
*
* This is done by checking known dates for when daylight savings start for different
* timezones.
*/
ambiguity_check = function () {
var ambiguity_list = jstz.olson.ambiguity_list[timezone_name],
length = ambiguity_list.length,
i = 0,
tz = ambiguity_list[0];
for (; i < length; i += 1) {
tz = ambiguity_list[i];
if (jstz.date_is_dst(jstz.olson.dst_start_dates[tz])) {
timezone_name = tz;
return;
}
}
},
/**
* Checks if it is possible that the timezone is ambiguous.
*/
is_ambiguous = function () {
return typeof (jstz.olson.ambiguity_list[timezone_name]) !== 'undefined';
},
/**
* Constructor for jstz.TimeZone
*/
Constr = function (tz_info) {
utc_offset = tz_info[0];
timezone_name = tz_info[1];
uses_dst = tz_info[2];
if (is_ambiguous()) {
ambiguity_check();
}
};
/**
* Public API for jstz.TimeZone
*/
Constr.prototype = {
constructor : jstz.TimeZone,
name : name,
dst : dst,
offset : offset
};
return Constr;
}());
jstz.olson = {};
/*
* The keys in this dictionary are comma separated as such:
*
* First the offset compared to UTC time in minutes.
*
* Then a flag which is 0 if the timezone does not take daylight savings into account and 1 if it
* does.
*
* Thirdly an optional 's' signifies that the timezone is in the southern hemisphere,
* only interesting for timezones with DST.
*
* The mapped arrays is used for constructing the jstz.TimeZone object from within
* jstz.determine_timezone();
*/
jstz.olson.timezones = (function () {
"use strict";
return {
'-720,0' : ['-12:00', 'Etc/GMT+12', false],
'-660,0' : ['-11:00', 'Pacific/Pago_Pago', false],
'-600,1' : ['-11:00', 'America/Adak', true],
'-600,0' : ['-10:00', 'US/Hawaii', false],
'-570,0' : ['-09:30', 'Pacific/Marquesas', false],
'-540,0' : ['-09:00', 'Pacific/Gambier', false],
'-540,1' : ['-09:00', 'US/Alaska', true],
'-480,1' : ['-08:00', 'US/Pacific', true],
'-480,0' : ['-08:00', 'Pacific/Pitcairn', false],
'-420,0' : ['-07:00', 'US/Arizona', false],
'-420,1' : ['-07:00', 'US/Mountain', true],
'-360,0' : ['-06:00', 'America/Guatemala', false],
'-360,1' : ['-06:00', 'US/Central', true],
'-360,1,s' : ['-06:00', 'Pacific/Easter', true],
'-300,0' : ['-05:00', 'America/Bogota', false],
'-300,1' : ['-05:00', 'US/Eastern', true],
'-270,0' : ['-04:30', 'America/Caracas', false],
'-240,1' : ['-04:00', 'America/Halifax', true],
'-240,0' : ['-04:00', 'America/Santo_Domingo', false],
'-240,1,s' : ['-04:00', 'America/Asuncion', true],
'-210,1' : ['-03:30', 'America/St_Johns', true],
'-180,1' : ['-03:00', 'America/Godthab', true],
'-180,0' : ['-03:00', 'America/Argentina/Buenos_Aires', false],
'-180,1,s' : ['-03:00', 'America/Montevideo', true],
'-120,0' : ['-02:00', 'America/Noronha', false],
'-120,1' : ['-02:00', 'Etc/GMT+2', true],
'-60,1' : ['-01:00', 'Atlantic/Azores', true],
'-60,0' : ['-01:00', 'Atlantic/Cape_Verde', false],
'0,0' : ['00:00', 'Etc/UTC', false],
'0,1' : ['00:00', 'Europe/London', true],
'60,1' : ['+01:00', 'Europe/Berlin', true],
'60,0' : ['+01:00', 'Africa/Lagos', false],
'60,1,s' : ['+01:00', 'Africa/Windhoek', true],
'120,1' : ['+02:00', 'Asia/Beirut', true],
'120,0' : ['+02:00', 'Africa/Johannesburg', false],
'180,1' : ['+03:00', 'Europe/Moscow', true],
'180,0' : ['+03:00', 'Asia/Baghdad', false],
'210,1' : ['+03:30', 'Asia/Tehran', true],
'240,0' : ['+04:00', 'Asia/Dubai', false],
'240,1' : ['+04:00', 'Asia/Yerevan', true],
'270,0' : ['+04:30', 'Asia/Kabul', false],
'300,1' : ['+05:00', 'Asia/Yekaterinburg', true],
'300,0' : ['+05:00', 'Asia/Karachi', false],
'330,0' : ['+05:30', 'Asia/Kolkata', false],
'345,0' : ['+05:45', 'Asia/Kathmandu', false],
'360,0' : ['+06:00', 'Asia/Dhaka', false],
'360,1' : ['+06:00', 'Asia/Omsk', true],
'390,0' : ['+06:30', 'Asia/Rangoon', false],
'420,1' : ['+07:00', 'Asia/Krasnoyarsk', true],
'420,0' : ['+07:00', 'Asia/Jakarta', false],
'480,0' : ['+08:00', 'Asia/Shanghai', false],
'480,1' : ['+08:00', 'Asia/Irkutsk', true],
'525,0' : ['+08:45', 'Australia/Eucla', true],
'525,1,s' : ['+08:45', 'Australia/Eucla', true],
'540,1' : ['+09:00', 'Asia/Yakutsk', true],
'540,0' : ['+09:00', 'Asia/Tokyo', false],
'570,0' : ['+09:30', 'Australia/Darwin', false],
'570,1,s' : ['+09:30', 'Australia/Adelaide', true],
'600,0' : ['+10:00', 'Australia/Brisbane', false],
'600,1' : ['+10:00', 'Asia/Vladivostok', true],
'600,1,s' : ['+10:00', 'Australia/Sydney', true],
'630,1,s' : ['+10:30', 'Australia/Lord_Howe', true],
'660,1' : ['+11:00', 'Asia/Kamchatka', true],
'660,0' : ['+11:00', 'Pacific/Noumea', false],
'690,0' : ['+11:30', 'Pacific/Norfolk', false],
'720,1,s' : ['+12:00', 'Pacific/Auckland', true],
'720,0' : ['+12:00', 'Pacific/Tarawa', false],
'765,1,s' : ['+12:45', 'Pacific/Chatham', true],
'780,0' : ['+13:00', 'Pacific/Tongatapu', false],
'780,1,s' : ['+13:00', 'Pacific/Apia', true],
'840,0' : ['+14:00', 'Pacific/Kiritimati', false]
};
}());
/**
* This object contains information on when daylight savings starts for
* different timezones.
*
* The list is short for a reason. Often we do not have to be very specific
* to single out the correct timezone. But when we do, this list comes in
* handy.
*
* Each value is a date denoting when daylight savings starts for that timezone.
*/
jstz.olson.dst_start_dates = (function () {
"use strict";
return {
'US/Mountain' : new Date(2011, 2, 13, 3, 0, 0, 0),
'America/Mazatlan' : new Date(2011, 3, 3, 3, 0, 0, 0),
'US/Central' : new Date(2011, 2, 13, 3, 0, 0, 0),
'America/Mexico_City' : new Date(2011, 3, 3, 3, 0, 0, 0),
'Atlantic/Stanley' : new Date(2011, 8, 4, 7, 0, 0, 0),
'America/Asuncion' : new Date(2011, 9, 2, 3, 0, 0, 0),
'America/Santiago' : new Date(2011, 9, 9, 3, 0, 0, 0),
'America/Campo_Grande' : new Date(2011, 9, 16, 5, 0, 0, 0),
'America/Montevideo' : new Date(2011, 9, 2, 3, 0, 0, 0),
'America/Sao_Paulo' : new Date(2011, 9, 16, 5, 0, 0, 0),
'US/Pacific' : new Date(2011, 2, 13, 8, 0, 0, 0),
'America/Santa_Isabel' : new Date(2011, 3, 5, 8, 0, 0, 0),
'America/Havana' : new Date(2011, 2, 13, 2, 0, 0, 0),
'US/Eastern' : new Date(2011, 2, 13, 7, 0, 0, 0),
'Asia/Gaza' : new Date(2011, 2, 26, 23, 0, 0, 0),
'Asia/Beirut' : new Date(2011, 2, 27, 1, 0, 0, 0),
'Europe/Minsk' : new Date(2011, 2, 27, 2, 0, 0, 0),
'Europe/Helsinki' : new Date(2011, 2, 27, 4, 0, 0, 0),
'Europe/Istanbul' : new Date(2011, 2, 28, 5, 0, 0, 0),
'Asia/Damascus' : new Date(2011, 3, 1, 2, 0, 0, 0),
'Asia/Jerusalem' : new Date(2011, 3, 1, 6, 0, 0, 0),
'Africa/Cairo' : new Date(2010, 3, 30, 4, 0, 0, 0),
'Asia/Yerevan' : new Date(2011, 2, 27, 4, 0, 0, 0),
'Asia/Baku' : new Date(2011, 2, 27, 8, 0, 0, 0),
'Pacific/Auckland' : new Date(2011, 8, 26, 7, 0, 0, 0),
'Pacific/Fiji' : new Date(2010, 11, 29, 23, 0, 0, 0),
'America/Halifax' : new Date(2011, 2, 13, 6, 0, 0, 0),
'America/Goose_Bay' : new Date(2011, 2, 13, 2, 1, 0, 0),
'America/Miquelon' : new Date(2011, 2, 13, 5, 0, 0, 0),
'America/Godthab' : new Date(2011, 2, 27, 1, 0, 0, 0)
};
}());
/**
* The keys in this object are timezones that we know may be ambiguous after
* a preliminary scan through the olson_tz object.
*
* The array of timezones to compare must be in the order that daylight savings
* starts for the regions.
*/
jstz.olson.ambiguity_list = {
'US/Mountain' : ['US/Mountain', 'America/Mazatlan'],
'US/Central' : ['US/Central', 'America/Mexico_City'],
'America/Asuncion' : ['Atlantic/Stanley', 'America/Asuncion', 'America/Santiago', 'America/Campo_Grande'],
'America/Montevideo' : ['America/Montevideo', 'America/Sao_Paulo'],
'Asia/Beirut' : ['Asia/Gaza', 'Asia/Beirut', 'Europe/Minsk', 'Europe/Helsinki', 'Europe/Istanbul', 'Asia/Damascus', 'Asia/Jerusalem', 'Africa/Cairo'],
'Asia/Yerevan' : ['Asia/Yerevan', 'Asia/Baku'],
'Pacific/Auckland' : ['Pacific/Auckland', 'Pacific/Fiji'],
'US/Pacific' : ['US/Pacific', 'America/Santa_Isabel'],
'US/Eastern' : ['America/Havana', 'US/Eastern'],
'America/Halifax' : ['America/Goose_Bay', 'America/Halifax'],
'America/Godthab' : ['America/Miquelon', 'America/Godthab']
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment