timezone.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Functions to provide timezone information for use with
  16. * date/time format.
  17. */
  18. goog.provide('goog.i18n.TimeZone');
  19. goog.require('goog.array');
  20. /** @suppress {extraRequire} goog.date.DateLike represents a Date or a
  21. * goog.Date object. It is a parameter in the following methods:
  22. * - getDaylightAdjustment
  23. * - getGMTString
  24. * - getLongName
  25. * - getOffset
  26. * - getRFCTimeZoneString
  27. * - getShortName
  28. * - isDaylightTime
  29. * - getLongNameGMT
  30. * - getGenericLocation
  31. * Lint warns that this require is unnecessary but the closure compiler needs
  32. * it in order to accept a Date or a goog.Date object as a goog.date.DateLike
  33. * parameter in any of these methods. */
  34. goog.require('goog.date.DateLike');
  35. goog.require('goog.object');
  36. goog.require('goog.string');
  37. /**
  38. * TimeZone class implemented a time zone resolution and name information
  39. * source for client applications. The time zone object is initiated from
  40. * a time zone information object. Application can initiate a time zone
  41. * statically, or it may choose to initiate from a data obtained from server.
  42. * Each time zone information array is small, but the whole set of data
  43. * is too much for client application to download. If end user is allowed to
  44. * change time zone setting, dynamic retrieval should be the method to use.
  45. * In case only time zone offset is known, there is a decent fallback
  46. * that only use the time zone offset to create a TimeZone object.
  47. * A whole set of time zone information array was available under
  48. * http://go/js_locale_data. It is generated based on CLDR/ICU and
  49. * Olson time zone data base, and will be updated timely.
  50. *
  51. * @constructor
  52. * @final
  53. */
  54. goog.i18n.TimeZone = function() {
  55. /**
  56. * The standard time zone id.
  57. * @type {string}
  58. * @private
  59. */
  60. this.timeZoneId_;
  61. /**
  62. * The standard, non-daylight time zone offset, in minutes WEST of UTC.
  63. * @type {number}
  64. * @private
  65. */
  66. this.standardOffset_;
  67. /**
  68. * An array of strings that can have 2 or 4 elements. The first two elements
  69. * are the long and short names for standard time in this time zone, and the
  70. * last two elements (if present) are the long and short names for daylight
  71. * time in this time zone.
  72. * @type {Array<string>}
  73. * @private
  74. */
  75. this.tzNames_;
  76. /**
  77. * An object of 2 to 4 elements. The STD_* are always available, while the
  78. * DST_* are only available when daylight saving time is available for this
  79. * time zone.
  80. * <ul>
  81. * <li>STD_LONG_NAME_GMT: long GMT name for standard time</li>
  82. * <li>STD_GENERIC_LOCATION: generic location for standard time</li>
  83. * <li>DST_LONG_NAME_GMT: long GMT for daylight saving time</li>
  84. * <li>DST_GENERIC_LOCATION: generic location for daylight saving time</li>
  85. * </ul>
  86. * @type { { STD_LONG_NAME_GMT:string, STD_GENERIC_LOCATION:string } |
  87. * { STD_LONG_NAME_GMT:string, STD_GENERIC_LOCATION:string,
  88. * DST_LONG_NAME_GMT:string, DST_GENERIC_LOCATION:string }
  89. * }
  90. * @private
  91. */
  92. this.tzNamesExt_;
  93. /**
  94. * This array specifies the Daylight Saving Time transitions for this time
  95. * zone. This is a flat array of numbers which are interpreted in pairs:
  96. * [time1, adjustment1, time2, adjustment2, ...] where each time is a DST
  97. * transition point given as a number of hours since 00:00 UTC, January 1,
  98. * 1970, and each adjustment is the adjustment to apply for times after the
  99. * DST transition, given as minutes EAST of UTC.
  100. * @type {Array<number>}
  101. * @private
  102. */
  103. this.transitions_;
  104. };
  105. /**
  106. * The number of milliseconds in an hour.
  107. * @type {number}
  108. * @private
  109. */
  110. goog.i18n.TimeZone.MILLISECONDS_PER_HOUR_ = 3600 * 1000;
  111. /**
  112. * Indices into the array of time zone names.
  113. * @enum {number}
  114. */
  115. goog.i18n.TimeZone.NameType = {
  116. STD_SHORT_NAME: 0,
  117. STD_LONG_NAME: 1,
  118. DLT_SHORT_NAME: 2,
  119. DLT_LONG_NAME: 3
  120. };
  121. /**
  122. * This factory method creates a time zone instance. It takes either an object
  123. * containing complete time zone information, or a single number representing a
  124. * constant time zone offset. If the latter form is used, DST functionality is
  125. * not available.
  126. *
  127. * @param {number|Object} timeZoneData If this parameter is a number, it should
  128. * indicate minutes WEST of UTC to be used as a constant time zone offset.
  129. * Otherwise, it should be an object with these four fields:
  130. * <ul>
  131. * <li>id: A string ID for the time zone.
  132. * <li>std_offset: The standard time zone offset in minutes EAST of UTC.
  133. * <li>names: An array of four names (standard short name, standard long
  134. * name, daylight short name, daylight long, name)
  135. * <li>names_ext: A hash of four fields (standard long name gmt, daylight
  136. * long name gmt, standard generic location, daylight generic
  137. * location)
  138. * <li>transitions: An array of numbers which are interpreted in pairs:
  139. * [time1, adjustment1, time2, adjustment2, ...] where each time is
  140. * a DST transition point given as a number of hours since 00:00 UTC,
  141. * January 1, 1970, and each adjustment is the adjustment to apply
  142. * for times after the DST transition, given as minutes EAST of UTC.
  143. * </ul>
  144. * @return {!goog.i18n.TimeZone} A goog.i18n.TimeZone object for the given
  145. * time zone data.
  146. */
  147. goog.i18n.TimeZone.createTimeZone = function(timeZoneData) {
  148. if (typeof timeZoneData == 'number') {
  149. return goog.i18n.TimeZone.createSimpleTimeZone_(timeZoneData);
  150. }
  151. var tz = new goog.i18n.TimeZone();
  152. tz.timeZoneId_ = timeZoneData['id'];
  153. tz.standardOffset_ = -timeZoneData['std_offset'];
  154. tz.tzNames_ = timeZoneData['names'];
  155. tz.tzNamesExt_ = timeZoneData['names_ext'];
  156. tz.transitions_ = timeZoneData['transitions'];
  157. return tz;
  158. };
  159. /**
  160. * This factory method creates a time zone object with a constant offset.
  161. * @param {number} timeZoneOffsetInMinutes Offset in minutes WEST of UTC.
  162. * @return {!goog.i18n.TimeZone} A time zone object with the given constant
  163. * offset. Note that the time zone ID of this object will use the POSIX
  164. * convention, which has a reversed sign ("Etc/GMT+8" means UTC-8 or PST).
  165. * @private
  166. */
  167. goog.i18n.TimeZone.createSimpleTimeZone_ = function(timeZoneOffsetInMinutes) {
  168. var tz = new goog.i18n.TimeZone();
  169. tz.standardOffset_ = timeZoneOffsetInMinutes;
  170. tz.timeZoneId_ =
  171. goog.i18n.TimeZone.composePosixTimeZoneID_(timeZoneOffsetInMinutes);
  172. var str = goog.i18n.TimeZone.composeUTCString_(timeZoneOffsetInMinutes);
  173. var strGMT = goog.i18n.TimeZone.composeGMTString_(timeZoneOffsetInMinutes);
  174. tz.tzNames_ = [str, str];
  175. tz.tzNamesExt_ = {STD_LONG_NAME_GMT: strGMT, STD_GENERIC_LOCATION: strGMT};
  176. tz.transitions_ = [];
  177. return tz;
  178. };
  179. /**
  180. * Generate a GMT-relative string for a constant time zone offset.
  181. * @param {number} offset The time zone offset in minutes WEST of UTC.
  182. * @return {string} The GMT string for this offset, which will indicate
  183. * hours EAST of UTC.
  184. * @private
  185. */
  186. goog.i18n.TimeZone.composeGMTString_ = function(offset) {
  187. var parts = ['GMT'];
  188. parts.push(offset <= 0 ? '+' : '-');
  189. offset = Math.abs(offset);
  190. parts.push(
  191. goog.string.padNumber(Math.floor(offset / 60) % 100, 2), ':',
  192. goog.string.padNumber(offset % 60, 2));
  193. return parts.join('');
  194. };
  195. /**
  196. * Generate a POSIX time zone ID for a constant time zone offset.
  197. * @param {number} offset The time zone offset in minutes WEST of UTC.
  198. * @return {string} The POSIX time zone ID for this offset, which will indicate
  199. * hours WEST of UTC.
  200. * @private
  201. */
  202. goog.i18n.TimeZone.composePosixTimeZoneID_ = function(offset) {
  203. if (offset == 0) {
  204. return 'Etc/GMT';
  205. }
  206. var parts = ['Etc/GMT', offset < 0 ? '-' : '+'];
  207. offset = Math.abs(offset);
  208. parts.push(Math.floor(offset / 60) % 100);
  209. offset = offset % 60;
  210. if (offset != 0) {
  211. parts.push(':', goog.string.padNumber(offset, 2));
  212. }
  213. return parts.join('');
  214. };
  215. /**
  216. * Generate a UTC-relative string for a constant time zone offset.
  217. * @param {number} offset The time zone offset in minutes WEST of UTC.
  218. * @return {string} The UTC string for this offset, which will indicate
  219. * hours EAST of UTC.
  220. * @private
  221. */
  222. goog.i18n.TimeZone.composeUTCString_ = function(offset) {
  223. if (offset == 0) {
  224. return 'UTC';
  225. }
  226. var parts = ['UTC', offset < 0 ? '+' : '-'];
  227. offset = Math.abs(offset);
  228. parts.push(Math.floor(offset / 60) % 100);
  229. offset = offset % 60;
  230. if (offset != 0) {
  231. parts.push(':', offset);
  232. }
  233. return parts.join('');
  234. };
  235. /**
  236. * Convert the contents of time zone object to a timeZoneData object, suitable
  237. * for passing to goog.i18n.TimeZone.createTimeZone.
  238. * @return {!Object} A timeZoneData object (see the documentation for
  239. * goog.i18n.TimeZone.createTimeZone).
  240. */
  241. goog.i18n.TimeZone.prototype.getTimeZoneData = function() {
  242. return {
  243. 'id': this.timeZoneId_,
  244. 'std_offset': -this.standardOffset_, // note createTimeZone flips the sign
  245. 'names': goog.array.clone(this.tzNames_), // avoid aliasing the array
  246. 'names_ext': goog.object.clone(this.tzNamesExt_), // avoid aliasing
  247. 'transitions': goog.array.clone(this.transitions_) // avoid aliasing
  248. };
  249. };
  250. /**
  251. * Return the DST adjustment to the time zone offset for a given time.
  252. * While Daylight Saving Time is in effect, this number is positive.
  253. * Otherwise, it is zero.
  254. * @param {goog.date.DateLike} date The time to check.
  255. * @return {number} The DST adjustment in minutes EAST of UTC.
  256. */
  257. goog.i18n.TimeZone.prototype.getDaylightAdjustment = function(date) {
  258. var timeInMs = Date.UTC(
  259. date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
  260. date.getUTCHours(), date.getUTCMinutes());
  261. var timeInHours = timeInMs / goog.i18n.TimeZone.MILLISECONDS_PER_HOUR_;
  262. var index = 0;
  263. while (index < this.transitions_.length &&
  264. timeInHours >= this.transitions_[index]) {
  265. index += 2;
  266. }
  267. return (index == 0) ? 0 : this.transitions_[index - 1];
  268. };
  269. /**
  270. * Return the GMT representation of this time zone object.
  271. * @param {goog.date.DateLike} date The date for which time to retrieve
  272. * GMT string.
  273. * @return {string} GMT representation string.
  274. */
  275. goog.i18n.TimeZone.prototype.getGMTString = function(date) {
  276. return goog.i18n.TimeZone.composeGMTString_(this.getOffset(date));
  277. };
  278. /**
  279. * Get the long time zone name for a given date/time.
  280. * @param {goog.date.DateLike} date The time for which to retrieve
  281. * the long time zone name.
  282. * @return {string} The long time zone name.
  283. */
  284. goog.i18n.TimeZone.prototype.getLongName = function(date) {
  285. return this.tzNames_[this.isDaylightTime(date) ?
  286. goog.i18n.TimeZone.NameType.DLT_LONG_NAME :
  287. goog.i18n.TimeZone.NameType.STD_LONG_NAME];
  288. };
  289. /**
  290. * Get the time zone offset in minutes WEST of UTC for a given date/time.
  291. * @param {goog.date.DateLike} date The time for which to retrieve
  292. * the time zone offset.
  293. * @return {number} The time zone offset in minutes WEST of UTC.
  294. */
  295. goog.i18n.TimeZone.prototype.getOffset = function(date) {
  296. return this.standardOffset_ - this.getDaylightAdjustment(date);
  297. };
  298. /**
  299. * Get the RFC representation of the time zone for a given date/time.
  300. * @param {goog.date.DateLike} date The time for which to retrieve the
  301. * RFC time zone string.
  302. * @return {string} The RFC time zone string.
  303. */
  304. goog.i18n.TimeZone.prototype.getRFCTimeZoneString = function(date) {
  305. var offset = -this.getOffset(date);
  306. var parts = [offset < 0 ? '-' : '+'];
  307. offset = Math.abs(offset);
  308. parts.push(
  309. goog.string.padNumber(Math.floor(offset / 60) % 100, 2),
  310. goog.string.padNumber(offset % 60, 2));
  311. return parts.join('');
  312. };
  313. /**
  314. * Get the short time zone name for given date/time.
  315. * @param {goog.date.DateLike} date The time for which to retrieve
  316. * the short time zone name.
  317. * @return {string} The short time zone name.
  318. */
  319. goog.i18n.TimeZone.prototype.getShortName = function(date) {
  320. return this.tzNames_[this.isDaylightTime(date) ?
  321. goog.i18n.TimeZone.NameType.DLT_SHORT_NAME :
  322. goog.i18n.TimeZone.NameType.STD_SHORT_NAME];
  323. };
  324. /**
  325. * Return the time zone ID for this time zone.
  326. * @return {string} The time zone ID.
  327. */
  328. goog.i18n.TimeZone.prototype.getTimeZoneId = function() {
  329. return this.timeZoneId_;
  330. };
  331. /**
  332. * Check if Daylight Saving Time is in effect at a given time in this time zone.
  333. * @param {goog.date.DateLike} date The time to check.
  334. * @return {boolean} True if Daylight Saving Time is in effect.
  335. */
  336. goog.i18n.TimeZone.prototype.isDaylightTime = function(date) {
  337. return this.getDaylightAdjustment(date) > 0;
  338. };
  339. /**
  340. * Get the long GMT time zone name for a given date/time.
  341. * @param {!goog.date.DateLike} date The time for which to retrieve
  342. * the long GMT time zone name.
  343. * @return {string} The long GMT time zone name.
  344. */
  345. goog.i18n.TimeZone.prototype.getLongNameGMT = function(date) {
  346. if (this.isDaylightTime(date)) {
  347. return (goog.isDef(this.tzNamesExt_.DST_LONG_NAME_GMT)) ?
  348. this.tzNamesExt_.DST_LONG_NAME_GMT :
  349. this.tzNamesExt_['DST_LONG_NAME_GMT'];
  350. } else {
  351. return (goog.isDef(this.tzNamesExt_.STD_LONG_NAME_GMT)) ?
  352. this.tzNamesExt_.STD_LONG_NAME_GMT :
  353. this.tzNamesExt_['STD_LONG_NAME_GMT'];
  354. }
  355. };
  356. /**
  357. * Get the generic location time zone name for a given date/time.
  358. * @param {!goog.date.DateLike} date The time for which to retrieve
  359. * the generic location time zone name.
  360. * @return {string} The generic location time zone name.
  361. */
  362. goog.i18n.TimeZone.prototype.getGenericLocation = function(date) {
  363. if (this.isDaylightTime(date)) {
  364. return (goog.isDef(this.tzNamesExt_.DST_GENERIC_LOCATION)) ?
  365. this.tzNamesExt_.DST_GENERIC_LOCATION :
  366. this.tzNamesExt_['DST_GENERIC_LOCATION'];
  367. } else {
  368. return (goog.isDef(this.tzNamesExt_.STD_GENERIC_LOCATION)) ?
  369. this.tzNamesExt_.STD_GENERIC_LOCATION :
  370. this.tzNamesExt_['STD_GENERIC_LOCATION'];
  371. }
  372. };