jstz.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /**
  2. * This script gives you the zone info key representing your device's time zone setting.
  3. *
  4. * @name jsTimezoneDetect
  5. * @version 1.0.5
  6. * @author Jon Nylander
  7. * @license MIT License - http://www.opensource.org/licenses/mit-license.php
  8. *
  9. * For usage and examples, visit:
  10. * http://pellepim.bitbucket.org/jstz/
  11. *
  12. * Copyright (c) Jon Nylander
  13. */
  14. /*jslint undef: true */
  15. /*global console, exports*/
  16. (function(root) {
  17. /**
  18. * Namespace to hold all the code for timezone detection.
  19. */
  20. var jstz = (function () {
  21. 'use strict';
  22. var HEMISPHERE_SOUTH = 's',
  23. /**
  24. * Gets the offset in minutes from UTC for a certain date.
  25. * @param {Date} date
  26. * @returns {Number}
  27. */
  28. get_date_offset = function (date) {
  29. var offset = -date.getTimezoneOffset();
  30. return (offset !== null ? offset : 0);
  31. },
  32. get_date = function (year, month, date) {
  33. var d = new Date();
  34. if (year !== undefined) {
  35. d.setFullYear(year);
  36. }
  37. d.setMonth(month);
  38. d.setDate(date);
  39. return d;
  40. },
  41. get_january_offset = function (year) {
  42. return get_date_offset(get_date(year, 0 ,2));
  43. },
  44. get_june_offset = function (year) {
  45. return get_date_offset(get_date(year, 5, 2));
  46. },
  47. /**
  48. * Private method.
  49. * Checks whether a given date is in daylight saving time.
  50. * If the date supplied is after august, we assume that we're checking
  51. * for southern hemisphere DST.
  52. * @param {Date} date
  53. * @returns {Boolean}
  54. */
  55. date_is_dst = function (date) {
  56. var is_southern = date.getMonth() > 7,
  57. base_offset = is_southern ? get_june_offset(date.getFullYear()) :
  58. get_january_offset(date.getFullYear()),
  59. date_offset = get_date_offset(date),
  60. is_west = base_offset < 0,
  61. dst_offset = base_offset - date_offset;
  62. if (!is_west && !is_southern) {
  63. return dst_offset < 0;
  64. }
  65. return dst_offset !== 0;
  66. },
  67. /**
  68. * This function does some basic calculations to create information about
  69. * the user's timezone. It uses REFERENCE_YEAR as a solid year for which
  70. * the script has been tested rather than depend on the year set by the
  71. * client device.
  72. *
  73. * Returns a key that can be used to do lookups in jstz.olson.timezones.
  74. * eg: "720,1,2".
  75. *
  76. * @returns {String}
  77. */
  78. lookup_key = function () {
  79. var january_offset = get_january_offset(),
  80. june_offset = get_june_offset(),
  81. diff = january_offset - june_offset;
  82. if (diff < 0) {
  83. return january_offset + ",1";
  84. } else if (diff > 0) {
  85. return june_offset + ",1," + HEMISPHERE_SOUTH;
  86. }
  87. return january_offset + ",0";
  88. },
  89. /**
  90. * Uses get_timezone_info() to formulate a key to use in the olson.timezones dictionary.
  91. *
  92. * Returns a primitive object on the format:
  93. * {'timezone': TimeZone, 'key' : 'the key used to find the TimeZone object'}
  94. *
  95. * @returns Object
  96. */
  97. determine = function () {
  98. var key = lookup_key();
  99. return new jstz.TimeZone(jstz.olson.timezones[key]);
  100. },
  101. /**
  102. * This object contains information on when daylight savings starts for
  103. * different timezones.
  104. *
  105. * The list is short for a reason. Often we do not have to be very specific
  106. * to single out the correct timezone. But when we do, this list comes in
  107. * handy.
  108. *
  109. * Each value is a date denoting when daylight savings starts for that timezone.
  110. */
  111. dst_start_for = function (tz_name) {
  112. var ru_pre_dst_change = new Date(2010, 6, 15, 1, 0, 0, 0), // In 2010 Russia had DST, this allows us to detect Russia :)
  113. dst_starts = {
  114. 'America/Denver': new Date(2011, 2, 13, 3, 0, 0, 0),
  115. 'America/Mazatlan': new Date(2011, 3, 3, 3, 0, 0, 0),
  116. 'America/Chicago': new Date(2011, 2, 13, 3, 0, 0, 0),
  117. 'America/Mexico_City': new Date(2011, 3, 3, 3, 0, 0, 0),
  118. 'America/Asuncion': new Date(2012, 9, 7, 3, 0, 0, 0),
  119. 'America/Santiago': new Date(2012, 9, 3, 3, 0, 0, 0),
  120. 'America/Campo_Grande': new Date(2012, 9, 21, 5, 0, 0, 0),
  121. 'America/Montevideo': new Date(2011, 9, 2, 3, 0, 0, 0),
  122. 'America/Sao_Paulo': new Date(2011, 9, 16, 5, 0, 0, 0),
  123. 'America/Los_Angeles': new Date(2011, 2, 13, 8, 0, 0, 0),
  124. 'America/Santa_Isabel': new Date(2011, 3, 5, 8, 0, 0, 0),
  125. 'America/Havana': new Date(2012, 2, 10, 2, 0, 0, 0),
  126. 'America/New_York': new Date(2012, 2, 10, 7, 0, 0, 0),
  127. 'Europe/Helsinki': new Date(2013, 2, 31, 5, 0, 0, 0),
  128. 'Pacific/Auckland': new Date(2011, 8, 26, 7, 0, 0, 0),
  129. 'America/Halifax': new Date(2011, 2, 13, 6, 0, 0, 0),
  130. 'America/Goose_Bay': new Date(2011, 2, 13, 2, 1, 0, 0),
  131. 'America/Miquelon': new Date(2011, 2, 13, 5, 0, 0, 0),
  132. 'America/Godthab': new Date(2011, 2, 27, 1, 0, 0, 0),
  133. 'Europe/Moscow': ru_pre_dst_change,
  134. 'Asia/Amman': new Date(2013, 2, 29, 1, 0, 0, 0),
  135. 'Asia/Beirut': new Date(2013, 2, 31, 2, 0, 0, 0),
  136. 'Asia/Damascus': new Date(2013, 3, 6, 2, 0, 0, 0),
  137. 'Asia/Jerusalem': new Date(2013, 2, 29, 5, 0, 0, 0),
  138. 'Asia/Yekaterinburg': ru_pre_dst_change,
  139. 'Asia/Omsk': ru_pre_dst_change,
  140. 'Asia/Krasnoyarsk': ru_pre_dst_change,
  141. 'Asia/Irkutsk': ru_pre_dst_change,
  142. 'Asia/Yakutsk': ru_pre_dst_change,
  143. 'Asia/Vladivostok': ru_pre_dst_change,
  144. 'Asia/Baku': new Date(2013, 2, 31, 4, 0, 0),
  145. 'Asia/Yerevan': new Date(2013, 2, 31, 3, 0, 0),
  146. 'Asia/Kamchatka': ru_pre_dst_change,
  147. 'Asia/Gaza': new Date(2010, 2, 27, 4, 0, 0),
  148. 'Africa/Cairo': new Date(2010, 4, 1, 3, 0, 0),
  149. 'Europe/Minsk': ru_pre_dst_change,
  150. 'Pacific/Apia': new Date(2010, 10, 1, 1, 0, 0, 0),
  151. 'Pacific/Fiji': new Date(2010, 11, 1, 0, 0, 0),
  152. 'Australia/Perth': new Date(2008, 10, 1, 1, 0, 0, 0)
  153. };
  154. return dst_starts[tz_name];
  155. };
  156. return {
  157. determine: determine,
  158. date_is_dst: date_is_dst,
  159. dst_start_for: dst_start_for
  160. };
  161. }());
  162. /**
  163. * Simple object to perform ambiguity check and to return name of time zone.
  164. */
  165. jstz.TimeZone = function (tz_name) {
  166. 'use strict';
  167. /**
  168. * The keys in this object are timezones that we know may be ambiguous after
  169. * a preliminary scan through the olson_tz object.
  170. *
  171. * The array of timezones to compare must be in the order that daylight savings
  172. * starts for the regions.
  173. */
  174. var AMBIGUITIES = {
  175. 'America/Denver': ['America/Denver', 'America/Mazatlan'],
  176. 'America/Chicago': ['America/Chicago', 'America/Mexico_City'],
  177. 'America/Santiago': ['America/Santiago', 'America/Asuncion', 'America/Campo_Grande'],
  178. 'America/Montevideo': ['America/Montevideo', 'America/Sao_Paulo'],
  179. 'Asia/Beirut': ['Asia/Amman', 'Asia/Jerusalem', 'Asia/Beirut', 'Europe/Helsinki','Asia/Damascus'],
  180. 'Pacific/Auckland': ['Pacific/Auckland', 'Pacific/Fiji'],
  181. 'America/Los_Angeles': ['America/Los_Angeles', 'America/Santa_Isabel'],
  182. 'America/New_York': ['America/Havana', 'America/New_York'],
  183. 'America/Halifax': ['America/Goose_Bay', 'America/Halifax'],
  184. 'America/Godthab': ['America/Miquelon', 'America/Godthab'],
  185. 'Asia/Dubai': ['Europe/Moscow'],
  186. 'Asia/Dhaka': ['Asia/Yekaterinburg'],
  187. 'Asia/Jakarta': ['Asia/Omsk'],
  188. 'Asia/Shanghai': ['Asia/Krasnoyarsk', 'Australia/Perth'],
  189. 'Asia/Tokyo': ['Asia/Irkutsk'],
  190. 'Australia/Brisbane': ['Asia/Yakutsk'],
  191. 'Pacific/Noumea': ['Asia/Vladivostok'],
  192. 'Pacific/Tarawa': ['Asia/Kamchatka', 'Pacific/Fiji'],
  193. 'Pacific/Tongatapu': ['Pacific/Apia'],
  194. 'Asia/Baghdad': ['Europe/Minsk'],
  195. 'Asia/Baku': ['Asia/Yerevan','Asia/Baku'],
  196. 'Africa/Johannesburg': ['Asia/Gaza', 'Africa/Cairo']
  197. },
  198. timezone_name = tz_name,
  199. /**
  200. * Checks if a timezone has possible ambiguities. I.e timezones that are similar.
  201. *
  202. * For example, if the preliminary scan determines that we're in America/Denver.
  203. * We double check here that we're really there and not in America/Mazatlan.
  204. *
  205. * This is done by checking known dates for when daylight savings start for different
  206. * timezones during 2010 and 2011.
  207. */
  208. ambiguity_check = function () {
  209. var ambiguity_list = AMBIGUITIES[timezone_name],
  210. length = ambiguity_list.length,
  211. i = 0,
  212. tz = ambiguity_list[0];
  213. for (; i < length; i += 1) {
  214. tz = ambiguity_list[i];
  215. if (jstz.date_is_dst(jstz.dst_start_for(tz))) {
  216. timezone_name = tz;
  217. return;
  218. }
  219. }
  220. },
  221. /**
  222. * Checks if it is possible that the timezone is ambiguous.
  223. */
  224. is_ambiguous = function () {
  225. return typeof (AMBIGUITIES[timezone_name]) !== 'undefined';
  226. };
  227. if (is_ambiguous()) {
  228. ambiguity_check();
  229. }
  230. return {
  231. name: function () {
  232. return timezone_name;
  233. }
  234. };
  235. };
  236. jstz.olson = {};
  237. /*
  238. * The keys in this dictionary are comma separated as such:
  239. *
  240. * First the offset compared to UTC time in minutes.
  241. *
  242. * Then a flag which is 0 if the timezone does not take daylight savings into account and 1 if it
  243. * does.
  244. *
  245. * Thirdly an optional 's' signifies that the timezone is in the southern hemisphere,
  246. * only interesting for timezones with DST.
  247. *
  248. * The mapped arrays is used for constructing the jstz.TimeZone object from within
  249. * jstz.determine_timezone();
  250. */
  251. jstz.olson.timezones = {
  252. '-720,0' : 'Pacific/Majuro',
  253. '-660,0' : 'Pacific/Pago_Pago',
  254. '-600,1' : 'America/Adak',
  255. '-600,0' : 'Pacific/Honolulu',
  256. '-570,0' : 'Pacific/Marquesas',
  257. '-540,0' : 'Pacific/Gambier',
  258. '-540,1' : 'America/Anchorage',
  259. '-480,1' : 'America/Los_Angeles',
  260. '-480,0' : 'Pacific/Pitcairn',
  261. '-420,0' : 'America/Phoenix',
  262. '-420,1' : 'America/Denver',
  263. '-360,0' : 'America/Guatemala',
  264. '-360,1' : 'America/Chicago',
  265. '-360,1,s' : 'Pacific/Easter',
  266. '-300,0' : 'America/Bogota',
  267. '-300,1' : 'America/New_York',
  268. '-270,0' : 'America/Caracas',
  269. '-240,1' : 'America/Halifax',
  270. '-240,0' : 'America/Santo_Domingo',
  271. '-240,1,s' : 'America/Santiago',
  272. '-210,1' : 'America/St_Johns',
  273. '-180,1' : 'America/Godthab',
  274. '-180,0' : 'America/Argentina/Buenos_Aires',
  275. '-180,1,s' : 'America/Montevideo',
  276. '-120,0' : 'America/Noronha',
  277. '-120,1' : 'America/Noronha',
  278. '-60,1' : 'Atlantic/Azores',
  279. '-60,0' : 'Atlantic/Cape_Verde',
  280. '0,0' : 'Etc/UTC',
  281. '0,1' : 'Europe/London',
  282. '60,1' : 'Europe/Berlin',
  283. '60,0' : 'Africa/Lagos',
  284. '60,1,s' : 'Africa/Windhoek',
  285. '120,1' : 'Asia/Beirut',
  286. '120,0' : 'Africa/Johannesburg',
  287. '180,0' : 'Asia/Baghdad',
  288. '180,1' : 'Europe/Moscow',
  289. '210,1' : 'Asia/Tehran',
  290. '240,0' : 'Asia/Dubai',
  291. '240,1' : 'Asia/Baku',
  292. '270,0' : 'Asia/Kabul',
  293. '300,1' : 'Asia/Yekaterinburg',
  294. '300,0' : 'Asia/Karachi',
  295. '330,0' : 'Asia/Kolkata',
  296. '345,0' : 'Asia/Kathmandu',
  297. '360,0' : 'Asia/Dhaka',
  298. '360,1' : 'Asia/Omsk',
  299. '390,0' : 'Asia/Rangoon',
  300. '420,1' : 'Asia/Krasnoyarsk',
  301. '420,0' : 'Asia/Jakarta',
  302. '480,0' : 'Asia/Shanghai',
  303. '480,1' : 'Asia/Irkutsk',
  304. '525,0' : 'Australia/Eucla',
  305. '525,1,s' : 'Australia/Eucla',
  306. '540,1' : 'Asia/Yakutsk',
  307. '540,0' : 'Asia/Tokyo',
  308. '570,0' : 'Australia/Darwin',
  309. '570,1,s' : 'Australia/Adelaide',
  310. '600,0' : 'Australia/Brisbane',
  311. '600,1' : 'Asia/Vladivostok',
  312. '600,1,s' : 'Australia/Sydney',
  313. '630,1,s' : 'Australia/Lord_Howe',
  314. '660,1' : 'Asia/Kamchatka',
  315. '660,0' : 'Pacific/Noumea',
  316. '690,0' : 'Pacific/Norfolk',
  317. '720,1,s' : 'Pacific/Auckland',
  318. '720,0' : 'Pacific/Tarawa',
  319. '765,1,s' : 'Pacific/Chatham',
  320. '780,0' : 'Pacific/Tongatapu',
  321. '780,1,s' : 'Pacific/Apia',
  322. '840,0' : 'Pacific/Kiritimati'
  323. };
  324. if (typeof exports !== 'undefined') {
  325. exports.jstz = jstz;
  326. } else {
  327. root.jstz = jstz;
  328. }
  329. })(this);