| Index: packages/intl/lib/src/intl/date_format_helpers.dart
|
| diff --git a/packages/intl/lib/src/intl/date_format_helpers.dart b/packages/intl/lib/src/intl/date_format_helpers.dart
|
| index 6a6e68c8f46d593a126ee0609672236d30479458..05a6a4927d4d72deb3d84f97993cdf79467bf0fa 100644
|
| --- a/packages/intl/lib/src/intl/date_format_helpers.dart
|
| +++ b/packages/intl/lib/src/intl/date_format_helpers.dart
|
| @@ -4,10 +4,8 @@
|
|
|
| part of intl;
|
|
|
| -/**
|
| - * A class for holding onto the data for a date so that it can be built
|
| - * up incrementally.
|
| - */
|
| +/// A class for holding onto the data for a date so that it can be built
|
| +/// up incrementally.
|
| class _DateBuilder {
|
| // Default the date values to the EPOCH so that there's a valid date
|
| // in case the format doesn't set them.
|
| @@ -26,32 +24,36 @@ class _DateBuilder {
|
| void setYear(x) {
|
| year = x;
|
| }
|
| +
|
| void setMonth(x) {
|
| month = x;
|
| }
|
| +
|
| void setDay(x) {
|
| day = x;
|
| }
|
| +
|
| void setHour(x) {
|
| hour = x;
|
| }
|
| +
|
| void setMinute(x) {
|
| minute = x;
|
| }
|
| +
|
| void setSecond(x) {
|
| second = x;
|
| }
|
| +
|
| void setFractionalSecond(x) {
|
| fractionalSecond = x;
|
| }
|
|
|
| get hour24 => pm ? hour + 12 : hour;
|
|
|
| - /**
|
| - * Verify that we correspond to a valid date. This will reject out of
|
| - * range values, even if the DateTime constructor would accept them. An
|
| - * invalid message will result in throwing a [FormatException].
|
| - */
|
| + /// Verify that we correspond to a valid date. This will reject out of
|
| + /// range values, even if the DateTime constructor would accept them. An
|
| + /// invalid message will result in throwing a [FormatException].
|
| verify(String s) {
|
| _verify(month, 1, 12, "month", s);
|
| _verify(hour24, 0, 23, "hour", s);
|
| @@ -64,23 +66,24 @@ class _DateBuilder {
|
| // check the year, which we otherwise can't verify, and the hours,
|
| // which will catch cases like "14:00:00 PM".
|
| var date = asDate();
|
| - _verify(hour24, date.hour, date.hour, "hour", s);
|
| - _verify(day, date.day, date.day, "day", s);
|
| - _verify(year, date.year, date.year, "year", s);
|
| + _verify(hour24, date.hour, date.hour, "hour", s, date);
|
| + _verify(day, date.day, date.day, "day", s, date);
|
| + _verify(year, date.year, date.year, "year", s, date);
|
| }
|
|
|
| - _verify(int value, int min, int max, String desc, String originalInput) {
|
| + _verify(int value, int min, int max, String desc, String originalInput,
|
| + [DateTime parsed]) {
|
| if (value < min || value > max) {
|
| + var parsedDescription = parsed == null ? "" : " Date parsed as $parsed.";
|
| throw new FormatException(
|
| - "Error parsing $originalInput, invalid $desc value: $value");
|
| + "Error parsing $originalInput, invalid $desc value: $value."
|
| + " Expected value between $min and $max.$parsedDescription");
|
| }
|
| }
|
|
|
| - /**
|
| - * Return a date built using our values. If no date portion is set,
|
| - * use the "Epoch" of January 1, 1970.
|
| - */
|
| - DateTime asDate({retry: true}) {
|
| + /// Return a date built using our values. If no date portion is set,
|
| + /// use the "Epoch" of January 1, 1970.
|
| + DateTime asDate({int retries: 10}) {
|
| // TODO(alanknight): Validate the date, especially for things which
|
| // can crash the VM, e.g. large month values.
|
| var result;
|
| @@ -90,22 +93,21 @@ class _DateBuilder {
|
| } else {
|
| result = new DateTime(
|
| year, month, day, hour24, minute, second, fractionalSecond);
|
| - // TODO(alanknight): Issue 15560 means non-UTC dates occasionally come
|
| - // out in UTC. If that happens, retry once. This will always happen if
|
| - // the local time zone is UTC, but that's ok.
|
| - if (result.toUtc() == result) {
|
| - result = asDate(retry: false);
|
| + // TODO(alanknight): Issue 15560 means non-UTC dates occasionally come out
|
| + // in UTC, or, alternatively, are constructed as if in UTC and then have
|
| + // the offset subtracted. If that happens, retry, several times if
|
| + // necessary.
|
| + if (retries > 0 && (result.hour != hour24 || result.day != day)) {
|
| + result = asDate(retries: retries - 1);
|
| }
|
| }
|
| return result;
|
| }
|
| }
|
|
|
| -/**
|
| - * A simple and not particularly general stream class to make parsing
|
| - * dates from strings simpler. It is general enough to operate on either
|
| - * lists or strings.
|
| - */
|
| +/// A simple and not particularly general stream class to make parsing
|
| +/// dates from strings simpler. It is general enough to operate on either
|
| +/// lists or strings.
|
| // TODO(alanknight): With the improvements to the collection libraries
|
| // since this was written we might be able to get rid of it entirely
|
| // in favor of e.g. aString.split('') giving us an iterable of one-character
|
| @@ -121,33 +123,29 @@ class _Stream {
|
|
|
| next() => contents[index++];
|
|
|
| - /**
|
| - * Return the next [howMany] items, or as many as there are remaining.
|
| - * Advance the stream by that many positions.
|
| - */
|
| + /// Return the next [howMany] items, or as many as there are remaining.
|
| + /// Advance the stream by that many positions.
|
| read([int howMany = 1]) {
|
| var result = peek(howMany);
|
| index += howMany;
|
| return result;
|
| }
|
|
|
| - /**
|
| - * Does the input start with the given string, if we start from the
|
| - * current position.
|
| - */
|
| + /// Does the input start with the given string, if we start from the
|
| + /// current position.
|
| bool startsWith(String pattern) {
|
| if (contents is String) return contents.startsWith(pattern, index);
|
| return pattern == peek(pattern.length);
|
| }
|
|
|
| - /**
|
| - * Return the next [howMany] items, or as many as there are remaining.
|
| - * Does not modify the stream position.
|
| - */
|
| + /// Return the next [howMany] items, or as many as there are remaining.
|
| + /// Does not modify the stream position.
|
| peek([int howMany = 1]) {
|
| var result;
|
| if (contents is String) {
|
| - result = contents.substring(index, min(index + howMany, contents.length));
|
| + String stringContents = contents;
|
| + result = stringContents.substring(
|
| + index, min(index + howMany, stringContents.length));
|
| } else {
|
| // Assume List
|
| result = contents.sublist(index, index + howMany);
|
| @@ -155,13 +153,11 @@ class _Stream {
|
| return result;
|
| }
|
|
|
| - /** Return the remaining contents of the stream */
|
| + /// Return the remaining contents of the stream
|
| rest() => peek(contents.length - index);
|
|
|
| - /**
|
| - * Find the index of the first element for which [f] returns true.
|
| - * Advances the stream to that position.
|
| - */
|
| + /// Find the index of the first element for which [f] returns true.
|
| + /// Advances the stream to that position.
|
| int findIndex(Function f) {
|
| while (!atEnd()) {
|
| if (f(next())) return index - 1;
|
| @@ -169,10 +165,8 @@ class _Stream {
|
| return null;
|
| }
|
|
|
| - /**
|
| - * Find the indexes of all the elements for which [f] returns true.
|
| - * Leaves the stream positioned at the end.
|
| - */
|
| + /// Find the indexes of all the elements for which [f] returns true.
|
| + /// Leaves the stream positioned at the end.
|
| List findIndexes(Function f) {
|
| var results = [];
|
| while (!atEnd()) {
|
| @@ -181,11 +175,9 @@ class _Stream {
|
| return results;
|
| }
|
|
|
| - /**
|
| - * Assuming that the contents are characters, read as many digits as we
|
| - * can see and then return the corresponding integer. Advance the stream.
|
| - */
|
| - var digitMatcher = new RegExp(r'\d+');
|
| + /// Assuming that the contents are characters, read as many digits as we
|
| + /// can see and then return the corresponding integer. Advance the stream.
|
| + var digitMatcher = new RegExp(r'^\d+');
|
| int nextInteger() {
|
| var string = digitMatcher.stringMatch(rest());
|
| if (string == null || string.isEmpty) return null;
|
|
|