Chromium Code Reviews| Index: sdk/lib/core/date_time.dart |
| diff --git a/sdk/lib/core/date_time.dart b/sdk/lib/core/date_time.dart |
| index 1e89080a5f688cb8219f982dbad99b719e5febd6..93a89b100fd6b274a24131db557abb1fe73cd9f6 100644 |
| --- a/sdk/lib/core/date_time.dart |
| +++ b/sdk/lib/core/date_time.dart |
| @@ -129,17 +129,13 @@ class DateTime implements Comparable { |
| static const int MONTHS_PER_YEAR = 12; |
| /** |
| - * The number of milliseconds since |
| - * the "Unix epoch" 1970-01-01T00:00:00Z (UTC). |
| - * |
| - * This value is independent of the time zone. |
| - * |
| - * This value is at most |
| - * 8,640,000,000,000,000ms (100,000,000 days) from the Unix epoch. |
| - * In other words: [:millisecondsSinceEpoch.abs() <= 8640000000000000:]. |
| + * The value of this DateTime. |
| * |
| + * The content of this field is implementation dependent. On JavaScript it is |
| + * equal to [millisecondsSinceEpoch]. On the VM it is equal to |
| + * [microsecondsSinceEpoch]. |
| */ |
| - final int millisecondsSinceEpoch; |
| + final int _value; |
| /** |
| * True if this [DateTime] is set to UTC time. |
| @@ -164,9 +160,11 @@ class DateTime implements Comparable { |
| int hour = 0, |
| int minute = 0, |
| int second = 0, |
| - int millisecond = 0]) |
| + int millisecond = 0, |
| + int microsecond = 0]) |
| : this._internal( |
| - year, month, day, hour, minute, second, millisecond, false); |
| + year, month, day, hour, minute, second, millisecond, microsecond, |
| + false); |
| /** |
| * Constructs a [DateTime] instance specified in the UTC time zone. |
| @@ -179,9 +177,11 @@ class DateTime implements Comparable { |
| int hour = 0, |
| int minute = 0, |
| int second = 0, |
| - int millisecond = 0]) |
| + int millisecond = 0, |
| + int microsecond = 0]) |
| : this._internal( |
| - year, month, day, hour, minute, second, millisecond, true); |
| + year, month, day, hour, minute, second, millisecond, microsecond, |
| + true); |
| /** |
| * Constructs a [DateTime] instance with current date and time in the |
| @@ -256,7 +256,7 @@ class DateTime implements Comparable { |
| * time_opt ::= <empty> | (' ' | 'T') hour minutes_opt |
| * minutes_opt ::= <empty> | colon_opt digit{2} seconds_opt |
| * seconds_opt ::= <empty> | colon_opt digit{2} millis_opt |
| - * millis_opt ::= <empty> | '.' digit{1,6} |
| + * micros_opt ::= <empty> | '.' digit{1,6} |
| * timezone_opt ::= <empty> | space_opt timezone |
| * space_opt :: ' ' | <empty> |
| * timezone ::= 'z' | 'Z' | sign digit{2} timezonemins_opt |
| @@ -274,35 +274,20 @@ class DateTime implements Comparable { |
| return int.parse(matched); |
| } |
| - // Parses fractional second digits of '.(\d{1,6})' into milliseconds. |
| - // Uses first three digits (assumed to be zero if not there), and |
| - // rounds up if fourth digit is 5 or greater. |
| - // Should be equivalent to `(double.parse(".$matchd")*1000).round()`. |
| - int parseMilliseconds(String matched) { |
| + // Parses fractional second digits of '.(\d{1,6})' into the combined |
| + // microseconds. |
| + int parseMilliAndMicroseconds(String matched) { |
| if (matched == null) return 0; |
| int length = matched.length; |
| assert(length >= 1); |
| assert(length <= 6); |
| - int result = (matched.codeUnitAt(0) ^ 0x30); |
| - if (length <= 3) { |
| - int i = 1; |
| - while (i < length) { |
| - result *= 10; |
| + int result = 0; |
| + for (int i = 0; i < 6; i++) { |
| + result *= 10; |
| + if (i < matched.length) { |
| result += matched.codeUnitAt(i) ^ 0x30; |
| - i++; |
| } |
| - while (i < 3) { |
| - result *= 10; |
| - i++; |
| - } |
| - return result; |
| - } |
| - // Parse the prefix from 0..3 without creating a new substring. |
| - result = result * 10 + (matched.codeUnitAt(1) ^ 0x30); |
| - result = result * 10 + (matched.codeUnitAt(2) ^ 0x30); |
| - if (matched.codeUnitAt(3) >= 0x35) { |
| - result += 1; |
| } |
| return result; |
| } |
| @@ -314,11 +299,11 @@ class DateTime implements Comparable { |
| int minute = parseIntOrZero(match[5]); |
| int second = parseIntOrZero(match[6]); |
| bool addOneMillisecond = false; |
| - int millisecond = parseMilliseconds(match[7]); |
| - if (millisecond == 1000) { |
| - addOneMillisecond = true; |
| - millisecond = 999; |
| - } |
| + int milliAndMicroseconds = parseMilliAndMicroseconds(match[7]); |
| + int millisecond = |
| + milliAndMicroseconds ~/ Duration.MICROSECONDS_PER_MILLISECOND; |
| + int microsecond = |
| + milliAndMicroseconds.remainder(Duration.MICROSECONDS_PER_MILLISECOND); |
| bool isUtc = false; |
| if (match[8] != null) { // timezone part |
| isUtc = true; |
| @@ -331,14 +316,13 @@ class DateTime implements Comparable { |
| minute -= sign * minuteDifference; |
| } |
| } |
| - int millisecondsSinceEpoch = _brokenDownDateToMillisecondsSinceEpoch( |
| - years, month, day, hour, minute, second, millisecond, isUtc); |
| - if (millisecondsSinceEpoch == null) { |
| + int value = _brokenDownDateToValue( |
| + years, month, day, hour, minute, second, millisecond, microsecond, |
| + isUtc); |
| + if (value == null) { |
| throw new FormatException("Time out of range", formattedString); |
| } |
| - if (addOneMillisecond) millisecondsSinceEpoch++; |
| - return new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, |
| - isUtc: isUtc); |
| + return new DateTime._withValue(value, isUtc: isUtc); |
| } else { |
| throw new FormatException("Invalid date format", formattedString); |
| } |
| @@ -356,11 +340,31 @@ class DateTime implements Comparable { |
| * 1970-01-01T00:00:00Z + [millisecondsSinceEpoch] ms in the given |
| * time zone (local or UTC). |
| */ |
| - DateTime.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch, |
| - {bool isUtc: false}) |
| - : this.millisecondsSinceEpoch = millisecondsSinceEpoch, |
| - this.isUtc = isUtc { |
| - if (millisecondsSinceEpoch.abs() > _MAX_MILLISECONDS_SINCE_EPOCH) { |
| + external DateTime.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch, |
| + {bool isUtc: false}); |
| + |
| + /** |
| + * Constructs a new [DateTime] instance |
| + * with the given [microsecondsSinceEpoch]. |
| + * |
| + * If [isUtc] is false then the date is in the local time zone. |
| + * |
| + * The constructed [DateTime] represents |
| + * 1970-01-01T00:00:00Z + [microsecondsSinceEpoch] us in the given |
| + * time zone (local or UTC). |
| + */ |
| + external DateTime.fromMicrosecondsSinceEpoch(int microsecondsSinceEpoch, |
| + {bool isUtc: false}); |
| + |
| + /** |
| + * Constructs a new [DateTime] instance with the given value. |
| + * |
| + * If [isUtc] is false then the date is in the local time zone. |
| + */ |
| + DateTime._withValue(this._value, {this.isUtc}) { |
| + if (millisecondsSinceEpoch.abs() > _MAX_MILLISECONDS_SINCE_EPOCH || |
| + (millisecondsSinceEpoch.abs() == _MAX_MILLISECONDS_SINCE_EPOCH && |
| + microsecond != 0)) { |
| throw new ArgumentError(millisecondsSinceEpoch); |
| } |
| if (isUtc == null) throw new ArgumentError(isUtc); |
| @@ -379,8 +383,7 @@ class DateTime implements Comparable { |
| */ |
| bool operator ==(other) { |
| if (!(other is DateTime)) return false; |
| - return (millisecondsSinceEpoch == other.millisecondsSinceEpoch && |
| - isUtc == other.isUtc); |
| + return (_value == other._value && isUtc == other.isUtc); |
| } |
| /** |
| @@ -396,7 +399,7 @@ class DateTime implements Comparable { |
| * |
| */ |
| bool isBefore(DateTime other) { |
| - return millisecondsSinceEpoch < other.millisecondsSinceEpoch; |
| + return _value < other._value; |
| } |
| /** |
| @@ -412,7 +415,7 @@ class DateTime implements Comparable { |
| * |
| */ |
| bool isAfter(DateTime other) { |
| - return millisecondsSinceEpoch > other.millisecondsSinceEpoch; |
| + return _value > other._value; |
| } |
| /** |
| @@ -427,7 +430,7 @@ class DateTime implements Comparable { |
| * assert(berlinWallFell.isAtSameMomentAs(moonLanding) == false); |
| */ |
| bool isAtSameMomentAs(DateTime other) { |
| - return millisecondsSinceEpoch == other.millisecondsSinceEpoch; |
| + return _value == other._value; |
| } |
| /** |
| @@ -438,10 +441,9 @@ class DateTime implements Comparable { |
| * if this DateTime is smaller (earlier) than [other], |
| * or a positive integer if it is greater (later). |
| */ |
| - int compareTo(DateTime other) |
| - => millisecondsSinceEpoch.compareTo(other.millisecondsSinceEpoch); |
| + int compareTo(DateTime other) => _value.compareTo(other._value); |
| - int get hashCode => millisecondsSinceEpoch; |
| + int get hashCode => _value; |
|
Lasse Reichstein Nielsen
2015/12/03 09:16:22
That's not necessarily a good hash code. Could we
floitsch
2015/12/04 08:11:31
(_value ^ (_value >> 30)) & 0x3FFFFFFF;
|
| /** |
| * Returns this DateTime value in the local time zone. |
| @@ -449,13 +451,12 @@ class DateTime implements Comparable { |
| * Returns [this] if it is already in the local time zone. |
| * Otherwise this method is equivalent to: |
| * |
| - * new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, |
| + * new DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, |
| * isUtc: false) |
| */ |
| DateTime toLocal() { |
| if (isUtc) { |
| - return new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, |
| - isUtc: false); |
| + return new DateTime._withValue(_value, isUtc: false); |
| } |
| return this; |
| } |
| @@ -466,13 +467,12 @@ class DateTime implements Comparable { |
| * Returns [this] if it is already in UTC. |
| * Otherwise this method is equivalent to: |
| * |
| - * new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, |
| + * new DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, |
| * isUtc: true) |
| */ |
| DateTime toUtc() { |
| if (isUtc) return this; |
| - return new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, |
| - isUtc: true); |
| + return new DateTime._withValue(_value, isUtc: true); |
| } |
| static String _fourDigits(int n) { |
| @@ -522,18 +522,19 @@ class DateTime implements Comparable { |
| String min = _twoDigits(minute); |
| String sec = _twoDigits(second); |
| String ms = _threeDigits(millisecond); |
| + String us = microsecond == 0 ? "" : _threeDigits(microsecond); |
| if (isUtc) { |
| - return "$y-$m-$d $h:$min:$sec.${ms}Z"; |
| + return "$y-$m-$d $h:$min:$sec.$ms${us}Z"; |
| } else { |
| - return "$y-$m-$d $h:$min:$sec.$ms"; |
| + return "$y-$m-$d $h:$min:$sec.$ms$us"; |
| } |
| } |
| /** |
| * Returns an ISO-8601 full-precision extended format representation. |
| * |
| - * The format is `yyyy-MM-ddTHH:mm:ss.sssZ` for UTC time, and |
| - * `yyyy-MM-ddTHH:mm:ss.sss` (no trailing "Z") for local/non-UTC time, |
| + * The format is `yyyy-MM-ddTHH:mm:ss.mmmuuuZ` for UTC time, and |
| + * `yyyy-MM-ddTHH:mm:ss.mmmuuu` (no trailing "Z") for local/non-UTC time, |
| * where: |
| * |
| * * `yyyy` is a, possibly negative, four digit representation of the year, |
| @@ -543,8 +544,10 @@ class DateTime implements Comparable { |
| * * `dd` is the day of the month in the range 01 to 31, |
| * * `HH` are hours in the range 00 to 23, |
| * * `mm` are minutes in the range 00 to 59, |
| - * * `ss` are seconds in the range 00 to 59 (no leap seconds), and |
| - * * `sss` are milliseconds in the range 000 to 999. |
| + * * `ss` are seconds in the range 00 to 59 (no leap seconds), |
| + * * `mmm` are microseconds in the range 000 to 999, and |
| + * * `uuu` are microseconds in the range 001 to 999. If [microsecond] equals |
| + * 0, then this part is omitted. |
| * |
| * The resulting string can be parsed back using [parse]. |
| */ |
| @@ -557,10 +560,11 @@ class DateTime implements Comparable { |
| String min = _twoDigits(minute); |
| String sec = _twoDigits(second); |
| String ms = _threeDigits(millisecond); |
| + String us = microsecond == 0 ? "" : _threeDigits(microsecond); |
| if (isUtc) { |
| - return "$y-$m-${d}T$h:$min:$sec.${ms}Z"; |
| + return "$y-$m-${d}T$h:$min:$sec.$ms${us}Z"; |
| } else { |
| - return "$y-$m-${d}T$h:$min:$sec.$ms"; |
| + return "$y-$m-${d}T$h:$min:$sec.$ms$us"; |
| } |
| } |
| @@ -570,11 +574,7 @@ class DateTime implements Comparable { |
| * DateTime today = new DateTime.now(); |
| * DateTime sixtyDaysFromNow = today.add(new Duration(days: 60)); |
| */ |
| - DateTime add(Duration duration) { |
| - int ms = millisecondsSinceEpoch; |
| - return new DateTime.fromMillisecondsSinceEpoch( |
| - ms + duration.inMilliseconds, isUtc: isUtc); |
| - } |
| + external DateTime add(Duration duration); |
| /** |
| * Returns a new [DateTime] instance with [duration] subtracted from [this]. |
| @@ -582,11 +582,7 @@ class DateTime implements Comparable { |
| * DateTime today = new DateTime.now(); |
| * DateTime sixtyDaysAgo = today.subtract(new Duration(days: 60)); |
| */ |
| - DateTime subtract(Duration duration) { |
| - int ms = millisecondsSinceEpoch; |
| - return new DateTime.fromMillisecondsSinceEpoch( |
| - ms - duration.inMilliseconds, isUtc: isUtc); |
| - } |
| + external DateTime subtract(Duration duration); |
| /** |
| * Returns a [Duration] with the difference between [this] and [other]. |
| @@ -597,12 +593,7 @@ class DateTime implements Comparable { |
| * Duration difference = berlinWallFell.difference(dDay); |
| * assert(difference.inDays == 16592); |
| */ |
| - |
| - Duration difference(DateTime other) { |
| - int ms = millisecondsSinceEpoch; |
| - int otherMs = other.millisecondsSinceEpoch; |
| - return new Duration(milliseconds: ms - otherMs); |
| - } |
| + external Duration difference(DateTime other); |
| external DateTime._internal(int year, |
| int month, |
| @@ -611,13 +602,43 @@ class DateTime implements Comparable { |
| int minute, |
| int second, |
| int millisecond, |
| + int microsecond, |
| bool isUtc); |
| + |
| external DateTime._now(); |
| - /// Returns the time as milliseconds since epoch, or null if the |
| - /// values are out of range. |
| - external static int _brokenDownDateToMillisecondsSinceEpoch( |
| + |
| + /// Returns the time as value (millisecond or microsecond since epoch), or |
| + /// null if the values are out of range. |
| + external static int _brokenDownDateToValue( |
| int year, int month, int day, int hour, int minute, int second, |
| - int millisecond, bool isUtc); |
| + int millisecond, int microsecond, bool isUtc); |
| + |
| + /** |
| + * The number of milliseconds since |
| + * the "Unix epoch" 1970-01-01T00:00:00Z (UTC). |
| + * |
| + * This value is independent of the time zone. |
| + * |
| + * This value is at most |
| + * 8,640,000,000,000,000ms (100,000,000 days) from the Unix epoch. |
| + * In other words: `millisecondsSinceEpoch.abs() <= 8640000000000000`. |
| + */ |
| + external int get millisecondsSinceEpoch; |
| + |
| + /** |
| + * The number of microseconds since |
| + * the "Unix epoch" 1970-01-01T00:00:00Z (UTC). |
| + * |
| + * This value is independent of the time zone. |
| + * |
| + * This value is at most |
| + * 8,640,000,000,000,000,000us (100,000,000 days) from the Unix epoch. |
| + * In other words: `microsecondsSinceEpoch.abs() <= 8640000000000000000`. |
| + * |
| + * Note that this value does not fit into 53 bits (the size of a IEEE double). |
| + * A JavaScript number is not able to hold this value. |
| + */ |
| + external int get microsecondsSinceEpoch; |
| /** |
| * The abbreviated time zone name—for example, |
| @@ -695,6 +716,14 @@ class DateTime implements Comparable { |
| external int get millisecond; |
| /** |
| + * The microsecond [0...999]. |
| + * |
| + * DateTime moonLanding = DateTime.parse("1969-07-20 20:18:00"); |
| + * assert(moonLanding.microsecond == 0); |
| + */ |
| + external int get microsecond; |
| + |
| + /** |
| * The day of the week [MONDAY]..[SUNDAY]. |
| * |
| * In accordance with ISO 8601 |