Merge pull request #290 from jamesderlin/copybara-sync
Copybara sync
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e0658d4..56e3dae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,18 @@
+## 0.17.0
+ * **Breaking Change** [#123][]: Fix parsing of two-digit years to match the
+ documented behavior. Previously a two-digit year would be parsed to a value
+ in the range [0, 99]. Now it is parsed relative to the current date,
+ returning a value between 80 years in the past and 20 years in the future.
+ * Use package:clock to get the current date/time.
+ * Fix some more analysis complaints.
+ * Update documentation to indicate that time zone specifiers are not yet
+ implemented [#264][].
+
## 0.16.2
* Fix bug with dates in January being treated as ordinal. e.g. 2020-01-32 would
be accepted as valid and the day treated as day-of-year.
+ * Compact currency formats will avoid displaying unecessary trailing zeros
+ in compact formats for currencies which specify decimal places.
## 0.16.1
* Add an analysis_options.yaml and fix or suppress all the complaints from it.
@@ -351,3 +363,6 @@
* Handle two different messages with the same text.
* Allow complex string literals in arguments (e.g. multi-line)
+
+[#123]: https://github.com/dart-lang/intl/issues/123
+[#264]: https://github.com/dart-lang/intl/issues/264
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 76203fd..81ede1d 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,9 +1,13 @@
analyzer:
+ language:
+ strict-raw-types: true
+
exclude:
- test/number_format_compact_icu_test.dart # TODO(240): Update for FFI changes
errors:
dead_code: error
+ missing_required_param: error
override_on_non_overriding_method: error
unused_element: error
unused_import: error
@@ -64,7 +68,6 @@
- prefer_is_empty
- prefer_is_not_empty
- prefer_null_aware_operators
- - prefer_single_quotes
- prefer_typing_uninitialized_variables
- recursive_getters
- slash_for_doc_comments
diff --git a/lib/intl.dart b/lib/intl.dart
index 102e2dc..98f3899 100644
--- a/lib/intl.dart
+++ b/lib/intl.dart
@@ -23,6 +23,8 @@
import 'dart:convert';
import 'dart:math';
+import 'package:clock/clock.dart';
+
import 'date_symbols.dart';
import 'number_symbols.dart';
import 'number_symbols_data.dart';
@@ -51,7 +53,7 @@
/// args: [date],
/// desc: 'Indicate the current date',
/// examples: const {'date' : 'June 8, 2012'});
-/// print(today(new DateTime.now().toString());
+/// print(today(DateTime.now().toString());
///
/// howManyPeople(numberOfPeople, place) => Intl.plural(numberOfPeople,
/// zero: 'I see no one at all in $place.',
@@ -75,7 +77,7 @@
///
/// To temporarily use a locale other than the default, use the `withLocale`
/// function.
-/// var todayString = new DateFormat('pt_BR').format(new DateTime.now());
+/// var todayString = DateFormat('pt_BR').format(DateTime.now());
/// print(withLocale('pt_BR', () => today(todayString));
///
/// See `tests/message_format_test.dart` for more examples.
@@ -566,7 +568,7 @@
///
/// For example
///
- /// Intl.withLocale('fr', () => new NumberFormat.format(123456));
+ /// Intl.withLocale('fr', () => NumberFormat.format(123456));
///
/// or
///
@@ -575,7 +577,7 @@
/// name: 'hello',
/// args: [name],
/// desc: 'Say Hello');
- /// Intl.withLocale('zh', new Timer(new Duration(milliseconds:10),
+ /// Intl.withLocale('zh', Timer(Duration(milliseconds:10),
/// () => print(hello('World')));
static dynamic withLocale<T>(String locale, T Function() function) {
// TODO(alanknight): Make this return T. This requires work because T might
diff --git a/lib/message_format.dart b/lib/message_format.dart
index 86d4d7b..44b68e9 100644
--- a/lib/message_format.dart
+++ b/lib/message_format.dart
@@ -604,10 +604,7 @@
argumentName = match.group(1);
return '';
});
- // The lint complaints about "Omit type annotations for local variables"
- // But if I make this `var` then it assumes that the value is a
- // always a string, but it is not.
- Map<String, Object> result = {'argumentName': argumentName};
+ var result = <String, Object>{'argumentName': argumentName};
var parts = _extractParts(pattern);
// Looking for (key block)+ sequence. One of the keys has to be "other".
diff --git a/lib/src/intl/compact_number_format.dart b/lib/src/intl/compact_number_format.dart
index d696685..162d739 100644
--- a/lib/src/intl/compact_number_format.dart
+++ b/lib/src/intl/compact_number_format.dart
@@ -57,8 +57,8 @@
/// 4: '00K'
/// which matches
///
-/// new _CompactStyle(pattern: '00K', normalizedExponent: 4, divisor: 1000,
-/// expectedDigits: 1, prefix: '', suffix: 'K');
+/// _CompactStyle(pattern: '00K', normalizedExponent: 4, divisor: 1000,
+/// expectedDigits: 1, prefix: '', suffix: 'K');
///
/// where expectedDigits is the number of zeros.
class _CompactStyle extends _CompactStyleBase {
@@ -244,8 +244,8 @@
String format(number) {
_style = _styleFor(number);
- var divisor = _style.printsAsIs ? 1 : _style.divisor;
- var numberToFormat = _divide(number, divisor);
+ final divisor = _style.printsAsIs ? 1 : _style.divisor;
+ final numberToFormat = _divide(number, divisor);
var formatted = super.format(numberToFormat);
var prefix = _style.prefix;
var suffix = _style.suffix;
@@ -259,7 +259,7 @@
prefix = prefix.replaceFirst('\u00a4', currencySymbol);
suffix = suffix.replaceFirst('\u00a4', currencySymbol);
}
- var withExtras = '$prefix$formatted$suffix';
+ final withExtras = '$prefix$formatted$suffix';
_style = null;
return withExtras;
}
@@ -267,7 +267,7 @@
/// How many digits after the decimal place should we display, given that
/// there are [remainingSignificantDigits] left to show.
int _fractionDigitsAfter(int remainingSignificantDigits) {
- var newFractionDigits =
+ final newFractionDigits =
super._fractionDigitsAfter(remainingSignificantDigits);
// For non-currencies, or for currencies if the numbers are large enough to
// compact, always use the number of significant digits and ignore
@@ -284,6 +284,19 @@
}
}
+ /// Defines minimumFractionDigits based on current style being formatted.
+ @override
+ int get minimumFractionDigits {
+ if (!_isForCurrency ||
+ !significantDigitsInUse ||
+ _style == null ||
+ _style.isFallback) {
+ return super.minimumFractionDigits;
+ } else {
+ return 0;
+ }
+ }
+
/// Divide numbers that may not have a division operator (e.g. Int64).
///
/// Only used for powers of 10, so we require an integer denominator.
diff --git a/lib/src/intl/date_format.dart b/lib/src/intl/date_format.dart
index a6b0e24..282d3ef 100644
--- a/lib/src/intl/date_format.dart
+++ b/lib/src/intl/date_format.dart
@@ -23,7 +23,7 @@
/// initialization. e.g.
///
/// ```dart
-/// print(new DateFormat.yMMMd().format(new DateTime.now()));
+/// print(DateFormat.yMMMd().format(DateTime.now()));
/// ```
///
/// But for other locales, the formatting data for the locale must be
@@ -108,24 +108,26 @@
/// HOUR j
/// HOUR_MINUTE jm
/// HOUR_MINUTE_SECOND jms
-/// HOUR_MINUTE_GENERIC_TZ jmv
-/// HOUR_MINUTE_TZ jmz
-/// HOUR_GENERIC_TZ jv
-/// HOUR_TZ jz
+/// HOUR_MINUTE_GENERIC_TZ jmv (not yet implemented)
+/// HOUR_MINUTE_TZ jmz (not yet implemented)
+/// HOUR_GENERIC_TZ jv (not yet implemented)
+/// HOUR_TZ jz (not yet implemented)
/// MINUTE m
/// MINUTE_SECOND ms
/// SECOND s
+//
+// TODO(https://github.com/dart-lang/intl/issues/74): Update table above.
///
/// Examples Using the US Locale:
///
/// Pattern Result
/// ---------------- -------
-/// new DateFormat.yMd() -> 7/10/1996
-/// new DateFormat('yMd') -> 7/10/1996
-/// new DateFormat.yMMMMd('en_US') -> July 10, 1996
-/// new DateFormat.jm() -> 5:08 PM
-/// new DateFormat.yMd().add_jm() -> 7/10/1996 5:08 PM
-/// new DateFormat.Hm() -> 17:08 // force 24 hour time
+/// DateFormat.yMd() -> 7/10/1996
+/// DateFormat('yMd') -> 7/10/1996
+/// DateFormat.yMMMMd('en_US') -> July 10, 1996
+/// DateFormat.jm() -> 5:08 PM
+/// DateFormat.yMd().add_jm() -> 7/10/1996 5:08 PM
+/// DateFormat.Hm() -> 17:08 // force 24 hour time
///
/// Explicit Pattern Syntax: Formats can also be specified with a pattern
/// string. This can be used for formats that don't have a skeleton available,
@@ -156,13 +158,20 @@
/// a am/pm marker (Text) PM
/// k hour in day (1~24) (Number) 24
/// K hour in am/pm (0~11) (Number) 0
-/// z time zone (Text) Pacific Standard Time
-/// Z time zone (RFC 822) (Number) -0800
-/// v time zone (generic) (Text) Pacific Time
/// Q quarter (Text) Q3
/// ' escape for text (Delimiter) 'Date='
/// '' single quote (Literal) 'o''clock'
///
+// TODO(https://github.com/dart-lang/intl/issues/74): Merge tables.
+//
+/// The following characters are reserved and currently are unimplemented:
+///
+/// Symbol Meaning Presentation Example
+/// ------ ------- ------------ -------
+/// z time zone (Text) Pacific Standard Time
+/// Z time zone (RFC 822) (Number) -0800
+/// v time zone (generic) (Text) Pacific Time
+///
/// The count of pattern letters determine the format.
///
/// **Text**:
@@ -190,23 +199,29 @@
///
/// Format Pattern Result
/// -------------- -------
-/// 'yyyy.MM.dd G 'at' HH:mm:ss vvvv' 1996.07.10 AD at 15:08:56 Pacific Time
/// 'EEE, MMM d, ''yy' Wed, Jul 10, '96
/// 'h:mm a' 12:08 PM
-/// 'hh 'o''clock' a, zzzz' 12 o'clock PM, Pacific Daylight Time
-/// 'K:mm a, vvv' 0:00 PM, PT
/// 'yyyyy.MMMMM.dd GGG hh:mm aaa' 01996.July.10 AD 12:08 PM
+//
+// TODO(https://github.com/dart-lang/intl/issues/74): Merge tables.
+//
+// NOT YET IMPLEMENTED
+// -------------------
+// 'yyyy.MM.dd G 'at' HH:mm:ss vvvv' 1996.07.10 AD at 15:08:56 Pacific Time
+// 'hh 'o''clock' a, zzzz' 12 o'clock PM, Pacific Daylight Time
+// 'K:mm a, vvv' 0:00 PM, PT
///
/// When parsing a date string using the abbreviated year pattern ('yy'),
/// DateFormat must interpret the abbreviated year relative to some
/// century. It does this by adjusting dates to be within 80 years before and 20
/// years after the time the parse function is called. For example, using a
-/// pattern of 'MM/dd/yy' and a DateParse instance created on Jan 1, 1997,
+/// pattern of 'MM/dd/yy' and a DateFormat instance created on Jan 1, 1997,
/// the string '01/11/12' would be interpreted as Jan 11, 2012 while the string
/// '05/04/64' would be interpreted as May 4, 1964. During parsing, only
/// strings consisting of exactly two digits will be parsed into the default
/// century. Any other numeric string, such as a one digit string, a three or
-/// more digit string will be interpreted as its face value.
+/// more digit string will be interpreted as its face value. Tests that parse
+/// two-digit years can control the current date with package:clock.
///
/// If the year pattern does not have exactly two 'y' characters, the year is
/// interpreted literally, regardless of the number of digits. So using the
@@ -224,13 +239,13 @@
/// For example, in an en_US locale, specifying the skeleton
///
/// ```dart
- /// new DateFormat.yMEd();
+ /// DateFormat.yMEd();
/// ```
///
/// or the explicit
///
/// ```dart
- /// new DateFormat('EEE, M/d/y');
+ /// DateFormat('EEE, M/d/y');
/// ```
///
/// would produce the same result, a date of the form 'Wed, 6/27/2012'.
@@ -319,14 +334,14 @@
///
/// For example, this will accept
///
- /// new DateFormat.yMMMd('en_US').parseLoose('SEp 3 2014');
- /// new DateFormat.yMd('en_US').parseLoose('09 03/2014');
- /// new DateFormat.yMd('en_US').parseLoose('09 / 03 / 2014');
+ /// DateFormat.yMMMd('en_US').parseLoose('SEp 3 2014');
+ /// DateFormat.yMd('en_US').parseLoose('09 03/2014');
+ /// DateFormat.yMd('en_US').parseLoose('09 / 03 / 2014');
///
/// It will NOT accept
///
- /// // 'Sept' is not a valid month name.
- /// new DateFormat.yMMMd('en_US').parseLoose('Sept 3, 2014');
+ /// // 'Sept' is not a valid month name.
+ /// DateFormat.yMMMd('en_US').parseLoose('Sept 3, 2014');
DateTime parseLoose(String inputString, [bool utc = false]) {
try {
return _parse(inputString, utc: utc, strict: true);
@@ -356,7 +371,7 @@
///
/// If [inputString] does not match our format, throws a [FormatException].
/// This will reject dates whose values are not strictly valid, even if the
- /// DateTime constructor will accept them. It will also rejct strings with
+ /// DateTime constructor will accept them. It will also reject strings with
/// additional characters (including whitespace) after a valid date. For
/// looser parsing, use [parse].
DateTime parseStrict(String inputString, [bool utc = false]) =>
@@ -418,20 +433,20 @@
/// So,
///
/// ```dart
- /// new DateFormat.yMd('en_US')
+ /// DateFormat.yMd('en_US')
/// ```
///
/// is equivalent to
///
/// ```dart
- /// new DateFormat('yMd', 'en_US')
+ /// DateFormat('yMd', 'en_US')
/// ```
///
/// To create a compound format you can use these constructors in combination
/// with the 'add_*' methods below. e.g.
///
/// ```dart
- /// new DateFormat.yMd().add_Hms();
+ /// DateFormat.yMd().add_Hms();
/// ```
///
/// If the optional [locale] is omitted, the format will be created using the
@@ -470,9 +485,17 @@
DateFormat.j([locale]) : this('j', locale);
DateFormat.jm([locale]) : this('jm', locale);
DateFormat.jms([locale]) : this('jms', locale);
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
DateFormat.jmv([locale]) : this('jmv', locale);
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
DateFormat.jmz([locale]) : this('jmz', locale);
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
DateFormat.jv([locale]) : this('jv', locale);
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
DateFormat.jz([locale]) : this('jz', locale);
DateFormat.m([locale]) : this('m', locale);
DateFormat.ms([locale]) : this('ms', locale);
@@ -483,7 +506,7 @@
/// useful for creating compound formats. For example
///
/// ```dart
- /// new DateFormat.yMd().add_Hms();
+ /// DateFormat.yMd().add_Hms();
/// ```
///
/// would create a date format that prints both the date and the time.
@@ -521,9 +544,17 @@
DateFormat add_j() => addPattern('j');
DateFormat add_jm() => addPattern('jm');
DateFormat add_jms() => addPattern('jms');
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
DateFormat add_jmv() => addPattern('jmv');
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
DateFormat add_jmz() => addPattern('jmz');
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
DateFormat add_jv() => addPattern('jv');
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
DateFormat add_jz() => addPattern('jz');
DateFormat add_m() => addPattern('m');
DateFormat add_ms() => addPattern('ms');
@@ -565,9 +596,17 @@
static const String HOUR = 'j';
static const String HOUR_MINUTE = 'jm';
static const String HOUR_MINUTE_SECOND = 'jms';
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
static const String HOUR_MINUTE_GENERIC_TZ = 'jmv';
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
static const String HOUR_MINUTE_TZ = 'jmz';
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
static const String HOUR_GENERIC_TZ = 'jv';
+ /// NOT YET IMPLEMENTED.
+ // TODO(https://github.com/dart-lang/intl/issues/74)
static const String HOUR_TZ = 'jz';
static const String MINUTE = 'm';
static const String MINUTE_SECOND = 'ms';
diff --git a/lib/src/intl/date_format_field.dart b/lib/src/intl/date_format_field.dart
index e15e685..360998c 100644
--- a/lib/src/intl/date_format_field.dart
+++ b/lib/src/intl/date_format_field.dart
@@ -324,7 +324,7 @@
case 'v':
break; // time zone id
case 'y':
- handleNumericField(input, builder.setYear);
+ parseYear(input, builder);
break;
case 'z':
break; // time zone
@@ -414,7 +414,7 @@
///
/// 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, void Function(num) setter,
+ void handleNumericField(_Stream input, void Function(int) setter,
[int offset = 0]) {
var result = input.nextInteger(
digitMatcher: parent.digitMatcher,
@@ -443,6 +443,11 @@
return longestResult;
}
+ void parseYear(_Stream input, _DateBuilder builder) {
+ handleNumericField(input, builder.setYear);
+ builder.setHasAmbiguousCentury(width == 2);
+ }
+
String formatMonth(DateTime date) {
switch (width) {
case 5:
@@ -456,7 +461,7 @@
}
}
- void parseMonth(input, dateFields) {
+ void parseMonth(_Stream input, _DateBuilder dateFields) {
List<String> possibilities;
switch (width) {
case 5:
diff --git a/lib/src/intl/date_format_helpers.dart b/lib/src/intl/date_format_helpers.dart
index c0b7f13..be42b84 100644
--- a/lib/src/intl/date_format_helpers.dart
+++ b/lib/src/intl/date_format_helpers.dart
@@ -45,6 +45,11 @@
bool pm = false;
bool utc = false;
+ /// Whether the century portion of [year] is ambiguous.
+ ///
+ /// Ignored if `year < 0` or `year >= 100`.
+ bool _hasAmbiguousCentury = false;
+
/// The locale, kept for logging purposes when there's an error.
final String _locale;
@@ -81,19 +86,25 @@
// Functions that exist just to be closurized so we can pass them to a general
// method.
- void setYear(x) {
+ void setYear(int x) {
year = x;
}
- void setMonth(x) {
+ /// Sets whether [year] should be treated as ambiguous because it lacks a
+ /// century.
+ void setHasAmbiguousCentury(bool isAmbiguous) {
+ _hasAmbiguousCentury = isAmbiguous;
+ }
+
+ void setMonth(int x) {
month = x;
}
- void setDay(x) {
+ void setDay(int x) {
day = x;
}
- void setDayOfYear(x) {
+ void setDayOfYear(int x) {
dayOfYear = x;
}
@@ -101,19 +112,19 @@
/// the day of the month.
int get dayOrDayOfYear => dayOfYear == 0 ? day : dayOfYear;
- void setHour(x) {
+ void setHour(int x) {
hour = x;
}
- void setMinute(x) {
+ void setMinute(int x) {
minute = x;
}
- void setSecond(x) {
+ void setSecond(int x) {
second = x;
}
- void setFractionalSecond(x) {
+ void setFractionalSecond(int x) {
fractionalSecond = x;
}
@@ -171,6 +182,22 @@
}
}
+ /// Offsets a [DateTime] by a specified number of years.
+ ///
+ /// All other fields of the [DateTime] normally will remain unaffected. An
+ /// exception is if the resulting [DateTime] otherwise would represent an
+ /// invalid date (e.g. February 29 of a non-leap year).
+ DateTime _offsetYear(DateTime dateTime, int offsetYears) =>
+ _dateTimeConstructor(
+ dateTime.year + offsetYears,
+ dateTime.month,
+ dateTime.day,
+ dateTime.hour,
+ dateTime.minute,
+ dateTime.second,
+ dateTime.millisecond,
+ dateTime.isUtc);
+
/// Return a date built using our values. If no date portion is set,
/// use the 'Epoch' of January 1, 1970.
DateTime asDate({int retries = 3}) {
@@ -178,12 +205,47 @@
// can crash the VM, e.g. large month values.
if (_date != null) return _date;
- if (utc) {
- _date = _dateTimeConstructor(year, month, dayOrDayOfYear, hour24, minute,
- second, fractionalSecond, utc);
- } else {
- var preliminaryResult = _dateTimeConstructor(year, month, dayOrDayOfYear,
+ DateTime preliminaryResult;
+ final hasCentury = !_hasAmbiguousCentury || year < 0 || year >= 100;
+ if (hasCentury) {
+ preliminaryResult = _dateTimeConstructor(year, month, dayOrDayOfYear,
hour24, minute, second, fractionalSecond, utc);
+ } else {
+ var now = clock.now();
+ if (utc) {
+ now = now.toUtc();
+ }
+
+ const lookBehindYears = 80;
+ var lowerDate = _offsetYear(now, -lookBehindYears);
+ var upperDate = _offsetYear(now, 100 - lookBehindYears);
+ var lowerCentury = (lowerDate.year ~/ 100) * 100;
+ var upperCentury = (upperDate.year ~/ 100) * 100;
+ preliminaryResult = _dateTimeConstructor(upperCentury + year, month,
+ dayOrDayOfYear, hour24, minute, second, fractionalSecond, utc);
+
+ // Our interval must be half-open since there otherwise could be ambiguity
+ // for a date that is exactly 20 years in the future or exactly 80 years
+ // in the past (mod 100). We'll treat the lower-bound date as the
+ // exclusive bound because:
+ // * It's farther away from the present, and we're less likely to care
+ // about it.
+ // * By the time this function exits, time will have advanced to favor
+ // the upper-bound date.
+ //
+ // We don't actually need to check both bounds.
+ if (preliminaryResult.compareTo(upperDate) <= 0) {
+ // Within range.
+ assert(preliminaryResult.compareTo(lowerDate) > 0);
+ } else {
+ preliminaryResult = _dateTimeConstructor(lowerCentury + year, month,
+ dayOrDayOfYear, hour24, minute, second, fractionalSecond, utc);
+ }
+ }
+
+ if (utc && hasCentury) {
+ _date = preliminaryResult;
+ } else {
_date = _correctForErrors(preliminaryResult, retries);
}
return _date;
@@ -336,7 +398,7 @@
/// Find the index of the first element for which [f] returns true.
/// Advances the stream to that position.
- int findIndex(Function f) {
+ int findIndex(bool Function(dynamic) f) {
while (!atEnd()) {
if (f(next())) return index - 1;
}
@@ -345,7 +407,7 @@
/// Find the indexes of all the elements for which [f] returns true.
/// Leaves the stream positioned at the end.
- List<dynamic> findIndexes(Function f) {
+ List<dynamic> findIndexes(bool Function(dynamic) f) {
var results = [];
while (!atEnd()) {
if (f(next())) results.add(index - 1);
diff --git a/lib/src/intl/number_format.dart b/lib/src/intl/number_format.dart
index 25d4454..000da63 100644
--- a/lib/src/intl/number_format.dart
+++ b/lib/src/intl/number_format.dart
@@ -30,7 +30,7 @@
///
/// For example,
///
-/// var f = new NumberFormat("###.0#", "en_US");
+/// var f = NumberFormat("###.0#", "en_US");
/// print(f.format(12.345));
/// ==> 12.34
///
@@ -41,8 +41,8 @@
/// There are also standard patterns available via the special constructors.
/// e.g.
///
-/// var percent = new NumberFormat.percentPattern("ar"); var
-/// eurosInUSFormat = new NumberFormat.currency(locale: "en_US",
+/// var percent = NumberFormat.percentPattern("ar"); var
+/// eurosInUSFormat = NumberFormat.currency(locale: "en_US",
/// symbol: "€");
///
/// There are several such constructors available, though some of them are
@@ -142,12 +142,12 @@
/// otherwise we use the value from the pattern for the locale.
///
/// So, for example,
- /// new NumberFormat.currency(name: 'USD', decimalDigits: 7)
+ /// NumberFormat.currency(name: 'USD', decimalDigits: 7)
/// will format with 7 decimal digits, because that's what we asked for. But
- /// new NumberFormat.currency(locale: 'en_US', name: 'JPY')
+ /// NumberFormat.currency(locale: 'en_US', name: 'JPY')
/// will format with zero, because that's the default for JPY, and the
/// currency's default takes priority over the locale's default.
- /// new NumberFormat.currency(locale: 'en_US')
+ /// NumberFormat.currency(locale: 'en_US')
/// will format with two, which is the default for that locale.
///
int get decimalDigits => _decimalDigits;
@@ -203,8 +203,8 @@
///
/// If provided,
/// use [currencyNameOrSymbol] in place of the default currency name. e.g.
- /// var eurosInCurrentLocale = new NumberFormat
- /// .currencyPattern(Intl.defaultLocale, "€");
+ /// var eurosInCurrentLocale = NumberFormat
+ /// .currencyPattern(Intl.defaultLocale, "€");
@Deprecated('Use NumberFormat.currency')
factory NumberFormat.currencyPattern(
[String locale, String currencyNameOrSymbol]) {
@@ -226,28 +226,28 @@
/// Otherwise we will use the default currency name for the current locale. If
/// no [symbol] is specified, we will use the currency name in the formatted
/// result. e.g.
- /// var f = new NumberFormat.currency(locale: 'en_US', name: 'EUR')
+ /// var f = NumberFormat.currency(locale: 'en_US', name: 'EUR')
/// will format currency like "EUR1.23". If we did not specify the name, it
/// would format like "USD1.23".
///
/// If [symbol] is used, then that symbol will be used in formatting instead
/// of the name. e.g.
- /// var eurosInCurrentLocale = new NumberFormat.currency(symbol: "€");
+ /// var eurosInCurrentLocale = NumberFormat.currency(symbol: "€");
/// will format like "€1.23". Otherwise it will use the currency name.
/// If this is not explicitly specified in the constructor, then for
/// currencies we use the default value for the currency if the name is given,
- /// otherwise we use the value from the pattern for the locale.
+ /// otherwise we use the value from the pattern for the locale.
///
/// If [decimalDigits] is specified, numbers will format with that many digits
/// after the decimal place. If it's not, they will use the default for the
/// currency in [name], and the default currency for [locale] if the currency
/// name is not specified. e.g.
- /// new NumberFormat.currency(name: 'USD', decimalDigits: 7)
+ /// NumberFormat.currency(name: 'USD', decimalDigits: 7)
/// will format with 7 decimal digits, because that's what we asked for. But
- /// new NumberFormat.currency(locale: 'en_US', name: 'JPY')
+ /// NumberFormat.currency(locale: 'en_US', name: 'JPY')
/// will format with zero, because that's the default for JPY, and the
/// currency's default takes priority over the locale's default.
- /// new NumberFormat.currency(locale: 'en_US')
+ /// NumberFormat.currency(locale: 'en_US')
/// will format with two, which is the default for that locale.
///
/// The [customPattern] parameter can be used to specify a particular
@@ -282,12 +282,12 @@
/// after the decimal place. If it's not, they will use the default for the
/// currency in [name], and the default currency for [locale] if the currency
/// name is not specified. e.g.
- /// new NumberFormat.simpleCurrency(name: 'USD', decimalDigits: 7)
+ /// NumberFormat.simpleCurrency(name: 'USD', decimalDigits: 7)
/// will format with 7 decimal digits, because that's what we asked for. But
- /// new NumberFormat.simpleCurrency(locale: 'en_US', name: 'JPY')
+ /// NumberFormat.simpleCurrency(locale: 'en_US', name: 'JPY')
/// will format with zero, because that's the default for JPY, and the
/// currency's default takes priority over the locale's default.
- /// new NumberFormat.simpleCurrency(locale: 'en_US')
+ /// NumberFormat.simpleCurrency(locale: 'en_US')
/// will format with two, which is the default for that locale.
factory NumberFormat.simpleCurrency(
{String locale, String name, int decimalDigits}) {
diff --git a/lib/src/intl_helpers.dart b/lib/src/intl_helpers.dart
index a807d97..356d00c 100644
--- a/lib/src/intl_helpers.dart
+++ b/lib/src/intl_helpers.dart
@@ -68,7 +68,12 @@
List<String> get keys => _throwException() as List<String>;
- bool containsKey(String key) => _isFallback(key) ? true : _throwException();
+ bool containsKey(String key) {
+ if (!_isFallback(key)) {
+ _throwException();
+ }
+ return true;
+ }
F _throwException() {
throw LocaleDataException('Locale data has not been initialized'
diff --git a/lib/src/lazy_locale_data.dart b/lib/src/lazy_locale_data.dart
index 0469ef6..0620033 100644
--- a/lib/src/lazy_locale_data.dart
+++ b/lib/src/lazy_locale_data.dart
@@ -41,7 +41,7 @@
/// [keys] lists the set of remotely available locale names so we know which
/// things can be fetched without having to check remotely.
LazyLocaleData(this._reader, this._creationFunction, this.availableLocales) {
- map = Map();
+ map = {};
availableLocaleSet = Set.from(availableLocales);
}
@@ -58,7 +58,7 @@
/// [localeName] then throw an exception with a different message.
dynamic operator [](String localeName) {
if (containsKey(localeName)) {
- var data = map[localeName];
+ dynamic data = map[localeName];
if (data == null) {
throw LocaleDataException('Locale $localeName has not been initialized.'
' Call initializeDateFormatting($localeName, <data url>) first');
@@ -72,7 +72,7 @@
/// Throw an exception indicating that the locale has no data available,
/// either locally or remotely.
- void unsupportedLocale(localeName) {
+ void unsupportedLocale(String localeName) {
throw LocaleDataException('Locale $localeName has no data available');
}
diff --git a/lib/src/plural_rules.dart b/lib/src/plural_rules.dart
index 1a3beb5..656bf62 100644
--- a/lib/src/plural_rules.dart
+++ b/lib/src/plural_rules.dart
@@ -75,7 +75,7 @@
_v = precision ?? math.min(_decimals(n, precision), defaultDigits);
- int base = math.pow(10, _v);
+ var base = math.pow(10, _v) as int;
_f = (n * base).floor() % base;
}
@@ -444,7 +444,7 @@
}
/// Selected Plural rules by locale.
-final Map pluralRules = {
+final pluralRules = {
'af': _es_rule,
'am': _hi_rule,
'ar': _ar_rule,
diff --git a/pubspec.yaml b/pubspec.yaml
index 064d030..d989d61 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: intl
-version: 0.16.2-dev
+version: 0.17.0-dev
author: Dart Team <misc@dartlang.org>
homepage: https://github.com/dart-lang/intl
description: >-
@@ -11,6 +11,7 @@
sdk: '>=2.5.0 <3.0.0'
dependencies:
+ clock: ^1.0.1
path: '>=0.9.0 <2.0.0'
dev_dependencies:
fixnum: '>=0.9.0 <0.11.0'
diff --git a/test/compact_number_test_data.dart b/test/compact_number_test_data.dart
old mode 100755
new mode 100644
diff --git a/test/compact_number_test_data_33.dart b/test/compact_number_test_data_33.dart
old mode 100755
new mode 100644
diff --git a/test/date_time_format_test_core.dart b/test/date_time_format_test_core.dart
index 83982c0..3c7a539 100644
--- a/test/date_time_format_test_core.dart
+++ b/test/date_time_format_test_core.dart
@@ -8,6 +8,7 @@
library date_time_format_tests;
+import 'package:clock/clock.dart';
import 'package:intl/intl.dart';
import 'package:test/test.dart';
import 'date_time_format_test_data.dart';
@@ -209,6 +210,28 @@
orderedEquals(['hh', ':', 'mm', ':', 'ss']));
});
+ test('Two-digit years', () {
+ withClock(Clock.fixed(DateTime(2000, 1, 1)), () {
+ var dateFormat = DateFormat('yy');
+ expect(dateFormat.parse('99'), DateTime(1999));
+ expect(dateFormat.parse('00'), DateTime(2000));
+ expect(dateFormat.parse('19'), DateTime(2019));
+ expect(dateFormat.parse('20'), DateTime(2020));
+ expect(dateFormat.parse('21'), DateTime(1921));
+
+ expect(dateFormat.parse('2000'), DateTime(2000));
+
+ dateFormat = DateFormat('MM-dd-yy');
+ expect(dateFormat.parse('12-31-19'), DateTime(2019, 12, 31));
+ expect(dateFormat.parse('1-1-20'), DateTime(2020, 1, 1));
+ expect(dateFormat.parse('1-2-20'), DateTime(1920, 1, 2));
+
+ expect(DateFormat('y').parse('99'), DateTime(99));
+ expect(DateFormat('yyy').parse('99'), DateTime(99));
+ expect(DateFormat('yyyy').parse('99'), DateTime(99));
+ });
+ });
+
test('Test ALL the supported formats on representative locales', () {
var aDate = DateTime(2012, 1, 27, 20, 58, 59, 0);
testLocale('en_US', english, aDate);
diff --git a/test/date_time_strict_test.dart b/test/date_time_strict_test.dart
index daed8c3..730ceeb 100644
--- a/test/date_time_strict_test.dart
+++ b/test/date_time_strict_test.dart
@@ -67,7 +67,7 @@
test('Invalid times 24 hour', () {
var format = DateFormat.Hms();
- check(s) => expect(() => format.parseStrict(s), throwsFormatException);
+ void check(s) => expect(() => format.parseStrict(s), throwsFormatException);
check('-1:15:00');
expect(format.parseStrict('0:15:00'), DateTime(1970, 1, 1, 0, 15));
check('24:00:00');
diff --git a/test/locale_test_data.dart b/test/locale_test_data.dart
old mode 100755
new mode 100644
diff --git a/test/more_compact_number_test_data.dart b/test/more_compact_number_test_data.dart
index 0c3bf8b..b443c0d 100644
--- a/test/more_compact_number_test_data.dart
+++ b/test/more_compact_number_test_data.dart
@@ -13,12 +13,12 @@
num number;
String expected;
- int maximumIntegerDigits = null;
- int minimumIntegerDigits = null;
- int maximumFractionDigits = null;
- int minimumFractionDigits = null;
- int minimumExponentDigits = null;
- int significantDigits = null;
+ int maximumIntegerDigits;
+ int minimumIntegerDigits;
+ int maximumFractionDigits;
+ int minimumFractionDigits;
+ int minimumExponentDigits;
+ int significantDigits;
String toString() => "CompactRoundingTestCase for $number, "
"maxIntDig: $maximumIntegerDigits, "
diff --git a/test/number_format_compact_test.dart b/test/number_format_compact_test.dart
index c89aa6a..a1e44bb 100644
--- a/test/number_format_compact_test.dart
+++ b/test/number_format_compact_test.dart
@@ -67,6 +67,7 @@
testCurrency('en_US', 12, r'$12.00', r'$10');
testCurrency('en_US', 12.3, r'$12.30', r'$10');
testCurrency('en_US', 123, r'$123', r'$100');
+ testCurrency('en_US', 1000, r'$1K', r'$1K');
testCurrency('en_US', 1234, r'$1.23K', r'$1K');
testCurrency('en_US', 12345, r'$12.3K', r'$10K');
testCurrency('en_US', 123456, r'$123K', r'$100K');
@@ -313,19 +314,30 @@
}
void validateFancy(more_testdata.CompactRoundingTestCase t) {
- var shortFormat = new NumberFormat.compact(locale: 'en');
- if (t.maximumIntegerDigits != null)
+ var shortFormat = NumberFormat.compact(locale: 'en');
+ if (t.maximumIntegerDigits != null) {
shortFormat.maximumIntegerDigits = t.maximumIntegerDigits;
- if (t.minimumIntegerDigits != null)
+ }
+
+ if (t.minimumIntegerDigits != null) {
shortFormat.minimumIntegerDigits = t.minimumIntegerDigits;
- if (t.maximumFractionDigits != null)
+ }
+
+ if (t.maximumFractionDigits != null) {
shortFormat.maximumFractionDigits = t.maximumFractionDigits;
- if (t.minimumFractionDigits != null)
+ }
+
+ if (t.minimumFractionDigits != null) {
shortFormat.minimumFractionDigits = t.minimumFractionDigits;
- if (t.minimumExponentDigits != null)
+ }
+
+ if (t.minimumExponentDigits != null) {
shortFormat.minimumExponentDigits = t.minimumExponentDigits;
- if (t.significantDigits != null)
+ }
+
+ if (t.significantDigits != null) {
shortFormat.significantDigits = t.significantDigits;
+ }
test(t.toString(), () {
expect(shortFormat.format(t.number), t.expected);
diff --git a/test/number_format_compact_web_test.dart b/test/number_format_compact_web_test.dart
index 46d608f..a6c60e1 100644
--- a/test/number_format_compact_web_test.dart
+++ b/test/number_format_compact_web_test.dart
@@ -8,7 +8,7 @@
/// We use @Tags rather than @TestOn to be able to specify something that can be
/// ignored when using a build system that can't read dart_test.yaml. This
/// depends on https://github.com/tc39/proposal-unified-intl-numberformat.
-@Tags(const ['unifiedNumberFormat'])
+@Tags(['unifiedNumberFormat'])
import 'package:intl/intl.dart' as intl;
import 'package:js/js_util.dart' as js;
@@ -17,7 +17,7 @@
import 'compact_number_test_data.dart' as testdata35;
import 'more_compact_number_test_data.dart' as more_testdata;
-main() {
+void main() {
testdata35.compactNumberTestData.forEach(validate);
more_testdata.cldr35CompactNumTests.forEach(validateMore);
@@ -62,14 +62,12 @@
}
String ecmaFormatNumber(String locale, num number,
- {String style: null,
- String currency: null,
- String notation: null,
- String compactDisplay: null}) {
+ {String style, String currency, String notation, String compactDisplay}) {
var options = js.newObject();
if (notation != null) js.setProperty(options, 'notation', notation);
- if (compactDisplay != null)
+ if (compactDisplay != null) {
js.setProperty(options, 'compactDisplay', compactDisplay);
+ }
if (style != null) js.setProperty(options, 'style', style);
if (currency != null) js.setProperty(options, 'currency', currency);
return js.callMethod(number, 'toLocaleString', [locale, options]);
@@ -131,16 +129,26 @@
void validateMore(more_testdata.CompactRoundingTestCase t) {
var options = js.newObject();
js.setProperty(options, 'notation', 'compact');
- if (t.maximumIntegerDigits != null)
+ if (t.maximumIntegerDigits != null) {
js.setProperty(options, 'maximumIntegerDigits', t.maximumIntegerDigits);
- if (t.minimumIntegerDigits != null)
+ }
+
+ if (t.minimumIntegerDigits != null) {
js.setProperty(options, 'minimumIntegerDigits', t.minimumIntegerDigits);
- if (t.maximumFractionDigits != null)
+ }
+
+ if (t.maximumFractionDigits != null) {
js.setProperty(options, 'maximumFractionDigits', t.maximumFractionDigits);
- if (t.minimumFractionDigits != null)
+ }
+
+ if (t.minimumFractionDigits != null) {
js.setProperty(options, 'minimumFractionDigits', t.minimumFractionDigits);
- if (t.minimumExponentDigits != null)
+ }
+
+ if (t.minimumExponentDigits != null) {
js.setProperty(options, 'minimumExponentDigits', t.minimumExponentDigits);
+ }
+
if (t.significantDigits != null) {
js.setProperty(options, 'minimumSignificantDigits', t.significantDigits);
js.setProperty(options, 'maximumSignificantDigits', t.significantDigits);
diff --git a/test/number_format_test_core.dart b/test/number_format_test_core.dart
index b2961cd..372b555 100644
--- a/test/number_format_test_core.dart
+++ b/test/number_format_test_core.dart
@@ -130,7 +130,7 @@
'9,876,543,210',
];
for (var i = 0; i < expected.length; i++) {
- var f = new NumberFormat.decimalPattern();
+ var f = NumberFormat.decimalPattern();
f.maximumIntegerDigits = i;
expect(f.format(9876543210), expected[i],
reason: 'maximumIntegerDigits: $i');
@@ -148,7 +148,7 @@
'1.000000',
];
for (var i = 0; i < 6; i++) {
- var f = new NumberFormat.decimalPattern();
+ var f = NumberFormat.decimalPattern();
f.minimumFractionDigits = i;
if (i > f.maximumFractionDigits) f.maximumFractionDigits = i;
expect(f.format(1), expected[i],
@@ -173,7 +173,7 @@
'9.123456789',
];
for (var i = 0; i < expected.length; i++) {
- var f = new NumberFormat.decimalPattern();
+ var f = NumberFormat.decimalPattern();
f.maximumFractionDigits = i;
expect(f.format(9.123456789), expected[i],
reason: 'maximumFractionDigits: $i');
@@ -198,7 +198,7 @@
'3.21E003',
];
for (var i = 0; i < expected.length; i++) {
- var f = new NumberFormat("#.###E0");
+ var f = NumberFormat("#.###E0");
f.minimumExponentDigits = i;
expect(f.format(3210), expected[i], reason: 'minimumExponentDigits: $i');
}
@@ -222,7 +222,7 @@
'9,876,543.21012',
];
for (var i = 0; i < expected.length; i++) {
- var f = new NumberFormat.decimalPattern();
+ var f = NumberFormat.decimalPattern();
f.significantDigits = i;
expect(f.format(9876543.21012), expected[i],
reason: 'significantDigits: $i');