timezone.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. /******************************************************************************
  2. * Dojo port of fleegix date plugin from
  3. *
  4. * http://js.fleegix.org/plugins/date/date
  5. *
  6. * contributed to Dojo under CLA, with thanks to Matthew Eernisse (mde@fleegix.org)
  7. * and Open Source Applications Foundation
  8. *
  9. * Credits: Ideas included from incomplete JS implementation of Olson
  10. * parser, "XMLDate" by Philippe Goetz (philippe.goetz@wanadoo.fr)
  11. *****************************************************************************/
  12. define("dojox/date/timezone", ["dojo", "dojo/date", "dojo/date/locale", "dojo/_base/array", "dojo/_base/xhr"],
  13. function(dojo, _dd, _ddl){
  14. dojo.experimental("dojox.date.timezone");
  15. dojo.getObject("date.timezone", true, dojox);
  16. var cfg = dojo.config;
  17. var _zoneFiles = [ "africa", "antarctica", "asia", "australasia", "backward",
  18. "etcetera", "europe", "northamerica", "pacificnew",
  19. "southamerica" ];
  20. // Our mins an maxes for years that we care about
  21. var _minYear = 1835,
  22. _maxYear = 2038;
  23. var _loadedZones = {},
  24. _zones = {},
  25. _loadedRanges = {},
  26. _rules = {};
  27. // timezoneFileBasePath: String
  28. // A different location to pull zone files from
  29. var timezoneFileBasePath = cfg.timezoneFileBasePath ||
  30. dojo.moduleUrl("dojox.date", "zoneinfo");
  31. // loadingScheme: String
  32. // One of "preloadAll", "lazyLoad" (Defaults "lazyLoad")
  33. var loadingScheme = cfg.timezoneLoadingScheme || "preloadAll";
  34. // defaultZoneFile: String or String[]
  35. // The default file (or files) to load on startup - other files will
  36. // be lazily-loaded on-demand
  37. var defaultZoneFile = cfg.defaultZoneFile ||
  38. ((loadingScheme == "preloadAll") ? _zoneFiles : "northamerica");
  39. // Set our olson-zoneinfo content handler
  40. dojo._contentHandlers["olson-zoneinfo"] = function(xhr){
  41. var str = dojo._contentHandlers["text"](xhr),
  42. s = "",
  43. lines = str.split("\n"),
  44. arr = [],
  45. chunk = "",
  46. zone = null,
  47. rule = null,
  48. ret = {zones: {}, rules: {}};
  49. for(var i = 0; i < lines.length; i++){
  50. var l = lines[i];
  51. if(l.match(/^\s/)){
  52. l = "Zone " + zone + l;
  53. }
  54. l = l.split("#")[0];
  55. if(l.length > 3){
  56. arr = l.split(/\s+/);
  57. chunk = arr.shift();
  58. switch(chunk){
  59. case 'Zone':
  60. zone = arr.shift();
  61. if(arr[0]){
  62. // Handle extra commas in the middle of a zone
  63. if(!ret.zones[zone]){ ret.zones[zone] = []; }
  64. ret.zones[zone].push(arr);
  65. }
  66. break;
  67. case 'Rule':
  68. rule = arr.shift();
  69. if(!ret.rules[rule]){ ret.rules[rule] = []; }
  70. ret.rules[rule].push(arr);
  71. break;
  72. case 'Link':
  73. // No zones for these should already exist
  74. if(ret.zones[arr[1]]){
  75. throw new Error('Error with Link ' + arr[1]);
  76. }
  77. // Create the link
  78. ret.zones[arr[1]] = arr[0];
  79. break;
  80. case 'Leap':
  81. break;
  82. default:
  83. // Fail silently
  84. break;
  85. }
  86. }
  87. }
  88. return ret; // Object
  89. };
  90. function loadZoneData(/* Object */ data){
  91. // summary:
  92. // Loads the given data object into the zone database
  93. //
  94. // data: Object
  95. // The data to load - contains "zones" and "rules" parameters
  96. data = data || {};
  97. _zones = dojo.mixin(_zones, data.zones||{});
  98. _rules = dojo.mixin(_rules, data.rules||{});
  99. }
  100. function loadZoneFile(/* String */ fileName){
  101. // summary:
  102. // Loads the given URL of the Olson zone information into the
  103. // zone database
  104. //
  105. // fileName: String
  106. // The zoneinfo file name to load
  107. // TODO: Maybe behave similar to requireLocalization - rather than
  108. // Using dojo.xhrGet?
  109. _loadedZones[fileName] = true;
  110. dojo.xhrGet({
  111. url: timezoneFileBasePath + "/" + fileName,
  112. sync: true, // Needs to be synchronous so we can return values
  113. handleAs: "olson-zoneinfo",
  114. load: loadZoneData,
  115. error: function(e){
  116. console.error("Error loading zone file:", e);
  117. throw e;
  118. }
  119. });
  120. }
  121. var monthMap = { 'jan': 0, 'feb': 1, 'mar': 2, 'apr': 3,'may': 4, 'jun': 5,
  122. 'jul': 6, 'aug': 7, 'sep': 8, 'oct': 9, 'nov': 10, 'dec': 11 },
  123. dayMap = {'sun': 0, 'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4,
  124. 'fri': 5, 'sat': 6 },
  125. regionMap = {'EST': "northamerica", 'MST': "northamerica",
  126. 'HST': "northamerica", 'EST5EDT': "northamerica",
  127. 'CST6CDT': "northamerica", 'MST7MDT': "northamerica",
  128. 'PST8PDT': "northamerica", 'America': "northamerica",
  129. 'Pacific': "australasia", 'Atlantic': "europe",
  130. 'Africa': "africa", 'Indian': "africa",
  131. 'Antarctica': "antarctica", 'Asia': "asia",
  132. 'Australia': "australasia", 'Europe': "europe",
  133. 'WET': "europe", 'CET': "europe", 'MET': "europe",
  134. 'EET': "europe"},
  135. regionExceptions = {'Pacific/Honolulu':"northamerica",
  136. 'Atlantic/Bermuda':"northamerica",
  137. 'Atlantic/Cape_Verde':"africa",
  138. 'Atlantic/St_Helena':"africa",
  139. 'Indian/Kerguelen':"antarctica",
  140. 'Indian/Chagos':"asia",
  141. 'Indian/Maldives':"asia",
  142. 'Indian/Christmas':"australasia",
  143. 'Indian/Cocos':"australasia",
  144. 'America/Danmarkshavn':"europe",
  145. 'America/Scoresbysund':"europe",
  146. 'America/Godthab':"europe",
  147. 'America/Thule':"europe",
  148. 'Asia/Yekaterinburg':"europe",
  149. 'Asia/Omsk':"europe",
  150. 'Asia/Novosibirsk':"europe",
  151. 'Asia/Krasnoyarsk':"europe",
  152. 'Asia/Irkutsk':"europe",
  153. 'Asia/Yakutsk':"europe",
  154. 'Asia/Vladivostok':"europe",
  155. 'Asia/Sakhalin':"europe",
  156. 'Asia/Magadan':"europe",
  157. 'Asia/Kamchatka':"europe",
  158. 'Asia/Anadyr':"europe",
  159. 'Africa/Ceuta':"europe",
  160. 'America/Argentina/Buenos_Aires':"southamerica",
  161. 'America/Argentina/Cordoba':"southamerica",
  162. 'America/Argentina/Tucuman':"southamerica",
  163. 'America/Argentina/La_Rioja':"southamerica",
  164. 'America/Argentina/San_Juan':"southamerica",
  165. 'America/Argentina/Jujuy':"southamerica",
  166. 'America/Argentina/Catamarca':"southamerica",
  167. 'America/Argentina/Mendoza':"southamerica",
  168. 'America/Argentina/Rio_Gallegos':"southamerica",
  169. 'America/Argentina/Ushuaia':"southamerica",
  170. 'America/Aruba':"southamerica",
  171. 'America/La_Paz':"southamerica",
  172. 'America/Noronha':"southamerica",
  173. 'America/Belem':"southamerica",
  174. 'America/Fortaleza':"southamerica",
  175. 'America/Recife':"southamerica",
  176. 'America/Araguaina':"southamerica",
  177. 'America/Maceio':"southamerica",
  178. 'America/Bahia':"southamerica",
  179. 'America/Sao_Paulo':"southamerica",
  180. 'America/Campo_Grande':"southamerica",
  181. 'America/Cuiaba':"southamerica",
  182. 'America/Porto_Velho':"southamerica",
  183. 'America/Boa_Vista':"southamerica",
  184. 'America/Manaus':"southamerica",
  185. 'America/Eirunepe':"southamerica",
  186. 'America/Rio_Branco':"southamerica",
  187. 'America/Santiago':"southamerica",
  188. 'Pacific/Easter':"southamerica",
  189. 'America/Bogota':"southamerica",
  190. 'America/Curacao':"southamerica",
  191. 'America/Guayaquil':"southamerica",
  192. 'Pacific/Galapagos':"southamerica",
  193. 'Atlantic/Stanley':"southamerica",
  194. 'America/Cayenne':"southamerica",
  195. 'America/Guyana':"southamerica",
  196. 'America/Asuncion':"southamerica",
  197. 'America/Lima':"southamerica",
  198. 'Atlantic/South_Georgia':"southamerica",
  199. 'America/Paramaribo':"southamerica",
  200. 'America/Port_of_Spain':"southamerica",
  201. 'America/Montevideo':"southamerica",
  202. 'America/Caracas':"southamerica"},
  203. abbrExceptions = { 'US': "S", 'Chatham': "S", 'NZ': "S", 'NT_YK': "S",
  204. 'Edm': "S", 'Salv': "S", 'Canada': "S", 'StJohns': "S",
  205. 'TC': "S", 'Guat': "S", 'Mexico': "S", 'Haiti': "S",
  206. 'Barb': "S", 'Belize': "S", 'CR': "S", 'Moncton': "S",
  207. 'Swift': "S", 'Hond': "S", 'Thule': "S", 'NZAQ': "S",
  208. 'Zion': "S", 'ROK': "S", 'PRC': "S", 'Taiwan': "S",
  209. 'Ghana': "GMT", 'SL': "WAT", 'Chicago': "S",
  210. 'Detroit': "S", 'Vanc': "S", 'Denver': "S",
  211. 'Halifax': "S", 'Cuba': "S", 'Indianapolis': "S",
  212. 'Starke': "S", 'Marengo': "S", 'Pike': "S",
  213. 'Perry': "S", 'Vincennes': "S", 'Pulaski': "S",
  214. 'Louisville': "S", 'CA': "S", 'Nic': "S",
  215. 'Menominee': "S", 'Mont': "S", 'Bahamas': "S",
  216. 'NYC': "S", 'Regina': "S", 'Resolute': "ES",
  217. 'DR': "S", 'Toronto': "S", 'Winn': "S" };
  218. function invalidTZError(t) {
  219. throw new Error('Timezone "' + t +
  220. '" is either incorrect, or not loaded in the timezone registry.');
  221. }
  222. function getRegionForTimezone(/* String */ tz) {
  223. // summary:
  224. // Returns the Olson region for the given timezone
  225. var ret = regionExceptions[tz];
  226. if(!ret){
  227. var reg = tz.split('/')[0];
  228. ret = regionMap[reg];
  229. // If there's nothing listed in the main regions for
  230. // this TZ, check the 'backward' links
  231. if(!ret){
  232. var link = _zones[tz];
  233. if(typeof link == 'string'){
  234. return getRegionForTimezone(link); // String
  235. }else{
  236. // Backward-compat file hasn't loaded yet, try looking in there
  237. if (!_loadedZones.backward) {
  238. // This is for obvious legacy zones (e.g., Iceland) that
  239. // don't even have a prefix like "America/" that look like
  240. // normal zones
  241. loadZoneFile("backward");
  242. return getRegionForTimezone(tz); // String
  243. }else{
  244. invalidTZError(tz);
  245. }
  246. }
  247. }
  248. }
  249. return ret; // String
  250. }
  251. function parseTimeString(/* String */ str) {
  252. // summary:
  253. // Parses the given time string and returns it as an integer array
  254. var pat = /(\d+)(?::0*(\d*))?(?::0*(\d*))?([su])?$/;
  255. var hms = str.match(pat);
  256. if(!hms){
  257. return null;
  258. }
  259. hms[1] = parseInt(hms[1], 10);
  260. hms[2] = hms[2] ? parseInt(hms[2], 10) : 0;
  261. hms[3] = hms[3] ? parseInt(hms[3], 10) : 0;
  262. return hms; // int[]
  263. }
  264. function getUTCStamp(/* int */ y, /* int */ m, /* int */ d, /* int */ h,
  265. /* int */ mn, /* int */ s, /* int? */ off){
  266. // summary:
  267. // Returns the UTC timestamp, adjusted by the given (optional) offset
  268. return Date.UTC(y, m, d, h, mn, s) + ((off||0) * 60 * 1000);
  269. }
  270. function getMonthNumber(/* String */ m){
  271. // summary:
  272. // Returns the javascript month number for the given string
  273. return monthMap[m.substr(0, 3).toLowerCase()];
  274. }
  275. function getOffsetInMins(/* String */ str){
  276. // summary:
  277. // Returns the offset value represented by the string, in minutes
  278. var off = parseTimeString(str);
  279. if(off === null){ return 0; }
  280. var adj = str.indexOf('-') === 0 ? -1 : 1;
  281. off = adj * (((off[1] * 60 + off[2]) *60 + off[3]) * 1000);
  282. return -off/60/1000;
  283. }
  284. function _getRuleStart(/* Rule */ rule, /* int */ year, /* int */ off){
  285. // summary:
  286. // Returns a date that the rule begins matching in the given year.
  287. var month = getMonthNumber(rule[3]),
  288. day = rule[4],
  289. time = parseTimeString(rule[5]);
  290. if(time[4] == "u"){
  291. // We are UTC - so there is no offset to use
  292. off = 0;
  293. }
  294. var d, dtDay, incr;
  295. if(isNaN(day)){
  296. if(day.substr(0, 4) == "last"){
  297. // Last day of the month at the desired time of day
  298. day = dayMap[day.substr(4,3).toLowerCase()];
  299. d = new Date(getUTCStamp(year, month + 1, 1,
  300. time[1] - 24, time[2], time[3],
  301. off));
  302. dtDay = _dd.add(d, "minute", -off).getUTCDay();
  303. // Set it to the final day of the correct weekday that month
  304. incr = (day > dtDay) ? (day - dtDay - 7) : (day - dtDay);
  305. if(incr !== 0){
  306. d = _dd.add(d, "hour", incr * 24);
  307. }
  308. return d;
  309. }else{
  310. day = dayMap[day.substr(0, 3).toLowerCase()];
  311. if(day != "undefined"){
  312. if(rule[4].substr(3, 2) == '>='){
  313. // The stated date of the month
  314. d = new Date(getUTCStamp(year, month, parseInt(rule[4].substr(5), 10),
  315. time[1], time[2], time[3], off));
  316. dtDay = _dd.add(d, "minute", -off).getUTCDay();
  317. // Set to the first correct weekday after the stated date
  318. incr = (day < dtDay) ? (day - dtDay + 7) : (day - dtDay);
  319. if(incr !== 0){
  320. d = _dd.add(d, "hour", incr * 24);
  321. }
  322. return d;
  323. }else if(day.substr(3, 2) == '<='){
  324. // The stated date of the month
  325. d = new Date(getUTCStamp(year, month, parseInt(rule[4].substr(5), 10),
  326. time[1], time[2], time[3], off));
  327. dtDay = _dd.add(d, "minute", -off).getUTCDay();
  328. // Set to first correct weekday before the stated date
  329. incr = (day > dtDay) ? (day - dtDay - 7) : (day - dtDay);
  330. if(incr !== 0){
  331. d = _dd.add(d, "hour", incr * 24);
  332. }
  333. return d;
  334. }
  335. }
  336. }
  337. }else{
  338. // Numeric date
  339. d = new Date(getUTCStamp(year, month, parseInt(day, 10),
  340. time[1], time[2], time[3], off));
  341. return d;
  342. }
  343. return null;
  344. }
  345. function _getRulesForYear(/* Zone */ zone, /* int */ year){
  346. var rules = [];
  347. dojo.forEach(_rules[zone[1]]||[], function(r){
  348. // Clean up rules as needed
  349. for(var i = 0; i < 2; i++){
  350. switch(r[i]){
  351. case "min":
  352. r[i] = _minYear;
  353. break;
  354. case "max":
  355. r[i] = _maxYear;
  356. break;
  357. case "only":
  358. break;
  359. default:
  360. r[i] = parseInt(r[i], 10);
  361. if(isNaN(r[i])){
  362. throw new Error('Invalid year found on rule');
  363. }
  364. break;
  365. }
  366. }
  367. if(typeof r[6] == "string"){
  368. // Change our offset to be an integer
  369. r[6] = getOffsetInMins(r[6]);
  370. }
  371. // Quick-filter to grab all rules that match my year
  372. if((r[0] <= year && r[1] >= year) || // Matches my y
  373. (r[0] == year && r[1] == "only")){ // Matches my only
  374. rules.push({r: r, d: _getRuleStart(r, year, zone[0])});
  375. }
  376. });
  377. return rules;
  378. }
  379. function _loadZoneRanges(/* String */ tz, /* Object[] */ zoneList) {
  380. // summary:
  381. // Loads the zone ranges for the given timezone
  382. var zr = _loadedRanges[tz] = [];
  383. for(var i = 0; i < zoneList.length; i++){
  384. var z = zoneList[i];
  385. var r = zr[i] = [];
  386. var prevZone = null;
  387. var prevRange = null;
  388. var prevRules = [];
  389. // Set up our zone offset to not be a string anymore
  390. if(typeof z[0] == "string"){
  391. z[0] = getOffsetInMins(z[0]);
  392. }
  393. if(i === 0){
  394. // The beginning of zoneinfo time - let's not worry about
  395. // to-the-hour accuracy before Jan 1, 1835
  396. r[0] = Date.UTC(_minYear,0,1,0,0,0,0);
  397. }else{
  398. r[0] = zr[i - 1][1];
  399. prevZone = zoneList[i - 1];
  400. prevRange = zr[i - 1];
  401. prevRules = prevRange[2];
  402. }
  403. // Load the rules that will be going in to our zone
  404. var startYear = new Date(r[0]).getUTCFullYear();
  405. var endYear = z[3] ? parseInt(z[3], 10) : _maxYear;
  406. var rlz = [];
  407. var j;
  408. for(j = startYear; j <= endYear; j++){
  409. rlz = rlz.concat(_getRulesForYear(z, j));
  410. }
  411. rlz.sort(function(a, b){
  412. return _dd.compare(a.d, b.d);
  413. });
  414. var rl;
  415. for(j = 0, rl; (rl = rlz[j]); j++){
  416. var prevRule = j > 0 ? rlz[j - 1] : null;
  417. if(rl.r[5].indexOf("u") < 0 && rl.r[5].indexOf("s") < 0){
  418. if(j === 0 && i > 0){
  419. if(prevRules.length){
  420. // We have a previous rule - so use it
  421. rl.d = _dd.add(rl.d, "minute", prevRules[prevRules.length - 1].r[6]);
  422. }else if(_dd.compare(new Date(prevRange[1]), rl.d, "date") === 0){
  423. // No previous rules - but our date is the same as the
  424. // previous zone ended on - so use that.
  425. rl.d = new Date(prevRange[1]);
  426. }else{
  427. rl.d = _dd.add(rl.d, "minute", getOffsetInMins(prevZone[1]));
  428. }
  429. }else if(j > 0){
  430. rl.d = _dd.add(rl.d, "minute", prevRule.r[6]);
  431. }
  432. }
  433. }
  434. r[2] = rlz;
  435. if(!z[3]){
  436. // The end of zoneinfo time - we'll cross this bridge when we
  437. // get close to Dec 31, 2038
  438. r[1] = Date.UTC(_maxYear,11,31,23,59,59,999);
  439. }else{
  440. var year = parseInt(z[3], 10),
  441. month = getMonthNumber(z[4]||"Jan"),
  442. day = parseInt(z[5]||"1", 10),
  443. time = parseTimeString(z[6]||"0");
  444. var utcStmp = r[1] = getUTCStamp(year, month, day,
  445. time[1], time[2], time[3],
  446. ((time[4] == "u") ? 0 : z[0]));
  447. if(isNaN(utcStmp)){
  448. utcStmp = r[1] = _getRuleStart([0,0,0,z[4],z[5],z[6]||"0"],
  449. year, ((time[4] == "u") ? 0 : z[0])).getTime();
  450. }
  451. var matches = dojo.filter(rlz, function(rl, idx){
  452. var o = idx > 0 ? rlz[idx - 1].r[6] * 60 * 1000 : 0;
  453. return (rl.d.getTime() < utcStmp + o);
  454. });
  455. if(time[4] != "u" && time[4] != "s"){
  456. if(matches.length){
  457. r[1] += matches[matches.length - 1].r[6] * 60 * 1000;
  458. }else{
  459. r[1] += getOffsetInMins(z[1]) * 60 * 1000;
  460. }
  461. }
  462. }
  463. }
  464. }
  465. function getZoneInfo(/* String */ dt, /* String */ tz) {
  466. // summary:
  467. // Returns the zone entry from the zoneinfo database for the given date
  468. // and timezone
  469. var t = tz;
  470. var zoneList = _zones[t];
  471. // Follow links to get to an actual zone
  472. while(typeof zoneList == "string"){
  473. t = zoneList;
  474. zoneList = _zones[t];
  475. }
  476. if(!zoneList){
  477. // Backward-compat file hasn't loaded yet, try looking in there
  478. if(!_loadedZones.backward){
  479. // This is for backward entries like "America/Fort_Wayne" that
  480. // getRegionForTimezone *thinks* it has a region file and zone
  481. // for (e.g., America => 'northamerica'), but in reality it's a
  482. // legacy zone we need the backward file for
  483. var parsed = loadZoneFile("backward", true);
  484. return getZoneInfo(dt, tz); //Object
  485. }
  486. invalidTZError(t);
  487. }
  488. if(!_loadedRanges[tz]){
  489. _loadZoneRanges(tz, zoneList);
  490. }
  491. var ranges = _loadedRanges[tz];
  492. var tm = dt.getTime();
  493. for(var i = 0, r; (r = ranges[i]); i++){
  494. if(tm >= r[0] && tm < r[1]){
  495. return {zone: zoneList[i], range: ranges[i], idx: i};
  496. }
  497. }
  498. throw new Error('No Zone found for "' + tz + '" on ' + dt);
  499. }
  500. function getRule(/* Date */ dt, /* ZoneInfo */ zoneInfo) {
  501. // summary:
  502. // Returns the latest-matching rule entry from the zoneinfo
  503. // database for the given date and zone
  504. var lastMatch = -1;
  505. var rules = zoneInfo.range[2]||[];
  506. var tsp = dt.getTime();
  507. var zr = zoneInfo.range;
  508. for(var i = 0, r; (r = rules[i]); i++){
  509. if(tsp >= r.d.getTime()){
  510. lastMatch = i;
  511. }
  512. }
  513. if(lastMatch >= 0){
  514. return rules[lastMatch].r;
  515. }
  516. return null;
  517. }
  518. function getAbbreviation(/* String */ tz, /* Object */ zoneInfo, /* Object */ rule) {
  519. // summary:
  520. // Returns the abbreviation for the given zone and rule
  521. var res;
  522. var zone = zoneInfo.zone;
  523. var base = zone[2];
  524. if(base.indexOf('%s') > -1){
  525. var repl;
  526. if(rule){
  527. repl = rule[7];
  528. if(repl == "-"){ repl = ""; }
  529. }else if(zone[1] in abbrExceptions){
  530. repl = abbrExceptions[zone[1]];
  531. }else{
  532. if(zoneInfo.idx > 0){
  533. // Check if our previous zone's base is the same as our
  534. // current in "S" (standard) mode. If so, then use "S"
  535. // for our replacement
  536. var pz = _zones[tz][zoneInfo.idx - 1];
  537. var pb = pz[2];
  538. if(pb.indexOf('%s') < 0){
  539. if(base.replace('%s', "S") == pb){
  540. repl = "S";
  541. }else{
  542. repl = "";
  543. }
  544. }else{
  545. repl = "";
  546. }
  547. }else{
  548. repl = "";
  549. }
  550. }
  551. res = base.replace('%s', repl);
  552. }else if(base.indexOf("/") > -1){
  553. var bs = base.split("/");
  554. if(rule){
  555. res = bs[rule[6] === 0 ? 0 : 1];
  556. }else{
  557. res = bs[0];
  558. }
  559. }else{
  560. res = base;
  561. }
  562. return res; // String
  563. }
  564. /*=====
  565. dojox.date.timezone = function(){
  566. // summary:
  567. // mix-in to dojo.date to provide timezones based on
  568. // the Olson timezone data
  569. //
  570. // description:
  571. // mix-in to dojo.date to provide timezones based on
  572. // the Olson timezone data.
  573. // If you pass "timezone" as a parameter to your format options,
  574. // then you get the date formatted (and offset) for that timezone
  575. //TODOC
  576. };
  577. dojox.date.timezone.getTzInfo = function(dt, tz){
  578. // summary:
  579. // Returns the timezone information for the given date and
  580. // timezone string
  581. //
  582. // dt: Date
  583. // The Date - a "proxyDate"
  584. //
  585. // tz: String
  586. // String representation of the timezone you want to get info
  587. // for date
  588. };
  589. dojox.date.timezone.loadZoneData = function(data){
  590. // summary:
  591. // Loads the given data object into the zone database
  592. //
  593. // data: Object
  594. // The data to load - contains "zones" and "rules" parameters
  595. };
  596. dojox.date.timezone.getAllZones = function(){
  597. // summary:
  598. // Returns an array of zones that have been loaded
  599. };
  600. =====*/
  601. dojo.setObject("dojox.date.timezone", {
  602. getTzInfo: function(/* Date */ dt, /* String */ tz){
  603. // Lazy-load any zones not yet loaded
  604. if(loadingScheme == "lazyLoad"){
  605. // Get the correct region for the zone
  606. var zoneFile = getRegionForTimezone(tz);
  607. if(!zoneFile){
  608. throw new Error("Not a valid timezone ID.");
  609. }else{
  610. if(!_loadedZones[zoneFile]){
  611. // Get the file and parse it -- use synchronous XHR
  612. loadZoneFile(zoneFile);
  613. }
  614. }
  615. }
  616. var zoneInfo = getZoneInfo(dt, tz);
  617. var off = zoneInfo.zone[0];
  618. // See if the offset needs adjustment
  619. var rule = getRule(dt, zoneInfo);
  620. if(rule){
  621. off += rule[6];
  622. }else{
  623. if(_rules[zoneInfo.zone[1]] && zoneInfo.idx > 0){
  624. off += getOffsetInMins(_zones[tz][zoneInfo.idx - 1][1]);
  625. }else{
  626. off += getOffsetInMins(zoneInfo.zone[1]);
  627. }
  628. }
  629. var abbr = getAbbreviation(tz, zoneInfo, rule);
  630. return { tzOffset: off, tzAbbr: abbr }; // Object
  631. },
  632. loadZoneData: function(data){
  633. loadZoneData(data);
  634. },
  635. getAllZones: function(){
  636. var arr = [];
  637. for(var z in _zones){ arr.push(z); }
  638. arr.sort();
  639. return arr; // String[]
  640. }
  641. });
  642. // Now - initialize the stuff that we should have pre-loaded
  643. if(typeof defaultZoneFile == "string" && defaultZoneFile){
  644. defaultZoneFile = [defaultZoneFile];
  645. }
  646. if(dojo.isArray(defaultZoneFile)){
  647. dojo.forEach(defaultZoneFile, loadZoneFile);
  648. }
  649. // And enhance the default formatting functions
  650. // If you pass "timezone" as a parameter to your format options,
  651. // then you get the date formatted (and offset) for that timezone
  652. var oLocaleFmt = _ddl.format,
  653. oGetZone = _ddl._getZone;
  654. _ddl.format = function(dateObject, options){
  655. options = options||{};
  656. if(options.timezone && !options._tzInfo){
  657. // Store it in our options so we can use it later
  658. options._tzInfo = dojox.date.timezone.getTzInfo(dateObject, options.timezone);
  659. }
  660. if(options._tzInfo){
  661. // Roll our date to display the correct time according to the
  662. // desired offset
  663. var offset = dateObject.getTimezoneOffset() - options._tzInfo.tzOffset;
  664. dateObject = new Date(dateObject.getTime() + (offset * 60 * 1000));
  665. }
  666. return oLocaleFmt.call(this, dateObject, options);
  667. };
  668. _ddl._getZone = function(dateObject, getName, options){
  669. if(options._tzInfo){
  670. return getName ? options._tzInfo.tzAbbr : options._tzInfo.tzOffset;
  671. }
  672. return oGetZone.call(this, dateObject, getName, options);
  673. };
  674. });