| OLD | NEW |
| (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 /** | |
| 6 * DateFormat is for formatting and parsing dates in a locale-sensitive | |
| 7 * manner. | |
| 8 * It allows the user to choose from a set of standard date time formats as well | |
| 9 * as specify a customized pattern under certain locales. Date elements that | |
| 10 * vary across locales include month name, week name, field order, etc. | |
| 11 * <!-- TODO(efortuna): Customized pattern system -- suggested by i18n needs | |
| 12 * feedback on appropriateness. --> | |
| 13 * We also allow the user to use any customized pattern to parse or format | |
| 14 * date-time strings under certain locales. Date elements that vary across | |
| 15 * locales include month name, weekname, field, order, etc. | |
| 16 * | |
| 17 * This library uses the ICU/JDK date/time pattern specification both for | |
| 18 * complete format specifications and also the abbreviated "skeleton" form | |
| 19 * which can also adapt to different locales and is preferred where available. | |
| 20 * | |
| 21 * Skeletons: These can be specified either as the ICU constant name or as the | |
| 22 * skeleton to which it resolves. The supported set of skeletons is as follows | |
| 23 * ICU Name Skeleton | |
| 24 * -------- -------- | |
| 25 * DAY d | |
| 26 * ABBR_WEEKDAY E | |
| 27 * WEEKDAY EEEE | |
| 28 * ABBR_STANDALONE_MONTH LLL | |
| 29 * STANDALONE_MONTH LLLL | |
| 30 * NUM_MONTH M | |
| 31 * NUM_MONTH_DAY Md | |
| 32 * NUM_MONTH_WEEKDAY_DAY MEd | |
| 33 * ABBR_MONTH MMM | |
| 34 * ABBR_MONTH_DAY MMMd | |
| 35 * ABBR_MONTH_WEEKDAY_DAY MMMEd | |
| 36 * MONTH MMMM | |
| 37 * MONTH_DAY MMMMd | |
| 38 * MONTH_WEEKDAY_DAY MMMMEEEEd | |
| 39 * ABBR_QUARTER QQQ | |
| 40 * QUARTER QQQQ | |
| 41 * YEAR y | |
| 42 * YEAR_NUM_MONTH yM | |
| 43 * YEAR_NUM_MONTH_DAY yMd | |
| 44 * YEAR_NUM_MONTH_WEEKDAY_DAY yMEd | |
| 45 * YEAR_ABBR_MONTH yMMM | |
| 46 * YEAR_ABBR_MONTH_DAY yMMMd | |
| 47 * YEAR_ABBR_MONTH_WEEKDAY_DAY yMMMEd | |
| 48 * YEAR_MONTH yMMMM | |
| 49 * YEAR_MONTH_DAY yMMMMd | |
| 50 * YEAR_MONTH_WEEKDAY_DAY yMMMMEEEEd | |
| 51 * YEAR_ABBR_QUARTER yQQQ | |
| 52 * YEAR_QUARTER yQQQQ | |
| 53 * HOUR24 H | |
| 54 * HOUR24_MINUTE Hm | |
| 55 * HOUR24_MINUTE_SECOND Hms | |
| 56 * HOUR j | |
| 57 * HOUR_MINUTE jm | |
| 58 * HOUR_MINUTE_SECOND jms | |
| 59 * HOUR_MINUTE_GENERIC_TZ jmv | |
| 60 * HOUR_MINUTE_TZ jmz | |
| 61 * HOUR_GENERIC_TZ jv | |
| 62 * HOUR_TZ jz | |
| 63 * MINUTE m | |
| 64 * MINUTE_SECOND ms | |
| 65 * SECOND s | |
| 66 * | |
| 67 * Examples Using the US Locale: | |
| 68 * | |
| 69 * Pattern Result | |
| 70 * ---------------- ------- | |
| 71 * "yMd" -> 07/10/1996 | |
| 72 * "yMMMMd" -> July 10, 1996 | |
| 73 * "Hm" -> 12:08 PM | |
| 74 * | |
| 75 * Explicit Pattern Syntax: Formats can also be specified with a pattern string. | |
| 76 * The skeleton forms will resolve to explicit patterns of this form, but will | |
| 77 * also adapt to different patterns in different locales. | |
| 78 * The following characters are reserved: | |
| 79 * | |
| 80 * Symbol Meaning Presentation Example | |
| 81 * ------ ------- ------------ ------- | |
| 82 * G era designator (Text) AD | |
| 83 * y year (Number) 1996 | |
| 84 * M month in year (Text & Number) July & 07 | |
| 85 * L standalone month (Text & Number) July & 07 | |
| 86 * d day in month (Number) 10 | |
| 87 * c standalone day (Number) 10 | |
| 88 * h hour in am/pm (1~12) (Number) 12 | |
| 89 * H hour in day (0~23) (Number) 0 | |
| 90 * m minute in hour (Number) 30 | |
| 91 * s second in minute (Number) 55 | |
| 92 * S fractional second (Number) 978 | |
| 93 * E day of week (Text) Tuesday | |
| 94 * D day in year (Number) 189 | |
| 95 * a am/pm marker (Text) PM | |
| 96 * k hour in day (1~24) (Number) 24 | |
| 97 * K hour in am/pm (0~11) (Number) 0 | |
| 98 * z time zone (Text) Pacific Standard Time | |
| 99 * Z time zone (RFC 822) (Number) -0800 | |
| 100 * v time zone (generic) (Text) Pacific Time | |
| 101 * Q quarter (Text) Q3 | |
| 102 * ' escape for text (Delimiter) 'Date=' | |
| 103 * '' single quote (Literal) 'o''clock' | |
| 104 * | |
| 105 * The count of pattern letters determine the format. | |
| 106 * **Text**: | |
| 107 * * 5 pattern letters--use narrow form for standalone. Otherwise does not apply | |
| 108 * * 4 or more pattern letters--use full form, | |
| 109 * * 3 pattern letters--use short or abbreviated form if one exists | |
| 110 * * less than 3--use numeric form if one exists | |
| 111 * | |
| 112 * **Number**: the minimum number of digits. Shorter numbers are zero-padded to | |
| 113 * this amount (e.g. if "m" produces "6", "mm" produces "06"). Year is handled | |
| 114 * specially; that is, if the count of 'y' is 2, the Year will be truncated to | |
| 115 * 2 digits. (e.g., if "yyyy" produces "1997", "yy" produces "97".) Unlike other | |
| 116 * fields, fractional seconds are padded on the right with zero. | |
| 117 * | |
| 118 * (Text & Number): 3 or over, use text, otherwise use number. | |
| 119 * | |
| 120 * Any characters that not in the pattern will be treated as quoted text. For | |
| 121 * instance, characters like ':', '.', ' ', '#' and '@' will appear in the | |
| 122 * resulting time text even they are not embraced within single quotes. In our | |
| 123 * current pattern usage, we didn't use up all letters. But those unused | |
| 124 * letters are strongly discouraged to be used as quoted text without quote. | |
| 125 * That's because we may use other letter for pattern in future. | |
| 126 * | |
| 127 * Examples Using the US Locale: | |
| 128 * | |
| 129 * Format Pattern Result | |
| 130 * -------------- ------- | |
| 131 * "yyyy.MM.dd G 'at' HH:mm:ss vvvv"->1996.07.10 AD at 15:08:56 Pacific Time | |
| 132 * "EEE, MMM d, ''yy" ->Wed, July 10, '96 | |
| 133 * "h:mm a" ->12:08 PM | |
| 134 * "hh 'o''clock' a, zzzz" ->12 o'clock PM, Pacific Daylight Time | |
| 135 * "K:mm a, vvv" ->0:00 PM, PT | |
| 136 * "yyyyy.MMMMM.dd GGG hh:mm aaa" ->01996.July.10 AD 12:08 PM | |
| 137 * | |
| 138 * When parsing a date string using the abbreviated year pattern ("yy"), | |
| 139 * DateFormat must interpret the abbreviated year relative to some | |
| 140 * century. It does this by adjusting dates to be within 80 years before and 20 | |
| 141 * years after the time the parse function is called. For example, using a | |
| 142 * pattern of "MM/dd/yy" and a DateTimeParse instance created on Jan 1, 1997, | |
| 143 * the string "01/11/12" would be interpreted as Jan 11, 2012 while the string | |
| 144 * "05/04/64" would be interpreted as May 4, 1964. During parsing, only | |
| 145 * strings consisting of exactly two digits, as defined by {@link | |
| 146 * java.lang.Character#isDigit(char)}, will be parsed into the default | |
| 147 * century. Any other numeric string, such as a one digit string, a three or | |
| 148 * more digit string will be interpreted as its face value. | |
| 149 * | |
| 150 * If the year pattern does not have exactly two 'y' characters, the year is | |
| 151 * interpreted literally, regardless of the number of digits. So using the | |
| 152 * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D. | |
| 153 * | |
| 154 * When numeric fields abut one another directly, with no intervening | |
| 155 * delimiter characters, they constitute a run of abutting numeric fields. Such | |
| 156 * runs are parsed specially. For example, the format "HHmmss" parses the input | |
| 157 * text "123456" to 12:34:56, parses the input text "12345" to 1:23:45, and | |
| 158 * fails to parse "1234". In other words, the leftmost field of the run is | |
| 159 * flexible, while the others keep a fixed width. If the parse fails anywhere in | |
| 160 * the run, then the leftmost field is shortened by one character, and the | |
| 161 * entire run is parsed again. This is repeated until either the parse succeeds | |
| 162 * or the leftmost field is one character in length. If the parse still fails at | |
| 163 * that point, the parse of the run fails. | |
| 164 */ | |
| 165 | |
| 166 #library('date_format'); | |
| 167 | |
| 168 #import('intl.dart'); | |
| 169 #import('date_time_patterns.dart'); | |
| 170 #import('date_symbols.dart'); | |
| 171 #import('date_symbol_data.dart'); | |
| 172 | |
| 173 #source('lib/date_format_field.dart'); | |
| 174 #source('lib/date_format_helpers.dart'); | |
| 175 | |
| 176 class DateFormat { | |
| 177 | |
| 178 /** | |
| 179 * If [newPattern] matches one of the skeleton forms, it is looked up | |
| 180 * in [locale] or in the default if none is specified, and the corresponding | |
| 181 * full format string is used. If [newPattern] does not match one | |
| 182 * of the supported skeleton forms then it is used as a format directly. | |
| 183 * | |
| 184 * For example, in an en_US locale, specifying the skeleton | |
| 185 * `new DateFormat('yMEd');` | |
| 186 * or the explicit | |
| 187 * `new DateFormat('EEE, M/d/y');` | |
| 188 * would produce the same result, a date of the form | |
| 189 * `Wed, 6/27/2012` | |
| 190 * However, the skeleton version would also adapt to other locales. | |
| 191 * | |
| 192 * If [locale] does not exist in our set of supported locales then an | |
| 193 * [IllegalArgumentException] is thrown. | |
| 194 */ | |
| 195 DateFormat([String newPattern, String locale]) { | |
| 196 // TODO(alanknight): It should be possible to specify multiple skeletons eg | |
| 197 // date, time, timezone all separately. Adding many or named parameters to | |
| 198 // the constructor seems awkward, especially with the possibility of | |
| 199 // confusion with the locale. A "fluent" interface with cascading on an | |
| 200 // instance might work better? A list of patterns is also possible. | |
| 201 // TODO(alanknight): There will need to be at least setup type async | |
| 202 // operations to avoid the need to bring along every locale in every program | |
| 203 _locale = Intl.verifiedLocale(locale); | |
| 204 _setPattern(newPattern); | |
| 205 } | |
| 206 | |
| 207 /** | |
| 208 * Return a string representing [date] formatted according to our locale | |
| 209 * and internal format. | |
| 210 */ | |
| 211 String format(Date date) { | |
| 212 // TODO(efortuna): read optional TimeZone argument (or similar)? | |
| 213 var result = new StringBuffer(); | |
| 214 _formatFields.forEach((field) => result.add(field.format(date))); | |
| 215 return result.toString(); | |
| 216 } | |
| 217 | |
| 218 /** | |
| 219 * Returns a date string indicating how long ago (3 hours, 2 minutes) | |
| 220 * something has happened or how long in the future something will happen | |
| 221 * given a [reference] Date relative to the current time. | |
| 222 */ | |
| 223 String formatDuration(Date reference) { | |
| 224 return ''; | |
| 225 } | |
| 226 | |
| 227 /** | |
| 228 * Formats a string indicating how long ago (negative [duration]) or how far | |
| 229 * in the future (positive [duration]) some time is with respect to a | |
| 230 * reference [date]. | |
| 231 */ | |
| 232 String formatDurationFrom(Duration duration, Date date) { | |
| 233 return ''; | |
| 234 } | |
| 235 | |
| 236 /** | |
| 237 * Given user input, attempt to parse the [inputString] into the anticipated | |
| 238 * format, treating it as being in the local timezone. | |
| 239 */ | |
| 240 Date parse(String inputString, [utc = false]) { | |
| 241 var dateFields = new _DateBuilder(); | |
| 242 if (utc) dateFields.utc=true; | |
| 243 var stream = new _Stream(inputString); | |
| 244 _formatFields.forEach( | |
| 245 (each) => each.parse(stream, dateFields)); | |
| 246 return dateFields.asDate(); | |
| 247 } | |
| 248 | |
| 249 /** | |
| 250 * Given user input, attempt to parse the [inputString] into the anticipated | |
| 251 * format, treating it as being in UTC. | |
| 252 */ | |
| 253 Date parseUTC(String inputString) { | |
| 254 return parse(inputString, true); | |
| 255 } | |
| 256 | |
| 257 /** | |
| 258 * Return the locale code in which we operate, e.g. 'en_US' or 'pt'. | |
| 259 */ | |
| 260 String get locale() => _locale; | |
| 261 | |
| 262 /** | |
| 263 * Constructors for a set of predefined formats for which | |
| 264 * internationalized forms are known. These can be specified | |
| 265 * either as ICU constants, or as skeletons. | |
| 266 */ | |
| 267 DateFormat.DAY([locale]) : this(DAY, locale); | |
| 268 DateFormat.ABBR_WEEKDAY([locale]) : this(ABBR_WEEKDAY, locale); | |
| 269 DateFormat.WEEKDAY([locale]) : this(WEEKDAY, locale); | |
| 270 DateFormat.ABBR_STANDALONE_MONTH([locale]) : | |
| 271 this(ABBR_STANDALONE_MONTH, locale); | |
| 272 DateFormat.STANDALONE_MONTH([locale]) : this(STANDALONE_MONTH, locale); | |
| 273 DateFormat.NUM_MONTH([locale]) : this(NUM_MONTH, locale); | |
| 274 DateFormat.NUM_MONTH_DAY([locale]) : this(NUM_MONTH_DAY, locale); | |
| 275 DateFormat.NUM_MONTH_WEEKDAY_DAY([locale]) : | |
| 276 this(NUM_MONTH_WEEKDAY_DAY, locale); | |
| 277 DateFormat.ABBR_MONTH([locale]) : this(ABBR_MONTH, locale); | |
| 278 DateFormat.ABBR_MONTH_DAY([locale]) : this(ABBR_MONTH_DAY, locale); | |
| 279 DateFormat.ABBR_MONTH_WEEKDAY_DAY([locale]) : | |
| 280 this(ABBR_MONTH_WEEKDAY_DAY, locale); | |
| 281 DateFormat.MONTH([locale]) : this(MONTH, locale); | |
| 282 DateFormat.MONTH_DAY([locale]) : this(MONTH_DAY, locale); | |
| 283 DateFormat.MONTH_WEEKDAY_DAY([locale]) : this(MONTH_WEEKDAY_DAY, locale); | |
| 284 DateFormat.ABBR_QUARTER([locale]) : this(ABBR_QUARTER, locale); | |
| 285 DateFormat.QUARTER([locale]) : this(QUARTER, locale); | |
| 286 DateFormat.YEAR([locale]) : this(YEAR, locale); | |
| 287 DateFormat.YEAR_NUM_MONTH([locale]) : this(YEAR_NUM_MONTH, locale); | |
| 288 DateFormat.YEAR_NUM_MONTH_DAY([locale]) : this(YEAR_NUM_MONTH_DAY, locale); | |
| 289 DateFormat.YEAR_NUM_MONTH_WEEKDAY_DAY([locale]) : | |
| 290 this(YEAR_NUM_MONTH_WEEKDAY_DAY, locale); | |
| 291 DateFormat.YEAR_ABBR_MONTH([locale]) : this(YEAR_ABBR_MONTH, locale); | |
| 292 DateFormat.YEAR_ABBR_MONTH_DAY([locale]) : this(YEAR_ABBR_MONTH_DAY, locale); | |
| 293 DateFormat.YEAR_ABBR_MONTH_WEEKDAY_DAY([locale]) : | |
| 294 this(YEAR_ABBR_MONTH_WEEKDAY_DAY, locale); | |
| 295 DateFormat.YEAR_MONTH([locale]) : this(YEAR_MONTH, locale); | |
| 296 DateFormat.YEAR_MONTH_DAY([locale]) : this(YEAR_MONTH_DAY, locale); | |
| 297 DateFormat.YEAR_MONTH_WEEKDAY_DAY([locale]) : | |
| 298 this(YEAR_MONTH_WEEKDAY_DAY, locale); | |
| 299 DateFormat.YEAR_ABBR_QUARTER([locale]) : this(YEAR_ABBR_QUARTER, locale); | |
| 300 DateFormat.YEAR_QUARTER([locale]) : this(YEAR_QUARTER, locale); | |
| 301 DateFormat.HOUR24([locale]) : this(HOUR24, locale); | |
| 302 DateFormat.HOUR24_MINUTE([locale]) : this(HOUR24_MINUTE, locale); | |
| 303 DateFormat.HOUR24_MINUTE_SECOND([locale]) : | |
| 304 this(HOUR24_MINUTE_SECOND, locale); | |
| 305 DateFormat.HOUR([locale]) : this(HOUR, locale); | |
| 306 DateFormat.HOUR_MINUTE([locale]) : this(HOUR_MINUTE, locale); | |
| 307 DateFormat.HOUR_MINUTE_SECOND([locale]) : this(HOUR_MINUTE_SECOND, locale); | |
| 308 DateFormat.HOUR_MINUTE_GENERIC_TZ([locale]) : | |
| 309 this(HOUR_MINUTE_GENERIC_TZ, locale); | |
| 310 DateFormat.HOUR_MINUTE_TZ([locale]) : this(HOUR_MINUTE_TZ, locale); | |
| 311 DateFormat.HOUR_GENERIC_TZ([locale]) : this(HOUR_GENERIC_TZ, locale); | |
| 312 DateFormat.HOUR_TZ([locale]) : this(HOUR_TZ, locale); | |
| 313 DateFormat.MINUTE([locale]) : this(MINUTE, locale); | |
| 314 DateFormat.MINUTE_SECOND([locale]) : this(MINUTE_SECOND, locale); | |
| 315 DateFormat.SECOND([locale]) : this(SECOND, locale); | |
| 316 | |
| 317 DateFormat.d([locale]) : this("d", locale); | |
| 318 DateFormat.E([locale]) : this("E", locale); | |
| 319 DateFormat.EEEE([locale]) : this("EEEE", locale); | |
| 320 DateFormat.LLL([locale]) : this("LLL", locale); | |
| 321 DateFormat.LLLL([locale]) : this("LLLL", locale); | |
| 322 DateFormat.M([locale]) : this("M", locale); | |
| 323 DateFormat.Md([locale]) : this("Md", locale); | |
| 324 DateFormat.MEd([locale]) : this("MEd", locale); | |
| 325 DateFormat.MMM([locale]) : this("MMM", locale); | |
| 326 DateFormat.MMMd([locale]) : this("MMMd", locale); | |
| 327 DateFormat.MMMEd([locale]) : this("MMMEd", locale); | |
| 328 DateFormat.MMMM([locale]) : this("MMMM", locale); | |
| 329 DateFormat.MMMMd([locale]) : this("MMMMd", locale); | |
| 330 DateFormat.MMMMEEEEd([locale]) : this("MMMMEEEEd", locale); | |
| 331 DateFormat.QQQ([locale]) : this("QQQ", locale); | |
| 332 DateFormat.QQQQ([locale]) : this("QQQQ", locale); | |
| 333 DateFormat.y([locale]) : this("y", locale); | |
| 334 DateFormat.yM([locale]) : this("yM", locale); | |
| 335 DateFormat.yMd([locale]) : this("yMd", locale); | |
| 336 DateFormat.yMEd([locale]) : this("yMEd", locale); | |
| 337 DateFormat.yMMM([locale]) : this("yMMM", locale); | |
| 338 DateFormat.yMMMd([locale]) : this("yMMMd", locale); | |
| 339 DateFormat.yMMMEd([locale]) : this("yMMMEd", locale); | |
| 340 DateFormat.yMMMM([locale]) : this("yMMMM", locale); | |
| 341 DateFormat.yMMMMd([locale]) : this("yMMMMd", locale); | |
| 342 DateFormat.yMMMMEEEEd([locale]) : this("yMMMMEEEEd", locale); | |
| 343 DateFormat.yQQQ([locale]) : this("yQQQ", locale); | |
| 344 DateFormat.yQQQQ([locale]) : this("yQQQQ", locale); | |
| 345 DateFormat.H([locale]) : this("H", locale); | |
| 346 DateFormat.Hm([locale]) : this("Hm", locale); | |
| 347 DateFormat.Hms([locale]) : this("Hms", locale); | |
| 348 DateFormat.j([locale]) : this("j", locale); | |
| 349 DateFormat.jm([locale]) : this("jm", locale); | |
| 350 DateFormat.jms([locale]) : this("jms", locale); | |
| 351 DateFormat.jmv([locale]) : this("jmv", locale); | |
| 352 DateFormat.jmz([locale]) : this("jmz", locale); | |
| 353 DateFormat.jv([locale]) : this("jv", locale); | |
| 354 DateFormat.jz([locale]) : this("jz", locale); | |
| 355 DateFormat.m([locale]) : this("m", locale); | |
| 356 DateFormat.ms([locale]) : this("ms", locale); | |
| 357 DateFormat.s([locale]) : this("s", locale); | |
| 358 | |
| 359 /** | |
| 360 * ICU constants for format names, resolving to the corresponding skeletons. | |
| 361 */ | |
| 362 static final String DAY = 'd'; | |
| 363 static final String ABBR_WEEKDAY = 'E'; | |
| 364 static final String WEEKDAY = 'EEEE'; | |
| 365 static final String ABBR_STANDALONE_MONTH = 'LLL'; | |
| 366 static final String STANDALONE_MONTH = 'LLLL'; | |
| 367 static final String NUM_MONTH = 'M'; | |
| 368 static final String NUM_MONTH_DAY = 'Md'; | |
| 369 static final String NUM_MONTH_WEEKDAY_DAY = 'MEd'; | |
| 370 static final String ABBR_MONTH = 'MMM'; | |
| 371 static final String ABBR_MONTH_DAY = 'MMMd'; | |
| 372 static final String ABBR_MONTH_WEEKDAY_DAY = 'MMMEd'; | |
| 373 static final String MONTH = 'MMMM'; | |
| 374 static final String MONTH_DAY = 'MMMMd'; | |
| 375 static final String MONTH_WEEKDAY_DAY = 'MMMMEEEEd'; | |
| 376 static final String ABBR_QUARTER = 'QQQ'; | |
| 377 static final String QUARTER = 'QQQQ'; | |
| 378 static final String YEAR = 'y'; | |
| 379 static final String YEAR_NUM_MONTH = 'yM'; | |
| 380 static final String YEAR_NUM_MONTH_DAY = 'yMd'; | |
| 381 static final String YEAR_NUM_MONTH_WEEKDAY_DAY = 'yMEd'; | |
| 382 static final String YEAR_ABBR_MONTH = 'yMMM'; | |
| 383 static final String YEAR_ABBR_MONTH_DAY = 'yMMMd'; | |
| 384 static final String YEAR_ABBR_MONTH_WEEKDAY_DAY = 'yMMMEd'; | |
| 385 static final String YEAR_MONTH = 'yMMMM'; | |
| 386 static final String YEAR_MONTH_DAY = 'yMMMMd'; | |
| 387 static final String YEAR_MONTH_WEEKDAY_DAY = 'yMMMMEEEEd'; | |
| 388 static final String YEAR_ABBR_QUARTER = 'yQQQ'; | |
| 389 static final String YEAR_QUARTER = 'yQQQQ'; | |
| 390 static final String HOUR24 = 'H'; | |
| 391 static final String HOUR24_MINUTE = 'Hm'; | |
| 392 static final String HOUR24_MINUTE_SECOND = 'Hms'; | |
| 393 static final String HOUR = 'j'; | |
| 394 static final String HOUR_MINUTE = 'jm'; | |
| 395 static final String HOUR_MINUTE_SECOND = 'jms'; | |
| 396 static final String HOUR_MINUTE_GENERIC_TZ = 'jmv'; | |
| 397 static final String HOUR_MINUTE_TZ = 'jmz'; | |
| 398 static final String HOUR_GENERIC_TZ = 'jv'; | |
| 399 static final String HOUR_TZ = 'jz'; | |
| 400 static final String MINUTE = 'm'; | |
| 401 static final String MINUTE_SECOND = 'ms'; | |
| 402 static final String SECOND = 's'; | |
| 403 | |
| 404 /** The locale in which we operate, e.g. 'en_US', or 'pt'. */ | |
| 405 String _locale; | |
| 406 | |
| 407 /** | |
| 408 * The full template string. This may have been specified directly, or | |
| 409 * it may have been derived from a skeleton and the locale information | |
| 410 * on how to interpret that skeleton. | |
| 411 */ | |
| 412 String _pattern; | |
| 413 | |
| 414 /** | |
| 415 * We parse the format string into individual fields and store them here. | |
| 416 * This is what is actually used to do the formatting. | |
| 417 */ | |
| 418 List<_DateFormatField> _formatFields; | |
| 419 | |
| 420 /** | |
| 421 * A series of regular expressions used to parse a format string into its | |
| 422 * component fields. | |
| 423 */ | |
| 424 static var _matchers = const [ | |
| 425 // Quoted String - anything between single quotes, with escaping | |
| 426 // of single quotes by doubling them. | |
| 427 // e.g. in the pattern "hh 'o''clock'" will match 'o''clock' | |
| 428 const RegExp("^\'(?:[^\']|\'\')*\'"), | |
| 429 // Fields - any sequence of 1 or more of the same field characters. | |
| 430 // e.g. in "hh:mm:ss" will match hh, mm, and ss. But in "hms" would | |
| 431 // match each letter individually. | |
| 432 const RegExp( | |
| 433 "^(?:G+|y+|M+|k+|S+|E+|a+|h+|K+|H+|c+|L+|Q+|d+|m+|s+|v+|z+|Z+)"), | |
| 434 // Everything else - A sequence that is not quotes or field characters. | |
| 435 // e.g. in "hh:mm:ss" will match the colons. | |
| 436 const RegExp("^[^\'GyMkSEahKHcLQdmsvzZ]+") | |
| 437 ]; | |
| 438 | |
| 439 /** | |
| 440 * Given a format from the user look it up in our list of known skeletons. | |
| 441 * If it's there, then use the corresponding pattern for this locale. | |
| 442 * If it's not, then treat it as an explicit pattern. | |
| 443 */ | |
| 444 _setPattern(String inputPattern) { | |
| 445 // TODO(alanknight): This is an expensive operation. Caching recently used | |
| 446 // formats, or possibly introducing an entire "locale" object that would | |
| 447 // cache patterns for that locale could be a good optimization. | |
| 448 if (!_availableSkeletons.containsKey(inputPattern)) { | |
| 449 _pattern = inputPattern; | |
| 450 } else { | |
| 451 _pattern = _availableSkeletons[inputPattern]; | |
| 452 } | |
| 453 _formatFields = parsePattern(_pattern); | |
| 454 } | |
| 455 | |
| 456 /** Return the pattern that we use to format dates.*/ | |
| 457 get pattern() => _pattern; | |
| 458 | |
| 459 /** Return the skeletons for our current locale. */ | |
| 460 Map get _availableSkeletons() { | |
| 461 return dateTimePatterns[locale]; | |
| 462 } | |
| 463 | |
| 464 /** | |
| 465 * Set the locale. If the locale can't be found, we also look up | |
| 466 * based on alternative versions, e.g. if we have no 'en_CA' we will | |
| 467 * look for 'en' as a fallback. It will also translate en-ca into en_CA. | |
| 468 * Null is also considered a valid value for [newLocale], indicating | |
| 469 * to use the default. | |
| 470 */ | |
| 471 _setLocale(String newLocale) { | |
| 472 _locale = Intl.verifiedLocale(newLocale); | |
| 473 } | |
| 474 | |
| 475 /** | |
| 476 * Return true if the locale exists, or if it is null. The null case | |
| 477 * is interpreted to mean that we use the default locale. | |
| 478 */ | |
| 479 static bool localeExists(localeName) { | |
| 480 if (localeName == null) return false; | |
| 481 return dateTimeSymbols.containsKey(localeName); | |
| 482 } | |
| 483 | |
| 484 // TODO(alanknight): This can be a variable once that's permitted. | |
| 485 static List get _fieldConstructors() => [ | |
| 486 (pattern, parent) => new _DateFormatQuotedField(pattern, parent), | |
| 487 (pattern, parent) => new _DateFormatPatternField(pattern, parent), | |
| 488 (pattern, parent) => new _DateFormatLiteralField(pattern, parent)]; | |
| 489 | |
| 490 /** Parse the template pattern and return a list of field objects.*/ | |
| 491 List parsePattern(String pattern) { | |
| 492 if (pattern == null) return null; | |
| 493 return _reverse(_parsePatternHelper(pattern)); | |
| 494 } | |
| 495 | |
| 496 /** Recursive helper for parsing the template pattern. */ | |
| 497 List _parsePatternHelper(String pattern) { | |
| 498 if (pattern.isEmpty()) return []; | |
| 499 | |
| 500 var matched = _match(pattern); | |
| 501 if (matched == null) return []; | |
| 502 | |
| 503 var parsed = _parsePatternHelper( | |
| 504 pattern.substring(matched.fullPattern().length)); | |
| 505 parsed.add(matched); | |
| 506 return parsed; | |
| 507 } | |
| 508 | |
| 509 /** Find elements in a string that are patterns for specific fields.*/ | |
| 510 _DateFormatField _match(String pattern) { | |
| 511 for (var i = 0; i < _matchers.length; i++) { | |
| 512 var regex = _matchers[i]; | |
| 513 var match = regex.firstMatch(pattern); | |
| 514 if (match != null) { | |
| 515 return _fieldConstructors[i](match.group(0), this); | |
| 516 } | |
| 517 } | |
| 518 } | |
| 519 | |
| 520 /** Polyfill for missing library function. */ | |
| 521 List _reverse(List list) { | |
| 522 // TODO(alanknight): Use standardized list reverse when implemented. | |
| 523 // See Issue 2804. | |
| 524 var result = new List(); | |
| 525 for (var i = list.length-1; i >= 0; i--) { | |
| 526 result.addLast(list[i]); | |
| 527 } | |
| 528 return result; | |
| 529 } | |
| 530 } | |
| OLD | NEW |