| Index: packages/intl/lib/src/intl/date_format_field.dart
|
| diff --git a/packages/intl/lib/src/intl/date_format_field.dart b/packages/intl/lib/src/intl/date_format_field.dart
|
| index 8df11542920e771511d36787c058921cf028d487..3c70d99055c3feb80799c7ee1e2c2276e113f588 100644
|
| --- a/packages/intl/lib/src/intl/date_format_field.dart
|
| +++ b/packages/intl/lib/src/intl/date_format_field.dart
|
| @@ -4,49 +4,48 @@
|
|
|
| part of intl;
|
|
|
| -/**
|
| - * This is a private class internal to DateFormat which is used for formatting
|
| - * particular fields in a template. e.g. if the format is hh:mm:ss then the
|
| - * fields would be "hh", ":", "mm", ":", and "ss". Each type of field knows
|
| - * how to format that portion of a date.
|
| - */
|
| +/// This is a private class internal to DateFormat which is used for formatting
|
| +/// particular fields in a template. e.g. if the format is hh:mm:ss then the
|
| +/// fields would be "hh", ":", "mm", ":", and "ss". Each type of field knows
|
| +/// how to format that portion of a date.
|
| abstract class _DateFormatField {
|
| - /** The format string that defines us, e.g. "hh" */
|
| - String pattern;
|
| + /// The format string that defines us, e.g. "hh"
|
| + final String pattern;
|
|
|
| - /** The DateFormat that we are part of.*/
|
| + /// The DateFormat that we are part of.
|
| DateFormat parent;
|
|
|
| - _DateFormatField(this.pattern, this.parent);
|
| + /// Trimmed version of [pattern].
|
| + String _trimmedPattern;
|
| +
|
| + _DateFormatField(this.pattern, this.parent) {
|
| + _trimmedPattern = pattern.trim();
|
| + }
|
|
|
| - /**
|
| - * Return the width of [pattern]. Different widths represent different
|
| - * formatting options. See the comment for DateFormat for details.
|
| - */
|
| + /// Return the width of [pattern]. Different widths represent different
|
| + /// formatting options. See the comment for DateFormat for details.
|
| int get width => pattern.length;
|
|
|
| String fullPattern() => pattern;
|
|
|
| String toString() => pattern;
|
|
|
| - /** Format date according to our specification and return the result. */
|
| + /// Format date according to our specification and return the result.
|
| String format(DateTime date) {
|
| // Default implementation in the superclass, works for both types of
|
| // literal patterns, and is overridden by _DateFormatPatternField.
|
| return pattern;
|
| }
|
|
|
| - /** Abstract method for subclasses to implementing parsing for their format.*/
|
| + /// Abstract method for subclasses to implementing parsing for their format.
|
| void parse(_Stream input, _DateBuilder dateFields);
|
|
|
| - /**
|
| - * Abstract method for subclasses to implementing 'loose' parsing for
|
| - * their format, accepting input case-insensitively, and allowing some
|
| - * delimiters to be skipped.
|
| - */
|
| + /// Abstract method for subclasses to implementing 'loose' parsing for
|
| + /// their format, accepting input case-insensitively, and allowing some
|
| + /// delimiters to be skipped.
|
| void parseLoose(_Stream input, _DateBuilder dateFields);
|
|
|
| - /** Parse a literal field. We just look for the exact input. */
|
| + /// Parse a literal field. We just look for the exact input.
|
| void parseLiteral(_Stream input) {
|
| var found = input.read(width);
|
| if (found != pattern) {
|
| @@ -54,32 +53,41 @@ abstract class _DateFormatField {
|
| }
|
| }
|
|
|
| - /**
|
| - * Parse a literal field. We accept either an exact match, or an arbitrary
|
| - * amount of whitespace.
|
| - */
|
| + /// Parse a literal field. We accept either an exact match, or an arbitrary
|
| + /// amount of whitespace.
|
| + ///
|
| + /// Any whitespace which occurs before or after the literal field is trimmed
|
| + /// from the input stream. Any leading or trailing whitespace in the literal
|
| + /// field's format specification is also trimmed before matching is
|
| + /// attempted. Therefore, leading and trailing whitespace is optional, and
|
| + /// arbitrary additional whitespace may be added before/after the literal.
|
| void parseLiteralLoose(_Stream input) {
|
| - var found = input.peek(width);
|
| - if (found == pattern) {
|
| - input.read(width);
|
| + _trimWhitespace(input);
|
| +
|
| + var found = input.peek(_trimmedPattern.length);
|
| + if (found == _trimmedPattern) {
|
| + input.read(_trimmedPattern.length);
|
| }
|
| +
|
| + _trimWhitespace(input);
|
| + }
|
| +
|
| + void _trimWhitespace(_Stream input) {
|
| while (!input.atEnd() && input.peek().trim().isEmpty) {
|
| input.read();
|
| }
|
| }
|
|
|
| - /** Throw a format exception with an error message indicating the position.*/
|
| + /// Throw a format exception with an error message indicating the position.
|
| void throwFormatException(_Stream stream) {
|
| throw new FormatException("Trying to read $this from ${stream.contents} "
|
| "at position ${stream.index}");
|
| }
|
| }
|
|
|
| -/**
|
| - * Represents a literal field - a sequence of characters that doesn't
|
| - * change according to the date's data. As such, the implementation
|
| - * is extremely simple.
|
| - */
|
| +/// Represents a literal field - a sequence of characters that doesn't
|
| +/// change according to the date's data. As such, the implementation
|
| +/// is extremely simple.
|
| class _DateFormatLiteralField extends _DateFormatField {
|
| _DateFormatLiteralField(pattern, parent) : super(pattern, parent);
|
|
|
| @@ -91,18 +99,16 @@ class _DateFormatLiteralField extends _DateFormatField {
|
| parseLiteralLoose(input);
|
| }
|
|
|
| -/**
|
| - * Represents a literal field with quoted characters in it. This is
|
| - * only slightly more complex than a _DateFormatLiteralField.
|
| - */
|
| +/// Represents a literal field with quoted characters in it. This is
|
| +/// only slightly more complex than a _DateFormatLiteralField.
|
| class _DateFormatQuotedField extends _DateFormatField {
|
| String _fullPattern;
|
|
|
| String fullPattern() => _fullPattern;
|
|
|
| - _DateFormatQuotedField(pattern, parent) : super(pattern, parent) {
|
| + _DateFormatQuotedField(pattern, parent)
|
| + : super(_patchQuotes(pattern), parent) {
|
| _fullPattern = pattern;
|
| - patchQuotes();
|
| }
|
|
|
| parse(_Stream input, _DateBuilder dateFields) {
|
| @@ -112,30 +118,28 @@ class _DateFormatQuotedField extends _DateFormatField {
|
| parseLoose(_Stream input, _DateBuilder dateFields) =>
|
| parseLiteralLoose(input);
|
|
|
| - void patchQuotes() {
|
| + static final _twoEscapedQuotes = new RegExp(r"''");
|
| +
|
| + static String _patchQuotes(String pattern) {
|
| if (pattern == "''") {
|
| - pattern = "'";
|
| + return "'";
|
| } else {
|
| - pattern = pattern.substring(1, pattern.length - 1);
|
| - var twoEscapedQuotes = new RegExp(r"''");
|
| - pattern = pattern.replaceAll(twoEscapedQuotes, "'");
|
| + return pattern
|
| + .substring(1, pattern.length - 1)
|
| + .replaceAll(_twoEscapedQuotes, "'");
|
| }
|
| }
|
| }
|
|
|
| -/**
|
| - * A field that parses "loosely", meaning that we'll accept input that is
|
| - * missing delimiters, has upper/lower case mixed up, and might not strictly
|
| - * conform to the pattern, e.g. the pattern calls for Sep we might accept
|
| - * sep, september, sEPTember. Doesn't affect numeric fields.
|
| - */
|
| +/// A field that parses "loosely", meaning that we'll accept input that is
|
| +/// missing delimiters, has upper/lower case mixed up, and might not strictly
|
| +/// conform to the pattern, e.g. the pattern calls for Sep we might accept
|
| +/// sep, september, sEPTember. Doesn't affect numeric fields.
|
| class _LoosePatternField extends _DateFormatPatternField {
|
| _LoosePatternField(String pattern, parent) : super(pattern, parent);
|
|
|
| - /**
|
| - * Parse from a list of possibilities, but case-insensitively.
|
| - * Assumes that input is lower case.
|
| - */
|
| + /// Parse from a list of possibilities, but case-insensitively.
|
| + /// Assumes that input is lower case.
|
| int parseEnumeratedString(_Stream input, List possibilities) {
|
| var lowercasePossibilities =
|
| possibilities.map((x) => x.toLowerCase()).toList();
|
| @@ -146,10 +150,8 @@ class _LoosePatternField extends _DateFormatPatternField {
|
| }
|
| }
|
|
|
| - /**
|
| - * Parse a month name, case-insensitively, and set it in [dateFields].
|
| - * Assumes that [input] is lower case.
|
| - */
|
| + /// Parse a month name, case-insensitively, and set it in [dateFields].
|
| + /// Assumes that [input] is lower case.
|
| void parseMonth(input, dateFields) {
|
| if (width <= 2) {
|
| handleNumericField(input, dateFields.setMonth);
|
| @@ -163,12 +165,11 @@ class _LoosePatternField extends _DateFormatPatternField {
|
| return;
|
| }
|
| }
|
| + throwFormatException(input);
|
| }
|
|
|
| - /**
|
| - * Parse a standalone day name, case-insensitively.
|
| - * Assumes that input is lower case. Doesn't do anything
|
| - */
|
| + /// Parse a standalone day name, case-insensitively.
|
| + /// Assumes that input is lower case. Doesn't do anything
|
| void parseStandaloneDay(input) {
|
| // This is ignored, but we still have to skip over it the correct amount.
|
| if (width <= 2) {
|
| @@ -187,13 +188,11 @@ class _LoosePatternField extends _DateFormatPatternField {
|
| }
|
| }
|
|
|
| - /**
|
| - * Parse a standalone month name, case-insensitively.
|
| - * Assumes that input is lower case. Doesn't do anything
|
| - */
|
| + /// Parse a standalone month name, case-insensitively, and set it in
|
| + /// [dateFields]. Assumes that input is lower case.
|
| void parseStandaloneMonth(input, dateFields) {
|
| if (width <= 2) {
|
| - handleNumericField(input, (x) => x);
|
| + handleNumericField(input, dateFields.setMonth);
|
| return;
|
| }
|
| var possibilities = [
|
| @@ -207,12 +206,11 @@ class _LoosePatternField extends _DateFormatPatternField {
|
| return;
|
| }
|
| }
|
| + throwFormatException(input);
|
| }
|
|
|
| - /**
|
| - * Parse a day of the week name, case-insensitively.
|
| - * Assumes that input is lower case. Doesn't do anything
|
| - */
|
| + /// Parse a day of the week name, case-insensitively.
|
| + /// Assumes that input is lower case. Doesn't do anything
|
| void parseDayOfWeek(_Stream input) {
|
| // This is IGNORED, but we still have to skip over it the correct amount.
|
| if (width <= 2) {
|
| @@ -237,32 +235,26 @@ class _LoosePatternField extends _DateFormatPatternField {
|
| class _DateFormatPatternField extends _DateFormatField {
|
| _DateFormatPatternField(pattern, parent) : super(pattern, parent);
|
|
|
| - /** Format date according to our specification and return the result. */
|
| + /// Format date according to our specification and return the result.
|
| String format(DateTime date) {
|
| return formatField(date);
|
| }
|
|
|
| - /**
|
| - * Parse the date according to our specification and put the result
|
| - * into the correct place in dateFields.
|
| - */
|
| + /// Parse the date according to our specification and put the result
|
| + /// into the correct place in dateFields.
|
| void parse(_Stream input, _DateBuilder dateFields) {
|
| parseField(input, dateFields);
|
| }
|
|
|
| - /**
|
| - * Parse the date according to our specification and put the result
|
| - * into the correct place in dateFields. Allow looser parsing, accepting
|
| - * case-insensitive input and skipped delimiters.
|
| - */
|
| + /// Parse the date according to our specification and put the result
|
| + /// into the correct place in dateFields. Allow looser parsing, accepting
|
| + /// case-insensitive input and skipped delimiters.
|
| void parseLoose(_Stream input, _DateBuilder dateFields) {
|
| new _LoosePatternField(pattern, parent).parse(input, dateFields);
|
| }
|
|
|
| - /**
|
| - * Parse a field representing part of a date pattern. Note that we do not
|
| - * return a value, but rather build up the result in [builder].
|
| - */
|
| + /// Parse a field representing part of a date pattern. Note that we do not
|
| + /// return a value, but rather build up the result in [builder].
|
| void parseField(_Stream input, _DateBuilder builder) {
|
| try {
|
| switch (pattern[0]) {
|
| @@ -283,6 +275,7 @@ class _DateFormatPatternField extends _DateFormatField {
|
| parseDayOfWeek(input);
|
| break;
|
| case 'G':
|
| + parseEra(input);
|
| break; // era
|
| case 'h':
|
| parse1To12Hours(input, builder);
|
| @@ -330,7 +323,7 @@ class _DateFormatPatternField extends _DateFormatField {
|
| }
|
| }
|
|
|
| - /** Formatting logic if we are of type FIELD */
|
| + /// Formatting logic if we are of type FIELD
|
| String formatField(DateTime date) {
|
| switch (pattern[0]) {
|
| case 'a':
|
| @@ -378,8 +371,8 @@ class _DateFormatPatternField extends _DateFormatField {
|
| }
|
| }
|
|
|
| - /** Return the symbols for our current locale. */
|
| - DateSymbols get symbols => dateTimeSymbols[parent.locale];
|
| + /// Return the symbols for our current locale.
|
| + DateSymbols get symbols => parent.dateSymbols;
|
|
|
| formatEra(DateTime date) {
|
| var era = date.year > 0 ? 1 : 0;
|
| @@ -395,29 +388,25 @@ class _DateFormatPatternField extends _DateFormatField {
|
| return width == 2 ? padTo(2, year % 100) : padTo(width, year);
|
| }
|
|
|
| - /**
|
| - * We are given [input] as a stream from which we want to read a date. We
|
| - * can't dynamically build up a date, so we are given a list [dateFields] of
|
| - * the constructor arguments and an [position] at which to set it
|
| - * (year,month,day,hour,minute,second,fractionalSecond)
|
| - * then after all parsing is done we construct a date from the arguments.
|
| - * This method handles reading any of the numeric fields. The [offset]
|
| - * argument allows us to compensate for zero-based versus one-based values.
|
| - */
|
| + /// We are given [input] as a stream from which we want to read a date. We
|
| + /// can't dynamically build up a date, so we are given a list [dateFields] of
|
| + /// the constructor arguments and an [position] at which to set it
|
| + /// (year,month,day,hour,minute,second,fractionalSecond)
|
| + /// then after all parsing is done we construct a date from the arguments.
|
| + /// This method handles reading any of the numeric fields. The [offset]
|
| + /// argument allows us to compensate for zero-based versus one-based values.
|
| void handleNumericField(_Stream input, Function setter, [int offset = 0]) {
|
| var result = input.nextInteger();
|
| if (result == null) throwFormatException(input);
|
| setter(result + offset);
|
| }
|
|
|
| - /**
|
| - * We are given [input] as a stream from which we want to read a date. We
|
| - * can't dynamically build up a date, so we are given a list [dateFields] of
|
| - * the constructor arguments and an [position] at which to set it
|
| - * (year,month,day,hour,minute,second,fractionalSecond)
|
| - * then after all parsing is done we construct a date from the arguments.
|
| - * This method handles reading any of string fields from an enumerated set.
|
| - */
|
| + /// We are given [input] as a stream from which we want to read a date. We
|
| + /// can't dynamically build up a date, so we are given a list [dateFields] of
|
| + /// the constructor arguments and an [position] at which to set it
|
| + /// (year,month,day,hour,minute,second,fractionalSecond)
|
| + /// then after all parsing is done we construct a date from the arguments.
|
| + /// This method handles reading any of string fields from an enumerated set.
|
| int parseEnumeratedString(_Stream input, List possibilities) {
|
| var results = new _Stream(possibilities)
|
| .findIndexes((each) => input.peek(each.length) == each);
|
| @@ -477,7 +466,7 @@ class _DateFormatPatternField extends _DateFormatField {
|
|
|
| String formatAmPm(DateTime date) {
|
| var hours = date.hour;
|
| - var index = (date.hour >= 12) && (date.hour < 24) ? 1 : 0;
|
| + var index = (hours >= 12) && (hours < 24) ? 1 : 0;
|
| var ampm = symbols.AMPMS;
|
| return ampm[index];
|
| }
|
| @@ -573,37 +562,37 @@ class _DateFormatPatternField extends _DateFormatField {
|
|
|
| String formatQuarter(DateTime date) {
|
| var quarter = ((date.month - 1) / 3).truncate();
|
| - if (width < 4) {
|
| - return symbols.SHORTQUARTERS[quarter];
|
| - } else {
|
| - return symbols.QUARTERS[quarter];
|
| + switch (width) {
|
| + case 4:
|
| + return symbols.QUARTERS[quarter];
|
| + case 3:
|
| + return symbols.SHORTQUARTERS[quarter];
|
| + default:
|
| + return padTo(width, quarter + 1);
|
| }
|
| }
|
| +
|
| String formatDayOfMonth(DateTime date) {
|
| return padTo(width, date.day);
|
| }
|
|
|
| String formatDayOfYear(DateTime date) => padTo(width, dayNumberInYear(date));
|
|
|
| - /** Return the ordinal day, i.e. the day number in the year. */
|
| + /// Return the ordinal day, i.e. the day number in the year.
|
| int dayNumberInYear(DateTime date) {
|
| if (date.month == 1) return date.day;
|
| if (date.month == 2) return date.day + 31;
|
| return ordinalDayFromMarchFirst(date) + 59 + (isLeapYear(date) ? 1 : 0);
|
| }
|
|
|
| - /**
|
| - * Return the day of the year counting March 1st as 1, after which the
|
| - * number of days per month is constant, so it's easier to calculate.
|
| - * Formula from http://en.wikipedia.org/wiki/Ordinal_date
|
| - */
|
| + /// Return the day of the year counting March 1st as 1, after which the
|
| + /// number of days per month is constant, so it's easier to calculate.
|
| + /// Formula from http://en.wikipedia.org/wiki/Ordinal_date
|
| int ordinalDayFromMarchFirst(DateTime date) =>
|
| ((30.6 * date.month) - 91.4).floor() + date.day;
|
|
|
| - /**
|
| - * Return true if this is a leap year. Rely on [DateTime] to do the
|
| - * underlying calculation, even though it doesn't expose the test to us.
|
| - */
|
| + /// Return true if this is a leap year. Rely on [DateTime] to do the
|
| + /// underlying calculation, even though it doesn't expose the test to us.
|
| bool isLeapYear(DateTime date) {
|
| var feb29 = new DateTime(date.year, 2, 29);
|
| return feb29.month == 2;
|
| @@ -611,9 +600,8 @@ class _DateFormatPatternField extends _DateFormatField {
|
|
|
| String formatDayOfWeek(DateTime date) {
|
| // Note that Dart's weekday returns 1 for Monday and 7 for Sunday.
|
| - return (width >= 4
|
| - ? symbols.WEEKDAYS
|
| - : symbols.SHORTWEEKDAYS)[(date.weekday) % 7];
|
| + return (width >= 4 ? symbols.WEEKDAYS : symbols.SHORTWEEKDAYS)[
|
| + (date.weekday) % 7];
|
| }
|
|
|
| void parseDayOfWeek(_Stream input) {
|
| @@ -622,6 +610,11 @@ class _DateFormatPatternField extends _DateFormatField {
|
| parseEnumeratedString(input, possibilities);
|
| }
|
|
|
| + void parseEra(_Stream input) {
|
| + var possibilities = width >= 4 ? symbols.ERANAMES : symbols.ERAS;
|
| + parseEnumeratedString(input, possibilities);
|
| + }
|
| +
|
| String formatMinutes(DateTime date) {
|
| return padTo(width, date.minute);
|
| }
|
| @@ -643,18 +636,8 @@ class _DateFormatPatternField extends _DateFormatField {
|
| throw new UnimplementedError();
|
| }
|
|
|
| - /**
|
| - * Return a string representation of the object padded to the left with
|
| - * zeros. Primarily useful for numbers.
|
| - */
|
| - String padTo(int width, Object toBePrinted) {
|
| - var basicString = toBePrinted.toString();
|
| - if (basicString.length >= width) return basicString;
|
| - var buffer = new StringBuffer();
|
| - for (var i = 0; i < width - basicString.length; i++) {
|
| - buffer.write('0');
|
| - }
|
| - buffer.write(basicString);
|
| - return buffer.toString();
|
| - }
|
| + /// Return a string representation of the object padded to the left with
|
| + /// zeros. Primarily useful for numbers.
|
| + static String padTo(int width, Object toBePrinted) =>
|
| + '$toBePrinted'.padLeft(width, '0');
|
| }
|
|
|