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..d4df75393d80ed428dda58c0d6e5f4f9d94d3f48 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 ^ (_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 |