Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: intl/lib/src/intl/date_format.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « intl/lib/src/intl/bidi_utils.dart ('k') | intl/lib/src/intl/date_format_field.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of intl;
6
7 // TODO(efortuna): Customized pattern system -- suggested by i18n needs
8 // feedback on appropriateness.
9 /**
10 * DateFormat is for formatting and parsing dates in a locale-sensitive
11 * manner.
12 *
13 * It allows the user to choose from a set of standard date time formats as well
14 * as specify a customized pattern under certain locales. Date elements that
15 * vary across locales include month name, week name, field order, etc.
16 * We also allow the user to use any customized pattern to parse or format
17 * date-time strings under certain locales. Date elements that vary across
18 * locales include month name, weekname, field, order, etc.
19 *
20 * Formatting dates in the default "en_US" format does not require any
21 * initialization. e.g.
22 * print(new DateFormat.yMMMd().format(new Date.now()));
23 *
24 * But for other locales, the formatting data for the locale must be
25 * obtained. This can currently be done
26 * in one of three ways, determined by which library you import. In all cases,
27 * the "initializeDateFormatting" method must be called and will return a future
28 * that is complete once the locale data is available. The result of the future
29 * isn't important, but the data for that locale is available to the date
30 * formatting and parsing once it completes.
31 *
32 * The easiest option is that the data may be available locally, imported in a
33 * library that contains data for all the locales.
34 * import 'package:intl/date_symbol_data_local.dart';
35 * initializeDateFormatting("fr_FR", null).then((_) => runMyCode());
36 *
37 * If we are running outside of a browser, we may want to read the data
38 * from files in the file system.
39 * import 'package:intl/date_symbol_data_file.dart';
40 * initializeDateFormatting("de_DE", null).then((_) => runMyCode());
41 *
42 * If we are running in a browser, we may want to read the data from the
43 * server using the XmlHttpRequest mechanism.
44 * import 'package:intl/date_symbol_data_http_request.dart';
45 * initializeDateFormatting("pt_BR", null).then((_) => runMyCode());
46 *
47 * The code in example/basic/basic_example.dart shows a full example of
48 * using this mechanism.
49 *
50 * Once we have the locale data, we need to specify the particular format.
51 * This library uses the ICU/JDK date/time pattern specification both for
52 * complete format specifications and also the abbreviated "skeleton" form
53 * which can also adapt to different locales and is preferred where available.
54 *
55 * Skeletons: These can be specified either as the ICU constant name or as the
56 * skeleton to which it resolves. The supported set of skeletons is as follows.
57 * For each skeleton there is a named constructor that can be used to create it.
58 * It's also possible to pass the skeleton as a string, but the constructor
59 * is preferred.
60 *
61 * ICU Name Skeleton
62 * -------- --------
63 * DAY d
64 * ABBR_WEEKDAY E
65 * WEEKDAY EEEE
66 * ABBR_STANDALONE_MONTH LLL
67 * STANDALONE_MONTH LLLL
68 * NUM_MONTH M
69 * NUM_MONTH_DAY Md
70 * NUM_MONTH_WEEKDAY_DAY MEd
71 * ABBR_MONTH MMM
72 * ABBR_MONTH_DAY MMMd
73 * ABBR_MONTH_WEEKDAY_DAY MMMEd
74 * MONTH MMMM
75 * MONTH_DAY MMMMd
76 * MONTH_WEEKDAY_DAY MMMMEEEEd
77 * ABBR_QUARTER QQQ
78 * QUARTER QQQQ
79 * YEAR y
80 * YEAR_NUM_MONTH yM
81 * YEAR_NUM_MONTH_DAY yMd
82 * YEAR_NUM_MONTH_WEEKDAY_DAY yMEd
83 * YEAR_ABBR_MONTH yMMM
84 * YEAR_ABBR_MONTH_DAY yMMMd
85 * YEAR_ABBR_MONTH_WEEKDAY_DAY yMMMEd
86 * YEAR_MONTH yMMMM
87 * YEAR_MONTH_DAY yMMMMd
88 * YEAR_MONTH_WEEKDAY_DAY yMMMMEEEEd
89 * YEAR_ABBR_QUARTER yQQQ
90 * YEAR_QUARTER yQQQQ
91 * HOUR24 H
92 * HOUR24_MINUTE Hm
93 * HOUR24_MINUTE_SECOND Hms
94 * HOUR j
95 * HOUR_MINUTE jm
96 * HOUR_MINUTE_SECOND jms
97 * HOUR_MINUTE_GENERIC_TZ jmv
98 * HOUR_MINUTE_TZ jmz
99 * HOUR_GENERIC_TZ jv
100 * HOUR_TZ jz
101 * MINUTE m
102 * MINUTE_SECOND ms
103 * SECOND s
104 *
105 * Examples Using the US Locale:
106 *
107 * Pattern Result
108 * ---------------- -------
109 * new DateFormat.yMd() -> 7/10/1996
110 * new DateFormat("yMd") -> 7/10/1996
111 * new DateFormat.yMMMMd("en_US") -> July 10, 1996
112 * new DateFormat.jm() -> 5:08 PM
113 * new DateFormat.yMd().add_jm() -> 7/10/1996 5:08 PM
114 * new DateFormat.Hm() -> 17:08 // force 24 hour time
115 *
116 * Explicit Pattern Syntax: Formats can also be specified with a pattern string.
117 * This can be used for formats that don't have a skeleton available, but these
118 * will not adapt to different locales. For example, in an explicit pattern the
119 * letters "H" and "h" are available for 24 hour and 12 hour time formats
120 * respectively. But there isn't a way in an explicit pattern to get the
121 * behaviour of the "j" skeleton, which prints 24 hour or 12 hour time according
122 * to the conventions of the locale, and also includes am/pm markers where
123 * appropriate. So it is preferable to use the skeletons.
124 *
125 * The following characters are available in explicit patterns:
126 *
127 * Symbol Meaning Presentation Example
128 * ------ ------- ------------ -------
129 * G era designator (Text) AD
130 * y year (Number) 1996
131 * M month in year (Text & Number) July & 07
132 * L standalone month (Text & Number) July & 07
133 * d day in month (Number) 10
134 * c standalone day (Number) 10
135 * h hour in am/pm (1~12) (Number) 12
136 * H hour in day (0~23) (Number) 0
137 * m minute in hour (Number) 30
138 * s second in minute (Number) 55
139 * S fractional second (Number) 978
140 * E day of week (Text) Tuesday
141 * D day in year (Number) 189
142 * a am/pm marker (Text) PM
143 * k hour in day (1~24) (Number) 24
144 * K hour in am/pm (0~11) (Number) 0
145 * z time zone (Text) Pacific Standard Time
146 * Z time zone (RFC 822) (Number) -0800
147 * v time zone (generic) (Text) Pacific Time
148 * Q quarter (Text) Q3
149 * ' escape for text (Delimiter) 'Date='
150 * '' single quote (Literal) 'o''clock'
151 *
152 * The count of pattern letters determine the format.
153 *
154 * **Text**:
155 * * 5 pattern letters--use narrow form for standalone. Otherwise does not apply
156 * * 4 or more pattern letters--use full form,
157 * * 3 pattern letters--use short or abbreviated form if one exists
158 * * less than 3--use numeric form if one exists
159 *
160 * **Number**: the minimum number of digits. Shorter numbers are zero-padded to
161 * this amount (e.g. if "m" produces "6", "mm" produces "06"). Year is handled
162 * specially; that is, if the count of 'y' is 2, the Year will be truncated to
163 * 2 digits. (e.g., if "yyyy" produces "1997", "yy" produces "97".) Unlike other
164 * fields, fractional seconds are padded on the right with zero.
165 *
166 * **(Text & Number)**: 3 or over, use text, otherwise use number.
167 *
168 * Any characters not in the pattern will be treated as quoted text. For
169 * instance, characters like ':', '.', ' ', '#' and '@' will appear in the
170 * resulting text even though they are not enclosed in single quotes. In our
171 * current pattern usage, not all letters have meanings. But those unused
172 * letters are strongly discouraged to be used as quoted text without quotes,
173 * because we may use other letters as pattern characters in the future.
174 *
175 * Examples Using the US Locale:
176 *
177 * Format Pattern Result
178 * -------------- -------
179 * "yyyy.MM.dd G 'at' HH:mm:ss vvvv" 1996.07.10 AD at 15:08:56 Pacific Time
180 * "EEE, MMM d, ''yy" Wed, July 10, '96
181 * "h:mm a" 12:08 PM
182 * "hh 'o''clock' a, zzzz" 12 o'clock PM, Pacific Daylight Time
183 * "K:mm a, vvv" 0:00 PM, PT
184 * "yyyyy.MMMMM.dd GGG hh:mm aaa" 01996.July.10 AD 12:08 PM
185 *
186 * When parsing a date string using the abbreviated year pattern ("yy"),
187 * DateFormat must interpret the abbreviated year relative to some
188 * century. It does this by adjusting dates to be within 80 years before and 20
189 * years after the time the parse function is called. For example, using a
190 * pattern of "MM/dd/yy" and a DateParse instance created on Jan 1, 1997,
191 * the string "01/11/12" would be interpreted as Jan 11, 2012 while the string
192 * "05/04/64" would be interpreted as May 4, 1964. During parsing, only
193 * strings consisting of exactly two digits, as defined by {@link
194 * java.lang.Character#isDigit(char)}, will be parsed into the default
195 * century. Any other numeric string, such as a one digit string, a three or
196 * more digit string will be interpreted as its face value.
197 *
198 * If the year pattern does not have exactly two 'y' characters, the year is
199 * interpreted literally, regardless of the number of digits. So using the
200 * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
201 */
202
203 class DateFormat {
204
205 /**
206 * Creates a new DateFormat, using the format specified by [newPattern]. For
207 * forms that match one of our predefined skeletons, we look up the
208 * corresponding pattern in [locale] (or in the default locale if none is
209 * specified) and use the resulting full format string. This is the
210 * preferred usage, but if [newPattern] does not match one of the skeletons,
211 * then it is used as a format directly, but will not be adapted to suit
212 * the locale.
213 *
214 * For example, in an en_US locale, specifying the skeleton
215 * new DateFormat.yMEd();
216 * or the explicit
217 * new DateFormat('EEE, M/d/y');
218 * would produce the same result, a date of the form
219 * Wed, 6/27/2012
220 * The first version would produce a different format string if used in
221 * another locale, but the second format would always be the same.
222 *
223 * If [locale] does not exist in our set of supported locales then an
224 * [ArgumentError] is thrown.
225 */
226 DateFormat([String newPattern, String locale]) {
227 // TODO(alanknight): It should be possible to specify multiple skeletons eg
228 // date, time, timezone all separately. Adding many or named parameters to
229 // the constructor seems awkward, especially with the possibility of
230 // confusion with the locale. A "fluent" interface with cascading on an
231 // instance might work better? A list of patterns is also possible.
232 _locale = Intl.verifiedLocale(locale, localeExists);
233 addPattern(newPattern);
234 }
235
236 /**
237 * Return a string representing [date] formatted according to our locale
238 * and internal format.
239 */
240 String format(DateTime date) {
241 // TODO(efortuna): read optional TimeZone argument (or similar)?
242 var result = new StringBuffer();
243 _formatFields.forEach((field) => result.write(field.format(date)));
244 return result.toString();
245 }
246
247 /**
248 * NOT YET IMPLEMENTED.
249 *
250 * Returns a date string indicating how long ago (3 hours, 2 minutes)
251 * something has happened or how long in the future something will happen
252 * given a [reference] DateTime relative to the current time.
253 */
254 String formatDuration(DateTime reference) => '';
255
256 /**
257 * NOT YET IMPLEMENTED.
258 *
259 * Formats a string indicating how long ago (negative [duration]) or how far
260 * in the future (positive [duration]) some time is with respect to a
261 * reference [date].
262 */
263 String formatDurationFrom(Duration duration, DateTime date) => '';
264
265 /**
266 * Given user input, attempt to parse the [inputString] into the anticipated
267 * format, treating it as being in the local timezone. If [inputString] does
268 * not match our format, throws a [FormatException]. This will accept dates
269 * whose values are not strictly valid, or strings with additional characters
270 * (including whitespace) after a valid date. For stricter parsing, use
271 * [parseStrict].
272 */
273 DateTime parse(String inputString, [utc = false]) =>
274 _parse(inputString, utc: utc, strict: false);
275
276 /**
277 * Given user input, attempt to parse the [inputString] "loosely" into the
278 * anticipated format, accepting some variations from the strict format.
279 *
280 * If [inputString]
281 * is accepted by [parseStrict], just return the result. If not, attempt to
282 * parse it, but accepting either upper or
283 * lower case, allowing delimiters to be missing and replaced or
284 * supplemented with whitespace,
285 * and allowing arbitrary amounts of whitespace wherever whitespace is
286 * permitted. Note that this does not allow trailing characters, the way
287 * [parse] does. It also does not allow leading whitespace on delimiters,
288 * and does not allow alternative names for months or weekdays other than
289 * those the format knows about. The restrictions are quite arbitrary and
290 * it's not known how well they'll work for locales that aren't English-like.
291 *
292 * If [inputString] does not parse, this throws a
293 * [FormatException].
294 *
295 * For example, this will accept
296 *
297 * new DateFormat.yMMMd("en_US").parseLoose("SEp 3 2014");
298 * new DateFormat.yMd("en_US").parseLoose("09 03/2014");
299 *
300 * It will NOT accept
301 *
302 * // "Sept" is not a valid month name.
303 * new DateFormat.yMMMd("en_US").parseLoose("Sept 3, 2014");
304 * // Delimiters can't have leading whitespace.
305 * new DateFormat.yMd("en_US").parseLoose("09 / 03 / 2014");
306 */
307 DateTime parseLoose(String inputString, [utc = false]) {
308 try {
309 return _parse(inputString, utc: utc, strict: true);
310 } on FormatException {
311 return _parseLoose(inputString.toLowerCase(), utc);
312 }
313 }
314
315 _parseLoose(String inputString, bool utc) {
316 var dateFields = new _DateBuilder();
317 if (utc) dateFields.utc = true;
318 var stream = new _Stream(inputString);
319 _formatFields.forEach((f) => f.parseLoose(stream, dateFields));
320 if (!stream.atEnd()) {
321 throw new FormatException(
322 "Characters remaining after date parsing in $inputString");
323 }
324 dateFields.verify(inputString);
325 return dateFields.asDate();
326 }
327
328 /**
329 * Given user input, attempt to parse the [inputString] into the anticipated
330 * format, treating it as being in the local timezone. If [inputString] does
331 * not match our format, throws a [FormatException]. This will reject dates
332 * whose values are not strictly valid, even if the
333 * DateTime constructor will accept them. It will also rejct strings with
334 * additional characters (including whitespace) after a valid date. For
335 * looser parsing, use [parse].
336 */
337 DateTime parseStrict(String inputString, [utc = false]) =>
338 _parse(inputString, utc: utc, strict: true);
339
340 DateTime _parse(String inputString, {utc: false, strict: false}) {
341 // TODO(alanknight): The Closure code refers to special parsing of numeric
342 // values with no delimiters, which we currently don't do. Should we?
343 var dateFields = new _DateBuilder();
344 if (utc) dateFields.utc = true;
345 var stream = new _Stream(inputString);
346 _formatFields.forEach((f) => f.parse(stream, dateFields));
347 if (strict && !stream.atEnd()) {
348 throw new FormatException(
349 "Characters remaining after date parsing in $inputString");
350 }
351 if (strict) dateFields.verify(inputString);
352 return dateFields.asDate();
353 }
354
355 /**
356 * Given user input, attempt to parse the [inputString] into the anticipated
357 * format, treating it as being in UTC.
358 *
359 * The canonical Dart style name
360 * is [parseUtc], but [parseUTC] is retained
361 * for backward-compatibility.
362 */
363 DateTime parseUTC(String inputString) => parse(inputString, true);
364
365 /**
366 * Given user input, attempt to parse the [inputString] into the anticipated
367 * format, treating it as being in UTC.
368 *
369 * The canonical Dart style name
370 * is [parseUtc], but [parseUTC] is retained
371 * for backward-compatibility.
372 */
373 DateTime parseUtc(String inputString) => parse(inputString, true);
374
375 /**
376 * Return the locale code in which we operate, e.g. 'en_US' or 'pt'.
377 */
378 String get locale => _locale;
379
380 /**
381 * Returns a list of all locales for which we have date formatting
382 * information.
383 */
384 static List<String> allLocalesWithSymbols() => dateTimeSymbols.keys.toList();
385
386 /**
387 * The named constructors for this class are all conveniences for creating
388 * instances using one of the known "skeleton" formats, and having code
389 * completion support for discovering those formats.
390 * So,
391 * new DateFormat.yMd("en_US")
392 * is equivalent to
393 * new DateFormat("yMd", "en_US")
394 * To create a compound format you can use these constructors in combination
395 * with the add_ methods below. e.g.
396 * new DateFormat.yMd().add_Hms();
397 * If the optional [locale] is omitted, the format will be created using the
398 * default locale in [Intl.systemLocale].
399 */
400 DateFormat.d([locale]) : this("d", locale);
401 DateFormat.E([locale]) : this("E", locale);
402 DateFormat.EEEE([locale]) : this("EEEE", locale);
403 DateFormat.LLL([locale]) : this("LLL", locale);
404 DateFormat.LLLL([locale]) : this("LLLL", locale);
405 DateFormat.M([locale]) : this("M", locale);
406 DateFormat.Md([locale]) : this("Md", locale);
407 DateFormat.MEd([locale]) : this("MEd", locale);
408 DateFormat.MMM([locale]) : this("MMM", locale);
409 DateFormat.MMMd([locale]) : this("MMMd", locale);
410 DateFormat.MMMEd([locale]) : this("MMMEd", locale);
411 DateFormat.MMMM([locale]) : this("MMMM", locale);
412 DateFormat.MMMMd([locale]) : this("MMMMd", locale);
413 DateFormat.MMMMEEEEd([locale]) : this("MMMMEEEEd", locale);
414 DateFormat.QQQ([locale]) : this("QQQ", locale);
415 DateFormat.QQQQ([locale]) : this("QQQQ", locale);
416 DateFormat.y([locale]) : this("y", locale);
417 DateFormat.yM([locale]) : this("yM", locale);
418 DateFormat.yMd([locale]) : this("yMd", locale);
419 DateFormat.yMEd([locale]) : this("yMEd", locale);
420 DateFormat.yMMM([locale]) : this("yMMM", locale);
421 DateFormat.yMMMd([locale]) : this("yMMMd", locale);
422 DateFormat.yMMMEd([locale]) : this("yMMMEd", locale);
423 DateFormat.yMMMM([locale]) : this("yMMMM", locale);
424 DateFormat.yMMMMd([locale]) : this("yMMMMd", locale);
425 DateFormat.yMMMMEEEEd([locale]) : this("yMMMMEEEEd", locale);
426 DateFormat.yQQQ([locale]) : this("yQQQ", locale);
427 DateFormat.yQQQQ([locale]) : this("yQQQQ", locale);
428 DateFormat.H([locale]) : this("H", locale);
429 DateFormat.Hm([locale]) : this("Hm", locale);
430 DateFormat.Hms([locale]) : this("Hms", locale);
431 DateFormat.j([locale]) : this("j", locale);
432 DateFormat.jm([locale]) : this("jm", locale);
433 DateFormat.jms([locale]) : this("jms", locale);
434 DateFormat.jmv([locale]) : this("jmv", locale);
435 DateFormat.jmz([locale]) : this("jmz", locale);
436 DateFormat.jv([locale]) : this("jv", locale);
437 DateFormat.jz([locale]) : this("jz", locale);
438 DateFormat.m([locale]) : this("m", locale);
439 DateFormat.ms([locale]) : this("ms", locale);
440 DateFormat.s([locale]) : this("s", locale);
441
442 /**
443 * The "add_*" methods append a particular skeleton to the format, or set
444 * it as the only format if none was previously set. These are primarily
445 * useful for creating compound formats. For example
446 * new DateFormat.yMd().add_Hms();
447 * would create a date format that prints both the date and the time.
448 */
449 DateFormat add_d() => addPattern("d");
450 DateFormat add_E() => addPattern("E");
451 DateFormat add_EEEE() => addPattern("EEEE");
452 DateFormat add_LLL() => addPattern("LLL");
453 DateFormat add_LLLL() => addPattern("LLLL");
454 DateFormat add_M() => addPattern("M");
455 DateFormat add_Md() => addPattern("Md");
456 DateFormat add_MEd() => addPattern("MEd");
457 DateFormat add_MMM() => addPattern("MMM");
458 DateFormat add_MMMd() => addPattern("MMMd");
459 DateFormat add_MMMEd() => addPattern("MMMEd");
460 DateFormat add_MMMM() => addPattern("MMMM");
461 DateFormat add_MMMMd() => addPattern("MMMMd");
462 DateFormat add_MMMMEEEEd() => addPattern("MMMMEEEEd");
463 DateFormat add_QQQ() => addPattern("QQQ");
464 DateFormat add_QQQQ() => addPattern("QQQQ");
465 DateFormat add_y() => addPattern("y");
466 DateFormat add_yM() => addPattern("yM");
467 DateFormat add_yMd() => addPattern("yMd");
468 DateFormat add_yMEd() => addPattern("yMEd");
469 DateFormat add_yMMM() => addPattern("yMMM");
470 DateFormat add_yMMMd() => addPattern("yMMMd");
471 DateFormat add_yMMMEd() => addPattern("yMMMEd");
472 DateFormat add_yMMMM() => addPattern("yMMMM");
473 DateFormat add_yMMMMd() => addPattern("yMMMMd");
474 DateFormat add_yMMMMEEEEd() => addPattern("yMMMMEEEEd");
475 DateFormat add_yQQQ() => addPattern("yQQQ");
476 DateFormat add_yQQQQ() => addPattern("yQQQQ");
477 DateFormat add_H() => addPattern("H");
478 DateFormat add_Hm() => addPattern("Hm");
479 DateFormat add_Hms() => addPattern("Hms");
480 DateFormat add_j() => addPattern("j");
481 DateFormat add_jm() => addPattern("jm");
482 DateFormat add_jms() => addPattern("jms");
483 DateFormat add_jmv() => addPattern("jmv");
484 DateFormat add_jmz() => addPattern("jmz");
485 DateFormat add_jv() => addPattern("jv");
486 DateFormat add_jz() => addPattern("jz");
487 DateFormat add_m() => addPattern("m");
488 DateFormat add_ms() => addPattern("ms");
489 DateFormat add_s() => addPattern("s");
490
491 /**
492 * For each of the skeleton formats we also allow the use of the corresponding
493 * ICU constant names.
494 */
495 static const String ABBR_MONTH = 'MMM';
496 static const String DAY = 'd';
497 static const String ABBR_WEEKDAY = 'E';
498 static const String WEEKDAY = 'EEEE';
499 static const String ABBR_STANDALONE_MONTH = 'LLL';
500 static const String STANDALONE_MONTH = 'LLLL';
501 static const String NUM_MONTH = 'M';
502 static const String NUM_MONTH_DAY = 'Md';
503 static const String NUM_MONTH_WEEKDAY_DAY = 'MEd';
504 static const String ABBR_MONTH_DAY = 'MMMd';
505 static const String ABBR_MONTH_WEEKDAY_DAY = 'MMMEd';
506 static const String MONTH = 'MMMM';
507 static const String MONTH_DAY = 'MMMMd';
508 static const String MONTH_WEEKDAY_DAY = 'MMMMEEEEd';
509 static const String ABBR_QUARTER = 'QQQ';
510 static const String QUARTER = 'QQQQ';
511 static const String YEAR = 'y';
512 static const String YEAR_NUM_MONTH = 'yM';
513 static const String YEAR_NUM_MONTH_DAY = 'yMd';
514 static const String YEAR_NUM_MONTH_WEEKDAY_DAY = 'yMEd';
515 static const String YEAR_ABBR_MONTH = 'yMMM';
516 static const String YEAR_ABBR_MONTH_DAY = 'yMMMd';
517 static const String YEAR_ABBR_MONTH_WEEKDAY_DAY = 'yMMMEd';
518 static const String YEAR_MONTH = 'yMMMM';
519 static const String YEAR_MONTH_DAY = 'yMMMMd';
520 static const String YEAR_MONTH_WEEKDAY_DAY = 'yMMMMEEEEd';
521 static const String YEAR_ABBR_QUARTER = 'yQQQ';
522 static const String YEAR_QUARTER = 'yQQQQ';
523 static const String HOUR24 = 'H';
524 static const String HOUR24_MINUTE = 'Hm';
525 static const String HOUR24_MINUTE_SECOND = 'Hms';
526 static const String HOUR = 'j';
527 static const String HOUR_MINUTE = 'jm';
528 static const String HOUR_MINUTE_SECOND = 'jms';
529 static const String HOUR_MINUTE_GENERIC_TZ = 'jmv';
530 static const String HOUR_MINUTE_TZ = 'jmz';
531 static const String HOUR_GENERIC_TZ = 'jv';
532 static const String HOUR_TZ = 'jz';
533 static const String MINUTE = 'm';
534 static const String MINUTE_SECOND = 'ms';
535 static const String SECOND = 's';
536
537 /** The locale in which we operate, e.g. 'en_US', or 'pt'. */
538 String _locale;
539
540 /**
541 * The full template string. This may have been specified directly, or
542 * it may have been derived from a skeleton and the locale information
543 * on how to interpret that skeleton.
544 */
545 String _pattern;
546
547 /**
548 * We parse the format string into individual [_DateFormatField] objects
549 * that are used to do the actual formatting and parsing. Do not use
550 * this variable directly, use the getter [_formatFields].
551 */
552 List<_DateFormatField> _formatFieldsPrivate;
553
554 /**
555 * Getter for [_formatFieldsPrivate] that lazily initializes it.
556 */
557 get _formatFields {
558 if (_formatFieldsPrivate == null) {
559 if (_pattern == null) _useDefaultPattern();
560 _formatFieldsPrivate = parsePattern(_pattern);
561 }
562 return _formatFieldsPrivate;
563 }
564
565 /**
566 * We are being asked to do formatting without having set any pattern.
567 * Use a default.
568 */
569 _useDefaultPattern() {
570 add_yMMMMd();
571 add_jms();
572 }
573
574 /**
575 * A series of regular expressions used to parse a format string into its
576 * component fields.
577 */
578 static List<RegExp> _matchers = [
579 // Quoted String - anything between single quotes, with escaping
580 // of single quotes by doubling them.
581 // e.g. in the pattern "hh 'o''clock'" will match 'o''clock'
582 new RegExp("^\'(?:[^\']|\'\')*\'"),
583 // Fields - any sequence of 1 or more of the same field characters.
584 // e.g. in "hh:mm:ss" will match hh, mm, and ss. But in "hms" would
585 // match each letter individually.
586 new RegExp(
587 "^(?:G+|y+|M+|k+|S+|E+|a+|h+|K+|H+|c+|L+|Q+|d+|D+|m+|s+|v+|z+|Z+)"),
588 // Everything else - A sequence that is not quotes or field characters.
589 // e.g. in "hh:mm:ss" will match the colons.
590 new RegExp("^[^\'GyMkSEahKHcLQdDmsvzZ]+")
591 ];
592
593 /**
594 * Set our pattern, appending it to any existing patterns. Also adds a single
595 * space to separate the two.
596 */
597 _appendPattern(String inputPattern, [String separator = ' ']) {
598 _pattern =
599 _pattern == null ? inputPattern : "$_pattern$separator$inputPattern";
600 }
601
602 /**
603 * Add [inputPattern] to this instance as a pattern. If there was a previous
604 * pattern, then this appends to it, separating the two by [separator].
605 * [inputPattern] is first looked up in our list of known skeletons.
606 * If it's found there, then use the corresponding pattern for this locale.
607 * If it's not, then treat [inputPattern] as an explicit pattern.
608 */
609 DateFormat addPattern(String inputPattern, [String separator = ' ']) {
610 // TODO(alanknight): This is an expensive operation. Caching recently used
611 // formats, or possibly introducing an entire "locale" object that would
612 // cache patterns for that locale could be a good optimization.
613 // If we have already parsed the format fields, reset them.
614 _formatFieldsPrivate = null;
615 if (inputPattern == null) return this;
616 if (!_availableSkeletons.containsKey(inputPattern)) {
617 _appendPattern(inputPattern, separator);
618 } else {
619 _appendPattern(_availableSkeletons[inputPattern], separator);
620 }
621 return this;
622 }
623
624 /** Return the pattern that we use to format dates.*/
625 get pattern => _pattern;
626
627 /** Return the skeletons for our current locale. */
628 Map get _availableSkeletons => dateTimePatterns[locale];
629
630 /**
631 * Return the [DateSymbol] information for the locale. This can be useful
632 * to find lists like the names of weekdays or months in a locale, but
633 * the structure of this data may change, and it's generally better to go
634 * through the [format] and [parse] APIs. If the locale isn't present, or
635 * is uninitialized, returns null;
636 */
637 DateSymbols get dateSymbols => dateTimeSymbols[_locale];
638
639 /**
640 * Set the locale. If the locale can't be found, we also look up
641 * based on alternative versions, e.g. if we have no 'en_CA' we will
642 * look for 'en' as a fallback. It will also translate en-ca into en_CA.
643 * Null is also considered a valid value for [newLocale], indicating
644 * to use the default.
645 */
646 _setLocale(String newLocale) {
647 _locale = Intl.verifiedLocale(newLocale, localeExists);
648 }
649
650 /**
651 * Return true if the locale exists, or if it is null. The null case
652 * is interpreted to mean that we use the default locale.
653 */
654 static bool localeExists(localeName) {
655 if (localeName == null) return false;
656 return dateTimeSymbols.containsKey(localeName);
657 }
658
659 static List get _fieldConstructors => [
660 (pattern, parent) => new _DateFormatQuotedField(pattern, parent),
661 (pattern, parent) => new _DateFormatPatternField(pattern, parent),
662 (pattern, parent) => new _DateFormatLiteralField(pattern, parent)
663 ];
664
665 /** Parse the template pattern and return a list of field objects.*/
666 List parsePattern(String pattern) {
667 if (pattern == null) return null;
668 return _parsePatternHelper(pattern).reversed.toList();
669 }
670
671 /** Recursive helper for parsing the template pattern. */
672 List _parsePatternHelper(String pattern) {
673 if (pattern.isEmpty) return [];
674
675 var matched = _match(pattern);
676 if (matched == null) return [];
677
678 var parsed =
679 _parsePatternHelper(pattern.substring(matched.fullPattern().length));
680 parsed.add(matched);
681 return parsed;
682 }
683
684 /** Find elements in a string that are patterns for specific fields.*/
685 _DateFormatField _match(String pattern) {
686 for (var i = 0; i < _matchers.length; i++) {
687 var regex = _matchers[i];
688 var match = regex.firstMatch(pattern);
689 if (match != null) {
690 return _fieldConstructors[i](match.group(0), this);
691 }
692 }
693 }
694 }
OLDNEW
« no previous file with comments | « intl/lib/src/intl/bidi_utils.dart ('k') | intl/lib/src/intl/date_format_field.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698