Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(591)

Side by Side Diff: runtime/lib/date.dart

Issue 9568011: Unify Date.fromString and support more Iso 8601. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebase. Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frog/lib/math.dart ('k') | tests/co19/co19-leg.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 // Dart core library. 4 // Dart core library.
5 5
6 class TimeZoneImplementation implements TimeZone { 6 class TimeZoneImplementation implements TimeZone {
7 const TimeZoneImplementation.utc() : isUtc = true; 7 const TimeZoneImplementation.utc() : isUtc = true;
8 TimeZoneImplementation.local() : isUtc = false {} 8 TimeZoneImplementation.local() : isUtc = false {}
9 9
10 bool operator ==(Object other) { 10 bool operator ==(Object other) {
(...skipping 21 matching lines...) Expand all
32 32
33 DateImplementation.withTimeZone(int years, 33 DateImplementation.withTimeZone(int years,
34 int month, 34 int month,
35 int day, 35 int day,
36 int hours, 36 int hours,
37 int minutes, 37 int minutes,
38 int seconds, 38 int seconds,
39 int milliseconds, 39 int milliseconds,
40 TimeZoneImplementation timeZone) 40 TimeZoneImplementation timeZone)
41 : timeZone = timeZone, 41 : timeZone = timeZone,
42 value = brokenDownDateToMillisecondsSinceEpoch_( 42 value = _brokenDownDateToMillisecondsSinceEpoch(
43 years, month, day, hours, minutes, seconds, milliseconds, 43 years, month, day, hours, minutes, seconds, milliseconds,
44 timeZone.isUtc) {} 44 timeZone.isUtc) {
45 if (value === null) throw new IllegalArgumentException();
46 }
45 47
46 DateImplementation.now() 48 DateImplementation.now()
47 : timeZone = new TimeZone.local(), 49 : timeZone = new TimeZone.local(),
48 value = getCurrentMs_() { 50 value = getCurrentMs_() {
49 } 51 }
50 52
51 factory DateImplementation.fromString(String formattedString) { 53 factory DateImplementation.fromString(String formattedString) {
52 int substringToNumber(String str, int from, int to) { 54 // Read in (a subset of) ISO 8601.
53 int result = 0; 55 // Examples:
54 for (int i = from; i < to; i++) { 56 // - "2012-02-27 13:27:00"
55 result = result * 10 + str.charCodeAt(i) - "0".charCodeAt(0); 57 // - "2012-02-27 13:27:00.423z"
58 // - "20120227 13:27:00"
59 // - "20120227T132700"
60 // - "20120227"
61 // - "2012-02-27T14Z"
62 // - "-123450101 00:00:00 Z" // In the year -12345.
63 final RegExp re = const RegExp(
64 @'^([+-]?\d?\d\d\d\d)-?(\d\d)-?(\d\d)' + // The day part.
65 @'(?:[ T](\d\d)(?::?(\d\d)(?::?(\d\d)(?:.(\d{1,5}))?)?)? ?([zZ])?)?$');
66 Match match = re.firstMatch(formattedString);
67 if (match !== null) {
68 int parseIntOrZero(String matched) {
69 // TODO(floitsch): we should not need to test against the empty string.
70 if (matched === null || matched == "") return 0;
71 return Math.parseInt(matched);
56 } 72 }
57 return result; 73
74 int years = Math.parseInt(match[1]);
75 int month = Math.parseInt(match[2]);
76 int day = Math.parseInt(match[3]);
77 int hours = parseIntOrZero(match[4]);
78 int minutes = parseIntOrZero(match[5]);
79 int seconds = parseIntOrZero(match[6]);
80 bool addOneMillisecond = false;
81 int milliseconds = parseIntOrZero(match[7]);
82 if (milliseconds != 0) {
83 if (match[7].length == 1) {
84 milliseconds *= 100;
85 } else if (match[7].length == 2) {
86 milliseconds *= 10;
87 } else if (match[7].length == 3) {
88 // Do nothing.
89 } else if (match[7].length == 4) {
90 addOneMillisecond = ((milliseconds % 10) >= 5);
91 milliseconds ~/= 10;
92 } else {
93 assert(match[7].length == 5);
94 addOneMillisecond = ((milliseconds %100) >= 50);
95 milliseconds ~/= 100;
96 }
97 if (addOneMillisecond && milliseconds < 999) {
98 addOneMillisecond = false;
99 milliseconds++;
100 }
101 }
102 // TODO(floitsch): we should not need to test against the empty string.
103 bool isUtc = (match[8] !== null) && (match[8] != "");
104 TimeZone timezone = isUtc ? const TimeZone.utc() : new TimeZone.local();
105 int epochValue = _brokenDownDateToMillisecondsSinceEpoch(
106 years, month, day, hours, minutes, seconds, milliseconds, isUtc);
107 if (epochValue === null) {
108 throw new IllegalArgumentException(formattedString);
109 }
110 if (addOneMillisecond) epochValue++;
111 return new DateImplementation.fromEpoch(epochValue, timezone);
112 } else {
113 throw new IllegalArgumentException(formattedString);
58 } 114 }
59
60 // TODO(floitsch): improve DateImplementation parsing.
61 // Parse ISO 8601: "2011-05-14 00:37:18.231Z".
62 int yearMonthSeparator = formattedString.indexOf("-", 0);
63 if (yearMonthSeparator < 0) throw "UNIMPLEMENTED";
64 int monthDaySeparator =
65 formattedString.indexOf("-", yearMonthSeparator + 1);
66 if (monthDaySeparator < 0) throw "UNIMPLEMENTED";
67 int dateTimeSeparator = formattedString.indexOf(" ", monthDaySeparator + 1);
68 if (dateTimeSeparator < 0) throw "UNIMPLEMENTED";
69 int hoursMinutesSeparator =
70 formattedString.indexOf(":", dateTimeSeparator + 1);
71 if (hoursMinutesSeparator < 0) throw "UNIMPLEMENTED";
72 int minutesSecondsSeparator =
73 formattedString.indexOf(":", hoursMinutesSeparator + 1);
74 if (minutesSecondsSeparator < 0) throw "UNIMPLEMENTED";
75 int secondsMillisecondsSeparator =
76 formattedString.indexOf(".", minutesSecondsSeparator + 1);
77 bool isUtc = formattedString.endsWith("Z");
78 int end = formattedString.length - (isUtc ? 1 : 0);
79 if (secondsMillisecondsSeparator < 0) secondsMillisecondsSeparator = end;
80
81 int year = substringToNumber(formattedString, 0, yearMonthSeparator);
82 int month = substringToNumber(formattedString,
83 yearMonthSeparator + 1,
84 monthDaySeparator);
85 int day = substringToNumber(formattedString,
86 monthDaySeparator + 1,
87 dateTimeSeparator);
88 int hours = substringToNumber(formattedString,
89 dateTimeSeparator + 1,
90 hoursMinutesSeparator);
91 int minutes = substringToNumber(formattedString,
92 hoursMinutesSeparator + 1,
93 minutesSecondsSeparator);
94 int seconds = substringToNumber(formattedString,
95 minutesSecondsSeparator + 1,
96 secondsMillisecondsSeparator);
97 int milliseconds = substringToNumber(formattedString,
98 secondsMillisecondsSeparator + 1,
99 end);
100 TimeZone timeZone = (isUtc ? const TimeZone.utc() : new TimeZone.local());
101 return new DateImplementation.withTimeZone(
102 year, month, day, hours, minutes, seconds, milliseconds, timeZone);
103 } 115 }
104 116
105 const DateImplementation.fromEpoch(int this.value, 117 const DateImplementation.fromEpoch(int this.value,
106 TimeZone this.timeZone); 118 TimeZone this.timeZone);
107 119
108 bool operator ==(Object other) { 120 bool operator ==(Object other) {
109 if (!(other is DateImplementation)) return false; 121 if (!(other is DateImplementation)) return false;
110 return value == other.value && timeZone == other.timeZone; 122 return value == other.value && timeZone == other.timeZone;
111 } 123 }
112 124
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 203
192 bool isLocalTime() { 204 bool isLocalTime() {
193 return !timeZone.isUtc; 205 return !timeZone.isUtc;
194 } 206 }
195 207
196 bool isUtc() { 208 bool isUtc() {
197 return timeZone.isUtc; 209 return timeZone.isUtc;
198 } 210 }
199 211
200 String toString() { 212 String toString() {
213 String fourDigits(int n) {
214 int absN = n.abs();
215 String sign = n < 0 ? "-" : "";
216 if (absN >= 1000) return "$n";
217 if (absN >= 100) return "${sign}0$absN";
218 if (absN >= 10) return "${sign}00$absN";
219 if (absN >= 1) return "${sign}000$absN";
220 }
201 String threeDigits(int n) { 221 String threeDigits(int n) {
202 if (n >= 100) return "${n}"; 222 if (n >= 100) return "${n}";
203 if (n > 10) return "0${n}"; 223 if (n > 10) return "0${n}";
204 return "00${n}"; 224 return "00${n}";
205 } 225 }
206 String twoDigits(int n) { 226 String twoDigits(int n) {
207 if (n >= 10) return "${n}"; 227 if (n >= 10) return "${n}";
208 return "0${n}"; 228 return "0${n}";
209 } 229 }
210 230
231 String y = fourDigits(year);
211 String m = twoDigits(month); 232 String m = twoDigits(month);
212 String d = twoDigits(day); 233 String d = twoDigits(day);
213 String h = twoDigits(hours); 234 String h = twoDigits(hours);
214 String min = twoDigits(minutes); 235 String min = twoDigits(minutes);
215 String sec = twoDigits(seconds); 236 String sec = twoDigits(seconds);
216 String ms = threeDigits(milliseconds); 237 String ms = threeDigits(milliseconds);
217 if (timeZone.isUtc) { 238 if (timeZone.isUtc) {
218 return "$year-$m-$d $h:$min:$sec.${ms}Z"; 239 return "$y-$m-$d $h:$min:$sec.${ms}Z";
219 } else { 240 } else {
220 return "$year-$m-$d $h:$min:$sec.$ms"; 241 return "$y-$m-$d $h:$min:$sec.$ms";
221 } 242 }
222 } 243 }
223 244
224 // Adds the [duration] to this Date instance. 245 // Adds the [duration] to this Date instance.
225 Date add(Duration duration) { 246 Date add(Duration duration) {
226 return new DateImplementation.fromEpoch(value + duration.inMilliseconds, 247 return new DateImplementation.fromEpoch(value + duration.inMilliseconds,
227 timeZone); 248 timeZone);
228 } 249 }
229 250
230 // Subtracts the [duration] from this Date instance. 251 // Subtracts the [duration] from this Date instance.
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
334 // 15 days. 15 % 7 = 1. So after 12 years the week day has always 355 // 15 days. 15 % 7 = 1. So after 12 years the week day has always
335 // (now independently of leap-years) advanced by one. 356 // (now independently of leap-years) advanced by one.
336 // weekDay * 12 gives thus a year starting with the wanted weekDay. 357 // weekDay * 12 gives thus a year starting with the wanted weekDay.
337 int recentYear = (inLeapYear(year) ? 1956 : 1967) + (weekDay(year) * 12); 358 int recentYear = (inLeapYear(year) ? 1956 : 1967) + (weekDay(year) * 12);
338 // Close to the year 2008 the calendar cycles every 4 * 7 years (4 for the 359 // Close to the year 2008 the calendar cycles every 4 * 7 years (4 for the
339 // leap years, 7 for the weekdays). 360 // leap years, 7 for the weekdays).
340 // Find the year in the range 2008..2037 that is equivalent mod 28. 361 // Find the year in the range 2008..2037 that is equivalent mod 28.
341 return 2008 + (recentYear - 2008) % 28; 362 return 2008 + (recentYear - 2008) % 28;
342 } 363 }
343 364
344 static brokenDownDateToMillisecondsSinceEpoch_( 365 static _brokenDownDateToMillisecondsSinceEpoch(
345 int years, int month, int day, 366 int years, int month, int day,
346 int hours, int minutes, int seconds, int milliseconds, 367 int hours, int minutes, int seconds, int milliseconds,
347 bool isUtc) { 368 bool isUtc) {
348 if ((month < 1) || (month > 12)) { 369 if ((month < 1) || (month > 12)) return null;
349 throw new IllegalArgumentException(); 370 if ((day < 1) || (day > 31)) return null;
350 }
351 if ((day < 1) || (day > 31)) {
352 throw new IllegalArgumentException();
353 }
354 // Leap seconds can lead to hours == 24. 371 // Leap seconds can lead to hours == 24.
355 if ((hours < 0) || (hours > 24)) { 372 if ((hours < 0) || (hours > 24)) return null;
356 throw new IllegalArgumentException(); 373 if ((hours == 24) && ((minutes != 0) || (seconds != 0))) return null;
357 } 374 if ((minutes < 0) || (minutes > 59)) return null;
358 if ((hours == 24) && ((minutes != 0) || (seconds != 0))) { 375 if ((seconds < 0) || (seconds > 59)) return null;
359 throw new IllegalArgumentException(); 376 if ((milliseconds < 0) || (milliseconds > 999)) return null;
360 } 377
361 if ((minutes < 0) || (minutes > 59)) {
362 throw new IllegalArgumentException();
363 }
364 if ((seconds < 0) || (seconds > 59)) {
365 throw new IllegalArgumentException();
366 }
367 if ((milliseconds < 0) || (milliseconds > 999)) {
368 throw new IllegalArgumentException();
369 }
370 int equivalentYear; 378 int equivalentYear;
371 int offsetInSeconds; 379 int offsetInSeconds;
372 // According to V8 some library calls have troubles with negative values. 380 // According to V8 some library calls have troubles with negative values.
373 // Therefore clamp to 1970 - year 2035 (which is less than the size of 381 // Therefore clamp to 1970 - year 2035 (which is less than the size of
374 // 32bit). 382 // 32bit).
375 // We exclude the year 1970 when the time is not UTC, since the epoch 383 // We exclude the year 1970 when the time is not UTC, since the epoch
376 // value could then be negative. 384 // value could then be negative.
377 if (years < (isUtc ? 1970 : 1971) || years > 2035) { 385 if (years < (isUtc ? 1970 : 1971) || years > 2035) {
378 equivalentYear = equivalentYear_(years); 386 equivalentYear = equivalentYear_(years);
379 int offsetInDays = (dayFromYear_(years) - dayFromYear_(equivalentYear)); 387 int offsetInDays = (dayFromYear_(years) - dayFromYear_(equivalentYear));
380 // Leap seconds are ignored. 388 // Leap seconds are ignored.
381 offsetInSeconds = offsetInDays * Duration.SECONDS_PER_DAY; 389 offsetInSeconds = offsetInDays * Duration.SECONDS_PER_DAY;
382 } else { 390 } else {
383 equivalentYear = years; 391 equivalentYear = years;
384 offsetInSeconds = 0; 392 offsetInSeconds = 0;
385 } 393 }
386 int secondsSinceEpoch = brokenDownDateToSecondsSinceEpoch_( 394 int secondsSinceEpoch = _brokenDownDateToSecondsSinceEpoch(
387 equivalentYear, month, day, hours, minutes, seconds, isUtc); 395 equivalentYear, month, day, hours, minutes, seconds, isUtc);
388 int adjustedSeconds = secondsSinceEpoch + offsetInSeconds; 396 int adjustedSeconds = secondsSinceEpoch + offsetInSeconds;
389 return adjustedSeconds * Duration.MILLISECONDS_PER_SECOND + milliseconds; 397 return adjustedSeconds * Duration.MILLISECONDS_PER_SECOND + milliseconds;
390 } 398 }
391 399
392 // Natives 400 // Natives
393 static brokenDownDateToSecondsSinceEpoch_( 401 static _brokenDownDateToSecondsSinceEpoch(
394 int years, int month, int day, int hours, int minutes, int seconds, 402 int years, int month, int day, int hours, int minutes, int seconds,
395 bool isUtc) native "DateNatives_brokenDownToSecondsSinceEpoch"; 403 bool isUtc) native "DateNatives_brokenDownToSecondsSinceEpoch";
396 404
397 static int getCurrentMs_() native "DateNatives_currentTimeMillis"; 405 static int getCurrentMs_() native "DateNatives_currentTimeMillis";
398 406
399 // TODO(floitsch): it would be more efficient if we didn't call the native 407 // TODO(floitsch): it would be more efficient if we didn't call the native
400 // function for every member, but cached the broken-down date. 408 // function for every member, but cached the broken-down date.
401 static int getYear_(int secondsSinceEpoch, bool isUtc) 409 static int getYear_(int secondsSinceEpoch, bool isUtc)
402 native "DateNatives_getYear"; 410 native "DateNatives_getYear";
403 411
404 static int getMonth_(int secondsSinceEpoch, bool isUtc) 412 static int getMonth_(int secondsSinceEpoch, bool isUtc)
405 native "DateNatives_getMonth"; 413 native "DateNatives_getMonth";
406 414
407 static int getDay_(int secondsSinceEpoch, bool isUtc) 415 static int getDay_(int secondsSinceEpoch, bool isUtc)
408 native "DateNatives_getDay"; 416 native "DateNatives_getDay";
409 417
410 static int getHours_(int secondsSinceEpoch, bool isUtc) 418 static int getHours_(int secondsSinceEpoch, bool isUtc)
411 native "DateNatives_getHours"; 419 native "DateNatives_getHours";
412 420
413 static int getMinutes_(int secondsSinceEpoch, bool isUtc) 421 static int getMinutes_(int secondsSinceEpoch, bool isUtc)
414 native "DateNatives_getMinutes"; 422 native "DateNatives_getMinutes";
415 423
416 static int getSeconds_(int secondsSinceEpoch, bool isUtc) 424 static int getSeconds_(int secondsSinceEpoch, bool isUtc)
417 native "DateNatives_getSeconds"; 425 native "DateNatives_getSeconds";
418 } 426 }
OLDNEW
« no previous file with comments | « frog/lib/math.dart ('k') | tests/co19/co19-leg.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698