datetimeformat.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. // Copyright 2006 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 for dealing with date/time formatting.
  16. */
  17. /**
  18. * Namespace for i18n date/time formatting functions
  19. */
  20. goog.provide('goog.i18n.DateTimeFormat');
  21. goog.provide('goog.i18n.DateTimeFormat.Format');
  22. goog.require('goog.asserts');
  23. goog.require('goog.date');
  24. goog.require('goog.i18n.DateTimeSymbols');
  25. goog.require('goog.i18n.TimeZone');
  26. goog.require('goog.string');
  27. /**
  28. * Datetime formatting functions following the pattern specification as defined
  29. * in JDK, ICU and CLDR, with minor modification for typical usage in JS.
  30. * Pattern specification:
  31. * {@link http://userguide.icu-project.org/formatparse/datetime}
  32. * <pre>
  33. * Symbol Meaning Presentation Example
  34. * ------ ------- ------------ -------
  35. * G# era designator (Text) AD
  36. * y# year (Number) 1996
  37. * Y* year (week of year) (Number) 1997
  38. * u* extended year (Number) 4601
  39. * Q# quarter (Text) Q3 & 3rd quarter
  40. * M month in year (Text & Number) July & 07
  41. * L month in year (standalone) (Text & Number) July & 07
  42. * d day in month (Number) 10
  43. * h hour in am/pm (1~12) (Number) 12
  44. * H hour in day (0~23) (Number) 0
  45. * m minute in hour (Number) 30
  46. * s second in minute (Number) 55
  47. * S fractional second (Number) 978
  48. * E# day of week (Text) Tue & Tuesday
  49. * e* day of week (local 1~7) (Number) 2
  50. * c# day of week (standalone) (Text & Number) 2 & Tues & Tuesday & T
  51. * D* day in year (Number) 189
  52. * F* day of week in month (Number) 2 (2nd Wed in July)
  53. * w week in year (Number) 27
  54. * W* week in month (Number) 2
  55. * a am/pm marker (Text) PM
  56. * k hour in day (1~24) (Number) 24
  57. * K hour in am/pm (0~11) (Number) 0
  58. * z time zone (Text) Pacific Standard Time
  59. * Z# time zone (RFC 822) (Number) -0800
  60. * v# time zone (generic) (Text) America/Los_Angeles
  61. * V# time zone (Text) Los Angeles Time
  62. * g* Julian day (Number) 2451334
  63. * A* milliseconds in day (Number) 69540000
  64. * ' escape for text (Delimiter) 'Date='
  65. * '' single quote (Literal) 'o''clock'
  66. *
  67. * Item marked with '*' are not supported yet.
  68. * Item marked with '#' works different than java
  69. *
  70. * The count of pattern letters determine the format.
  71. * (Text): 4 or more, use full form, <4, use short or abbreviated form if it
  72. * exists. (e.g., "EEEE" produces "Monday", "EEE" produces "Mon")
  73. *
  74. * (Number): the minimum number of digits. Shorter numbers are zero-padded to
  75. * this amount (e.g. if "m" produces "6", "mm" produces "06"). Year is handled
  76. * specially; that is, if the count of 'y' is 2, the Year will be truncated to
  77. * 2 digits. (e.g., if "yyyy" produces "1997", "yy" produces "97".) Unlike other
  78. * fields, fractional seconds are padded on the right with zero.
  79. *
  80. * :(Text & Number) 3 or over, use text, otherwise use number. (e.g., "M"
  81. * produces "1", "MM" produces "01", "MMM" produces "Jan", and "MMMM" produces
  82. * "January".)
  83. *
  84. * Any characters in the pattern that are not in the ranges of ['a'..'z'] and
  85. * ['A'..'Z'] will be treated as quoted text. For instance, characters like ':',
  86. * '.', ' ', '#' and '@' will appear in the resulting time text even they are
  87. * not embraced within single quotes.
  88. * </pre>
  89. */
  90. /**
  91. * Construct a DateTimeFormat object based on current locale.
  92. * @constructor
  93. * @param {string|number} pattern pattern specification or pattern type.
  94. * @param {!Object=} opt_dateTimeSymbols Optional symbols to use for this
  95. * instance rather than the global symbols.
  96. * @final
  97. */
  98. goog.i18n.DateTimeFormat = function(pattern, opt_dateTimeSymbols) {
  99. goog.asserts.assert(goog.isDef(pattern), 'Pattern must be defined');
  100. goog.asserts.assert(
  101. goog.isDef(opt_dateTimeSymbols) || goog.isDef(goog.i18n.DateTimeSymbols),
  102. 'goog.i18n.DateTimeSymbols or explicit symbols must be defined');
  103. this.patternParts_ = [];
  104. /**
  105. * Data structure that with all the locale info needed for date formatting.
  106. * (day/month names, most common patterns, rules for week-end, etc.)
  107. * @private {!goog.i18n.DateTimeSymbolsType}
  108. */
  109. this.dateTimeSymbols_ = /** @type {!goog.i18n.DateTimeSymbolsType} */ (
  110. opt_dateTimeSymbols || goog.i18n.DateTimeSymbols);
  111. if (typeof pattern == 'number') {
  112. this.applyStandardPattern_(pattern);
  113. } else {
  114. this.applyPattern_(pattern);
  115. }
  116. };
  117. /**
  118. * Enum to identify predefined Date/Time format pattern.
  119. * @enum {number}
  120. */
  121. goog.i18n.DateTimeFormat.Format = {
  122. FULL_DATE: 0,
  123. LONG_DATE: 1,
  124. MEDIUM_DATE: 2,
  125. SHORT_DATE: 3,
  126. FULL_TIME: 4,
  127. LONG_TIME: 5,
  128. MEDIUM_TIME: 6,
  129. SHORT_TIME: 7,
  130. FULL_DATETIME: 8,
  131. LONG_DATETIME: 9,
  132. MEDIUM_DATETIME: 10,
  133. SHORT_DATETIME: 11
  134. };
  135. /**
  136. * regular expression pattern for parsing pattern string
  137. * @type {Array<RegExp>}
  138. * @private
  139. */
  140. goog.i18n.DateTimeFormat.TOKENS_ = [
  141. // quote string
  142. /^\'(?:[^\']|\'\')*(\'|$)/,
  143. // pattern chars
  144. /^(?:G+|y+|M+|k+|S+|E+|a+|h+|K+|H+|c+|L+|Q+|d+|m+|s+|v+|V+|w+|z+|Z+)/,
  145. // and all the other chars
  146. /^[^\'GyMkSEahKHcLQdmsvVwzZ]+/ // and all the other chars
  147. ];
  148. /**
  149. * These are token types, corresponding to above token definitions.
  150. * @enum {number}
  151. * @private
  152. */
  153. goog.i18n.DateTimeFormat.PartTypes_ = {
  154. QUOTED_STRING: 0,
  155. FIELD: 1,
  156. LITERAL: 2
  157. };
  158. /**
  159. * @param {!goog.date.DateLike} date
  160. * @return {number}
  161. * @private
  162. */
  163. goog.i18n.DateTimeFormat.getHours_ = function(date) {
  164. return date.getHours ? date.getHours() : 0;
  165. };
  166. /**
  167. * Apply specified pattern to this formatter object.
  168. * @param {string} pattern String specifying how the date should be formatted.
  169. * @private
  170. */
  171. goog.i18n.DateTimeFormat.prototype.applyPattern_ = function(pattern) {
  172. if (goog.i18n.DateTimeFormat.removeRlmInPatterns_) {
  173. // Remove RLM unicode control character from pattern.
  174. pattern = pattern.replace(/\u200f/g, '');
  175. }
  176. // lex the pattern, once for all uses
  177. while (pattern) {
  178. var previousPattern = pattern;
  179. for (var i = 0; i < goog.i18n.DateTimeFormat.TOKENS_.length; ++i) {
  180. var m = pattern.match(goog.i18n.DateTimeFormat.TOKENS_[i]);
  181. if (m) {
  182. var part = m[0];
  183. pattern = pattern.substring(part.length);
  184. if (i == goog.i18n.DateTimeFormat.PartTypes_.QUOTED_STRING) {
  185. if (part == "''") {
  186. part = "'"; // '' -> '
  187. } else {
  188. part = part.substring(
  189. 1,
  190. m[1] == '\'' ? part.length - 1 : part.length); // strip quotes
  191. part = part.replace(/\'\'/g, '\'');
  192. }
  193. }
  194. this.patternParts_.push({text: part, type: i});
  195. break;
  196. }
  197. }
  198. if (previousPattern === pattern) {
  199. // On every iteration, part of the pattern string must be consumed.
  200. throw new Error('Malformed pattern part: ' + pattern);
  201. }
  202. }
  203. };
  204. /**
  205. * Format the given date object according to preset pattern and current locale.
  206. * @param {goog.date.DateLike} date The Date object that is being formatted.
  207. * @param {goog.i18n.TimeZone=} opt_timeZone optional, if specified, time
  208. * related fields will be formatted based on its setting. When this field
  209. * is not specified, "undefined" will be pass around and those function
  210. * that really need time zone service will create a default one.
  211. * @return {string} Formatted string for the given date.
  212. * Throws an error if the date is null or if one tries to format a date-only
  213. * object (for instance goog.date.Date) using a pattern with time fields.
  214. */
  215. goog.i18n.DateTimeFormat.prototype.format = function(date, opt_timeZone) {
  216. if (!date) throw Error('The date to format must be non-null.');
  217. // We don't want to write code to calculate each date field because we
  218. // want to maximize performance and minimize code size.
  219. // JavaScript only provide API to render local time.
  220. // Suppose target date is: 16:00 GMT-0400
  221. // OS local time is: 12:00 GMT-0800
  222. // We want to create a Local Date Object : 16:00 GMT-0800, and fix the
  223. // time zone display ourselves.
  224. // Thing get a little bit tricky when daylight time transition happens. For
  225. // example, suppose OS timeZone is America/Los_Angeles, it is impossible to
  226. // represent "2006/4/2 02:30" even for those timeZone that has no transition
  227. // at this time. Because 2:00 to 3:00 on that day does not exist in
  228. // America/Los_Angeles time zone. To avoid calculating date field through
  229. // our own code, we uses 3 Date object instead, one for "Year, month, day",
  230. // one for time within that day, and one for timeZone object since it need
  231. // the real time to figure out actual time zone offset.
  232. var diff = opt_timeZone ?
  233. (date.getTimezoneOffset() - opt_timeZone.getOffset(date)) * 60000 :
  234. 0;
  235. var dateForDate = diff ? new Date(date.getTime() + diff) : date;
  236. var dateForTime = dateForDate;
  237. // When the time manipulation applied above spans the DST on/off hour, this
  238. // could alter the time incorrectly by adding or subtracting an additional
  239. // hour.
  240. // We can mitigate this by:
  241. // - Adding the difference in timezone offset to the date. This ensures that
  242. // the dateForDate is still within the right day if the extra DST hour
  243. // affected the date.
  244. // - Move the time one day forward if we applied a timezone offset backwards,
  245. // or vice versa. This trick ensures that the time is in the same offset
  246. // as the original date, so we remove the additional hour added or
  247. // subtracted by the DST switch.
  248. if (opt_timeZone &&
  249. dateForDate.getTimezoneOffset() != date.getTimezoneOffset()) {
  250. var dstDiff =
  251. (dateForDate.getTimezoneOffset() - date.getTimezoneOffset()) * 60000;
  252. dateForDate = new Date(dateForDate.getTime() + dstDiff);
  253. diff += diff > 0 ? -goog.date.MS_PER_DAY : goog.date.MS_PER_DAY;
  254. dateForTime = new Date(date.getTime() + diff);
  255. }
  256. var out = [];
  257. for (var i = 0; i < this.patternParts_.length; ++i) {
  258. var text = this.patternParts_[i].text;
  259. if (goog.i18n.DateTimeFormat.PartTypes_.FIELD ==
  260. this.patternParts_[i].type) {
  261. out.push(
  262. this.formatField_(
  263. text, date, dateForDate, dateForTime, opt_timeZone));
  264. } else {
  265. out.push(text);
  266. }
  267. }
  268. return out.join('');
  269. };
  270. /**
  271. * Apply a predefined pattern as identified by formatType, which is stored in
  272. * locale specific repository.
  273. * @param {number} formatType A number that identified the predefined pattern.
  274. * @private
  275. */
  276. goog.i18n.DateTimeFormat.prototype.applyStandardPattern_ = function(
  277. formatType) {
  278. var pattern;
  279. if (formatType < 4) {
  280. pattern = this.dateTimeSymbols_.DATEFORMATS[formatType];
  281. } else if (formatType < 8) {
  282. pattern = this.dateTimeSymbols_.TIMEFORMATS[formatType - 4];
  283. } else if (formatType < 12) {
  284. pattern = this.dateTimeSymbols_.DATETIMEFORMATS[formatType - 8];
  285. pattern = pattern.replace(
  286. '{1}', this.dateTimeSymbols_.DATEFORMATS[formatType - 8]);
  287. pattern = pattern.replace(
  288. '{0}', this.dateTimeSymbols_.TIMEFORMATS[formatType - 8]);
  289. } else {
  290. this.applyStandardPattern_(goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
  291. return;
  292. }
  293. this.applyPattern_(pattern);
  294. };
  295. /**
  296. * Localizes a string potentially containing numbers, replacing ASCII digits
  297. * with native digits if specified so by the locale. Leaves other characters.
  298. * @param {string} input the string to be localized, using ASCII digits.
  299. * @return {string} localized string, potentially using native digits.
  300. * @private
  301. */
  302. goog.i18n.DateTimeFormat.prototype.localizeNumbers_ = function(input) {
  303. return goog.i18n.DateTimeFormat.localizeNumbers(input, this.dateTimeSymbols_);
  304. };
  305. /**
  306. * If the usage of Ascii digits should be enforced regardless of locale.
  307. * @type {boolean}
  308. * @private
  309. */
  310. goog.i18n.DateTimeFormat.enforceAsciiDigits_ = false;
  311. /**
  312. * If RLM unicode characters should be removed from date/time patterns (useful
  313. * when enforcing ASCII digits for Arabic). See {@code #setEnforceAsciiDigits}.
  314. * @type {boolean}
  315. * @private
  316. */
  317. goog.i18n.DateTimeFormat.removeRlmInPatterns_ = false;
  318. /**
  319. * Sets if the usage of Ascii digits in formatting should be enforced in
  320. * formatted date/time even for locales where native digits are indicated.
  321. * Also sets whether to remove RLM unicode control characters when using
  322. * standard enumerated patterns (they exist e.g. in standard d/M/y for Arabic).
  323. * Production code should call this once before any {@code DateTimeFormat}
  324. * object is instantiated.
  325. * Caveats:
  326. * * Enforcing ASCII digits affects all future formatting by new or existing
  327. * {@code DateTimeFormat} objects.
  328. * * Removal of RLM characters only applies to {@code DateTimeFormat} objects
  329. * instantiated after this call.
  330. * @param {boolean} enforceAsciiDigits Whether Ascii digits should be enforced.
  331. */
  332. goog.i18n.DateTimeFormat.setEnforceAsciiDigits = function(enforceAsciiDigits) {
  333. goog.i18n.DateTimeFormat.enforceAsciiDigits_ = enforceAsciiDigits;
  334. // Also setting removal of RLM chracters when forcing ASCII digits since it's
  335. // the right thing to do for Arabic standard patterns. One could add an
  336. // optional argument here or to the {@code DateTimeFormat} constructor to
  337. // enable an alternative behavior.
  338. goog.i18n.DateTimeFormat.removeRlmInPatterns_ = enforceAsciiDigits;
  339. };
  340. /**
  341. * @return {boolean} Whether enforcing ASCII digits for all locales. See
  342. * {@code #setEnforceAsciiDigits} for more details.
  343. */
  344. goog.i18n.DateTimeFormat.isEnforceAsciiDigits = function() {
  345. return goog.i18n.DateTimeFormat.enforceAsciiDigits_;
  346. };
  347. /**
  348. * Localizes a string potentially containing numbers, replacing ASCII digits
  349. * with native digits if specified so by the locale. Leaves other characters.
  350. * @param {number|string} input the string to be localized, using ASCII digits.
  351. * @param {!Object=} opt_dateTimeSymbols Optional symbols to use rather than
  352. * the global symbols.
  353. * @return {string} localized string, potentially using native digits.
  354. */
  355. goog.i18n.DateTimeFormat.localizeNumbers = function(
  356. input, opt_dateTimeSymbols) {
  357. input = String(input);
  358. var dateTimeSymbols = opt_dateTimeSymbols || goog.i18n.DateTimeSymbols;
  359. if (dateTimeSymbols.ZERODIGIT === undefined ||
  360. goog.i18n.DateTimeFormat.enforceAsciiDigits_) {
  361. return input;
  362. }
  363. var parts = [];
  364. for (var i = 0; i < input.length; i++) {
  365. var c = input.charCodeAt(i);
  366. parts.push(
  367. (0x30 <= c && c <= 0x39) ? // '0' <= c <= '9'
  368. String.fromCharCode(dateTimeSymbols.ZERODIGIT + c - 0x30) :
  369. input.charAt(i));
  370. }
  371. return parts.join('');
  372. };
  373. /**
  374. * Formats Era field according to pattern specified.
  375. *
  376. * @param {number} count Number of time pattern char repeats, it controls
  377. * how a field should be formatted.
  378. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  379. * @return {string} Formatted string that represent this field.
  380. * @private
  381. */
  382. goog.i18n.DateTimeFormat.prototype.formatEra_ = function(count, date) {
  383. var value = date.getFullYear() > 0 ? 1 : 0;
  384. return count >= 4 ? this.dateTimeSymbols_.ERANAMES[value] :
  385. this.dateTimeSymbols_.ERAS[value];
  386. };
  387. /**
  388. * Formats Year field according to pattern specified
  389. * Javascript Date object seems incapable handling 1BC and
  390. * year before. It can show you year 0 which does not exists.
  391. * following we just keep consistent with javascript's
  392. * toString method. But keep in mind those things should be
  393. * unsupported.
  394. * @param {number} count Number of time pattern char repeats, it controls
  395. * how a field should be formatted.
  396. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  397. * @return {string} Formatted string that represent this field.
  398. * @private
  399. */
  400. goog.i18n.DateTimeFormat.prototype.formatYear_ = function(count, date) {
  401. var value = date.getFullYear();
  402. if (value < 0) {
  403. value = -value;
  404. }
  405. if (count == 2) {
  406. // See comment about special casing 'yy' at the start of the file, this
  407. // matches ICU and CLDR behaviour. See also:
  408. // http://icu-project.org/apiref/icu4j/com/ibm/icu/text/SimpleDateFormat.html
  409. // http://www.unicode.org/reports/tr35/tr35-dates.html
  410. value = value % 100;
  411. }
  412. return this.localizeNumbers_(goog.string.padNumber(value, count));
  413. };
  414. /**
  415. * Formats Month field according to pattern specified
  416. *
  417. * @param {number} count Number of time pattern char repeats, it controls
  418. * how a field should be formatted.
  419. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  420. * @return {string} Formatted string that represent this field.
  421. * @private
  422. */
  423. goog.i18n.DateTimeFormat.prototype.formatMonth_ = function(count, date) {
  424. var value = date.getMonth();
  425. switch (count) {
  426. case 5:
  427. return this.dateTimeSymbols_.NARROWMONTHS[value];
  428. case 4:
  429. return this.dateTimeSymbols_.MONTHS[value];
  430. case 3:
  431. return this.dateTimeSymbols_.SHORTMONTHS[value];
  432. default:
  433. return this.localizeNumbers_(goog.string.padNumber(value + 1, count));
  434. }
  435. };
  436. /**
  437. * Validates is the goog.date.DateLike object to format has a time.
  438. * DateLike means Date|goog.date.Date, and goog.date.DateTime inherits
  439. * from goog.date.Date. But goog.date.Date does not have time related
  440. * members (getHours, getMinutes, getSeconds).
  441. * Formatting can be done, if there are no time placeholders in the pattern.
  442. *
  443. * @param {!goog.date.DateLike} date the object to validate.
  444. * @private
  445. */
  446. goog.i18n.DateTimeFormat.validateDateHasTime_ = function(date) {
  447. if (date.getHours && date.getSeconds && date.getMinutes) return;
  448. // if (date instanceof Date || date instanceof goog.date.DateTime)
  449. throw Error(
  450. 'The date to format has no time (probably a goog.date.Date). ' +
  451. 'Use Date or goog.date.DateTime, or use a pattern without time fields.');
  452. };
  453. /**
  454. * Formats (1..24) Hours field according to pattern specified
  455. *
  456. * @param {number} count Number of time pattern char repeats. This controls
  457. * how a field should be formatted.
  458. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  459. * @return {string} Formatted string that represent this field.
  460. * @private
  461. */
  462. goog.i18n.DateTimeFormat.prototype.format24Hours_ = function(count, date) {
  463. goog.i18n.DateTimeFormat.validateDateHasTime_(date);
  464. var hours = goog.i18n.DateTimeFormat.getHours_(date) || 24;
  465. return this.localizeNumbers_(goog.string.padNumber(hours, count));
  466. };
  467. /**
  468. * Formats Fractional seconds field according to pattern
  469. * specified
  470. *
  471. * @param {number} count Number of time pattern char repeats, it controls
  472. * how a field should be formatted.
  473. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  474. *
  475. * @return {string} Formatted string that represent this field.
  476. * @private
  477. */
  478. goog.i18n.DateTimeFormat.prototype.formatFractionalSeconds_ = function(
  479. count, date) {
  480. // Fractional seconds left-justify, append 0 for precision beyond 3
  481. var value = date.getTime() % 1000 / 1000;
  482. return this.localizeNumbers_(
  483. value.toFixed(Math.min(3, count)).substr(2) +
  484. (count > 3 ? goog.string.padNumber(0, count - 3) : ''));
  485. };
  486. /**
  487. * Formats Day of week field according to pattern specified
  488. *
  489. * @param {number} count Number of time pattern char repeats, it controls
  490. * how a field should be formatted.
  491. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  492. * @return {string} Formatted string that represent this field.
  493. * @private
  494. */
  495. goog.i18n.DateTimeFormat.prototype.formatDayOfWeek_ = function(count, date) {
  496. var value = date.getDay();
  497. return count >= 4 ? this.dateTimeSymbols_.WEEKDAYS[value] :
  498. this.dateTimeSymbols_.SHORTWEEKDAYS[value];
  499. };
  500. /**
  501. * Formats Am/Pm field according to pattern specified
  502. *
  503. * @param {number} count Number of time pattern char repeats, it controls
  504. * how a field should be formatted.
  505. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  506. * @return {string} Formatted string that represent this field.
  507. * @private
  508. */
  509. goog.i18n.DateTimeFormat.prototype.formatAmPm_ = function(count, date) {
  510. goog.i18n.DateTimeFormat.validateDateHasTime_(date);
  511. var hours = goog.i18n.DateTimeFormat.getHours_(date);
  512. return this.dateTimeSymbols_.AMPMS[hours >= 12 && hours < 24 ? 1 : 0];
  513. };
  514. /**
  515. * Formats (1..12) Hours field according to pattern specified
  516. *
  517. * @param {number} count Number of time pattern char repeats, it controls
  518. * how a field should be formatted.
  519. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  520. * @return {string} formatted string that represent this field.
  521. * @private
  522. */
  523. goog.i18n.DateTimeFormat.prototype.format1To12Hours_ = function(count, date) {
  524. goog.i18n.DateTimeFormat.validateDateHasTime_(date);
  525. var hours = goog.i18n.DateTimeFormat.getHours_(date) % 12 || 12;
  526. return this.localizeNumbers_(goog.string.padNumber(hours, count));
  527. };
  528. /**
  529. * Formats (0..11) Hours field according to pattern specified
  530. *
  531. * @param {number} count Number of time pattern char repeats, it controls
  532. * how a field should be formatted.
  533. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  534. * @return {string} formatted string that represent this field.
  535. * @private
  536. */
  537. goog.i18n.DateTimeFormat.prototype.format0To11Hours_ = function(count, date) {
  538. goog.i18n.DateTimeFormat.validateDateHasTime_(date);
  539. var hours = goog.i18n.DateTimeFormat.getHours_(date) % 12;
  540. return this.localizeNumbers_(goog.string.padNumber(hours, count));
  541. };
  542. /**
  543. * Formats (0..23) Hours field according to pattern specified
  544. *
  545. * @param {number} count Number of time pattern char repeats, it controls
  546. * how a field should be formatted.
  547. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  548. * @return {string} formatted string that represent this field.
  549. * @private
  550. */
  551. goog.i18n.DateTimeFormat.prototype.format0To23Hours_ = function(count, date) {
  552. goog.i18n.DateTimeFormat.validateDateHasTime_(date);
  553. var hours = goog.i18n.DateTimeFormat.getHours_(date);
  554. return this.localizeNumbers_(goog.string.padNumber(hours, count));
  555. };
  556. /**
  557. * Formats Standalone weekday field according to pattern specified
  558. *
  559. * @param {number} count Number of time pattern char repeats, it controls
  560. * how a field should be formatted.
  561. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  562. * @return {string} formatted string that represent this field.
  563. * @private
  564. */
  565. goog.i18n.DateTimeFormat.prototype.formatStandaloneDay_ = function(
  566. count, date) {
  567. var value = date.getDay();
  568. switch (count) {
  569. case 5:
  570. return this.dateTimeSymbols_.STANDALONENARROWWEEKDAYS[value];
  571. case 4:
  572. return this.dateTimeSymbols_.STANDALONEWEEKDAYS[value];
  573. case 3:
  574. return this.dateTimeSymbols_.STANDALONESHORTWEEKDAYS[value];
  575. default:
  576. return this.localizeNumbers_(goog.string.padNumber(value, 1));
  577. }
  578. };
  579. /**
  580. * Formats Standalone Month field according to pattern specified
  581. *
  582. * @param {number} count Number of time pattern char repeats, it controls
  583. * how a field should be formatted.
  584. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  585. * @return {string} formatted string that represent this field.
  586. * @private
  587. */
  588. goog.i18n.DateTimeFormat.prototype.formatStandaloneMonth_ = function(
  589. count, date) {
  590. var value = date.getMonth();
  591. switch (count) {
  592. case 5:
  593. return this.dateTimeSymbols_.STANDALONENARROWMONTHS[value];
  594. case 4:
  595. return this.dateTimeSymbols_.STANDALONEMONTHS[value];
  596. case 3:
  597. return this.dateTimeSymbols_.STANDALONESHORTMONTHS[value];
  598. default:
  599. return this.localizeNumbers_(goog.string.padNumber(value + 1, count));
  600. }
  601. };
  602. /**
  603. * Formats Quarter field according to pattern specified
  604. *
  605. * @param {number} count Number of time pattern char repeats, it controls
  606. * how a field should be formatted.
  607. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  608. * @return {string} Formatted string that represent this field.
  609. * @private
  610. */
  611. goog.i18n.DateTimeFormat.prototype.formatQuarter_ = function(count, date) {
  612. var value = Math.floor(date.getMonth() / 3);
  613. return count < 4 ? this.dateTimeSymbols_.SHORTQUARTERS[value] :
  614. this.dateTimeSymbols_.QUARTERS[value];
  615. };
  616. /**
  617. * Formats Date field according to pattern specified
  618. *
  619. * @param {number} count Number of time pattern char repeats, it controls
  620. * how a field should be formatted.
  621. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  622. * @return {string} Formatted string that represent this field.
  623. * @private
  624. */
  625. goog.i18n.DateTimeFormat.prototype.formatDate_ = function(count, date) {
  626. return this.localizeNumbers_(goog.string.padNumber(date.getDate(), count));
  627. };
  628. /**
  629. * Formats Minutes field according to pattern specified
  630. *
  631. * @param {number} count Number of time pattern char repeats, it controls
  632. * how a field should be formatted.
  633. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  634. * @return {string} Formatted string that represent this field.
  635. * @private
  636. */
  637. goog.i18n.DateTimeFormat.prototype.formatMinutes_ = function(count, date) {
  638. goog.i18n.DateTimeFormat.validateDateHasTime_(date);
  639. return this.localizeNumbers_(
  640. goog.string.padNumber(
  641. /** @type {!goog.date.DateTime} */ (date).getMinutes(), count));
  642. };
  643. /**
  644. * Formats Seconds field according to pattern specified
  645. *
  646. * @param {number} count Number of time pattern char repeats, it controls
  647. * how a field should be formatted.
  648. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  649. * @return {string} Formatted string that represent this field.
  650. * @private
  651. */
  652. goog.i18n.DateTimeFormat.prototype.formatSeconds_ = function(count, date) {
  653. goog.i18n.DateTimeFormat.validateDateHasTime_(date);
  654. return this.localizeNumbers_(
  655. goog.string.padNumber(
  656. /** @type {!goog.date.DateTime} */ (date).getSeconds(), count));
  657. };
  658. /**
  659. * Formats the week of year field according to pattern specified
  660. *
  661. * @param {number} count Number of time pattern char repeats, it controls
  662. * how a field should be formatted.
  663. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  664. * @return {string} Formatted string that represent this field.
  665. * @private
  666. */
  667. goog.i18n.DateTimeFormat.prototype.formatWeekOfYear_ = function(count, date) {
  668. var weekNum = goog.date.getWeekNumber(
  669. date.getFullYear(), date.getMonth(), date.getDate(),
  670. this.dateTimeSymbols_.FIRSTWEEKCUTOFFDAY,
  671. this.dateTimeSymbols_.FIRSTDAYOFWEEK);
  672. return this.localizeNumbers_(goog.string.padNumber(weekNum, count));
  673. };
  674. /**
  675. * Formats TimeZone field following RFC
  676. *
  677. * @param {number} count Number of time pattern char repeats, it controls
  678. * how a field should be formatted.
  679. * @param {!goog.date.DateLike} date It holds the date object to be formatted.
  680. * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
  681. * @return {string} Formatted string that represent this field.
  682. * @private
  683. */
  684. goog.i18n.DateTimeFormat.prototype.formatTimeZoneRFC_ = function(
  685. count, date, opt_timeZone) {
  686. opt_timeZone = opt_timeZone ||
  687. goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
  688. // RFC 822 formats should be kept in ASCII, but localized GMT formats may need
  689. // to use native digits.
  690. return count < 4 ? opt_timeZone.getRFCTimeZoneString(date) :
  691. this.localizeNumbers_(opt_timeZone.getGMTString(date));
  692. };
  693. /**
  694. * Generate GMT timeZone string for given date
  695. * @param {number} count Number of time pattern char repeats, it controls
  696. * how a field should be formatted.
  697. * @param {!goog.date.DateLike} date Whose value being evaluated.
  698. * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
  699. * @return {string} GMT timeZone string.
  700. * @private
  701. */
  702. goog.i18n.DateTimeFormat.prototype.formatTimeZone_ = function(
  703. count, date, opt_timeZone) {
  704. opt_timeZone = opt_timeZone ||
  705. goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
  706. return count < 4 ? opt_timeZone.getShortName(date) :
  707. opt_timeZone.getLongName(date);
  708. };
  709. /**
  710. * Generate GMT timeZone string for given date
  711. * @param {!goog.date.DateLike} date Whose value being evaluated.
  712. * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
  713. * @return {string} GMT timeZone string.
  714. * @private
  715. */
  716. goog.i18n.DateTimeFormat.prototype.formatTimeZoneId_ = function(
  717. date, opt_timeZone) {
  718. opt_timeZone = opt_timeZone ||
  719. goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
  720. return opt_timeZone.getTimeZoneId();
  721. };
  722. /**
  723. * Generate localized, location dependent time zone id
  724. * @param {number} count Number of time pattern char repeats, it controls
  725. * how a field should be formatted.
  726. * @param {!goog.date.DateLike} date Whose value being evaluated.
  727. * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
  728. * @return {string} GMT timeZone string.
  729. * @private
  730. */
  731. goog.i18n.DateTimeFormat.prototype.formatTimeZoneLocationId_ = function(
  732. count, date, opt_timeZone) {
  733. opt_timeZone = opt_timeZone ||
  734. goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
  735. return count <= 2 ? opt_timeZone.getTimeZoneId() :
  736. opt_timeZone.getGenericLocation(date);
  737. };
  738. /**
  739. * Formatting one date field.
  740. * @param {string} patternStr The pattern string for the field being formatted.
  741. * @param {!goog.date.DateLike} date represents the real date to be formatted.
  742. * @param {!goog.date.DateLike} dateForDate used to resolve date fields
  743. * for formatting.
  744. * @param {!goog.date.DateLike} dateForTime used to resolve time fields
  745. * for formatting.
  746. * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
  747. * @return {string} string representation for the given field.
  748. * @private
  749. */
  750. goog.i18n.DateTimeFormat.prototype.formatField_ = function(
  751. patternStr, date, dateForDate, dateForTime, opt_timeZone) {
  752. var count = patternStr.length;
  753. switch (patternStr.charAt(0)) {
  754. case 'G':
  755. return this.formatEra_(count, dateForDate);
  756. case 'y':
  757. return this.formatYear_(count, dateForDate);
  758. case 'M':
  759. return this.formatMonth_(count, dateForDate);
  760. case 'k':
  761. return this.format24Hours_(count, dateForTime);
  762. case 'S':
  763. return this.formatFractionalSeconds_(count, dateForTime);
  764. case 'E':
  765. return this.formatDayOfWeek_(count, dateForDate);
  766. case 'a':
  767. return this.formatAmPm_(count, dateForTime);
  768. case 'h':
  769. return this.format1To12Hours_(count, dateForTime);
  770. case 'K':
  771. return this.format0To11Hours_(count, dateForTime);
  772. case 'H':
  773. return this.format0To23Hours_(count, dateForTime);
  774. case 'c':
  775. return this.formatStandaloneDay_(count, dateForDate);
  776. case 'L':
  777. return this.formatStandaloneMonth_(count, dateForDate);
  778. case 'Q':
  779. return this.formatQuarter_(count, dateForDate);
  780. case 'd':
  781. return this.formatDate_(count, dateForDate);
  782. case 'm':
  783. return this.formatMinutes_(count, dateForTime);
  784. case 's':
  785. return this.formatSeconds_(count, dateForTime);
  786. case 'v':
  787. return this.formatTimeZoneId_(date, opt_timeZone);
  788. case 'V':
  789. return this.formatTimeZoneLocationId_(count, date, opt_timeZone);
  790. case 'w':
  791. return this.formatWeekOfYear_(count, dateForTime);
  792. case 'z':
  793. return this.formatTimeZone_(count, date, opt_timeZone);
  794. case 'Z':
  795. return this.formatTimeZoneRFC_(count, date, opt_timeZone);
  796. default:
  797. return '';
  798. }
  799. };