OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 // VM implementation of DateTime. | 6 // VM implementation of DateTime. |
7 @patch | 7 @patch |
8 class DateTime { | 8 class DateTime { |
9 // Natives. | 9 // Natives. |
10 // The natives have been moved up here to work around Issue 10401. | 10 // The natives have been moved up here to work around Issue 10401. |
11 static int _getCurrentMicros() native "DateTime_currentTimeMicros"; | 11 static int _getCurrentMicros() native "DateTime_currentTimeMicros"; |
12 | 12 |
13 static String _timeZoneNameForClampedSeconds(int secondsSinceEpoch) | 13 static String _timeZoneNameForSeconds(int secondsSinceEpoch) |
14 native "DateTime_timeZoneName"; | 14 native "DateTime_timeZoneName"; |
15 | 15 |
16 static int _timeZoneOffsetInSecondsForClampedSeconds(int secondsSinceEpoch) | 16 static int _timeZoneOffsetInSecondsForSeconds(int secondsSinceEpoch) |
17 native "DateTime_timeZoneOffsetInSeconds"; | 17 native "DateTime_timeZoneOffsetInSeconds"; |
18 | 18 |
19 static int _localTimeZoneAdjustmentInSeconds() | 19 static int _localTimeZoneAdjustmentInSeconds() |
20 native "DateTime_localTimeZoneAdjustmentInSeconds"; | 20 native "DateTime_localTimeZoneAdjustmentInSeconds"; |
21 | 21 |
| 22 // Returns `null` if an error occurred. |
| 23 static int |
| 24 _brokenDownToSeconds(int year, int month, int day, int hour, int minute, |
| 25 int second, bool isUtc) |
| 26 native "DateTime_brokenDownToSeconds"; // Would use mktime or _mkgmtime/ti
megm |
| 27 |
22 static const _MICROSECOND_INDEX = 0; | 28 static const _MICROSECOND_INDEX = 0; |
23 static const _MILLISECOND_INDEX = 1; | 29 static const _MILLISECOND_INDEX = 1; |
24 static const _SECOND_INDEX = 2; | 30 static const _SECOND_INDEX = 2; |
25 static const _MINUTE_INDEX = 3; | 31 static const _MINUTE_INDEX = 3; |
26 static const _HOUR_INDEX = 4; | 32 static const _HOUR_INDEX = 4; |
27 static const _DAY_INDEX = 5; | 33 static const _DAY_INDEX = 5; |
28 static const _WEEKDAY_INDEX = 6; | 34 static const _WEEKDAY_INDEX = 6; |
29 static const _MONTH_INDEX = 7; | 35 static const _MONTH_INDEX = 7; |
30 static const _YEAR_INDEX = 8; | 36 static const _YEAR_INDEX = 8; |
31 | 37 |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 | 260 |
255 static bool _isLeapYear(y) { | 261 static bool _isLeapYear(y) { |
256 // (y % 16 == 0) matches multiples of 400, and is faster than % 400. | 262 // (y % 16 == 0) matches multiples of 400, and is faster than % 400. |
257 return (y % 4 == 0) && ((y % 16 == 0) || (y % 100 != 0)); | 263 return (y % 4 == 0) && ((y % 16 == 0) || (y % 100 != 0)); |
258 } | 264 } |
259 | 265 |
260 /// Converts the given broken down date to microseconds. | 266 /// Converts the given broken down date to microseconds. |
261 @patch | 267 @patch |
262 static int _brokenDownDateToValue(int year, int month, int day, int hour, | 268 static int _brokenDownDateToValue(int year, int month, int day, int hour, |
263 int minute, int second, int millisecond, int microsecond, bool isUtc) { | 269 int minute, int second, int millisecond, int microsecond, bool isUtc) { |
264 // Simplify calculations by working with zero-based month. | 270 var seconds = |
265 --month; | 271 _brokenDownToSeconds(year, month - 1, day, hour, minute, second, isUtc); |
266 // Deal with under and overflow. | 272 if (seconds == null) return null; |
267 if (month >= 12) { | 273 return seconds * Duration.MICROSECONDS_PER_SECOND + |
268 year += month ~/ 12; | 274 milliseconds * Duration.MICROSECONDS_PER_MILLISECONDS + |
269 month = month % 12; | 275 microseconds; |
270 } else if (month < 0) { | |
271 int realMonth = month % 12; | |
272 year += (month - realMonth) ~/ 12; | |
273 month = realMonth; | |
274 } | |
275 | |
276 // First compute the seconds in UTC, independent of the [isUtc] flag. If | |
277 // necessary we will add the time-zone offset later on. | |
278 int days = day - 1; | |
279 days += _DAYS_UNTIL_MONTH[_isLeapYear(year) ? 1 : 0][month]; | |
280 days += _dayFromYear(year); | |
281 int microsecondsSinceEpoch = days * Duration.MICROSECONDS_PER_DAY + | |
282 hour * Duration.MICROSECONDS_PER_HOUR + | |
283 minute * Duration.MICROSECONDS_PER_MINUTE + | |
284 second * Duration.MICROSECONDS_PER_SECOND + | |
285 millisecond * Duration.MICROSECONDS_PER_MILLISECOND + | |
286 microsecond; | |
287 | |
288 // Since [_timeZoneOffsetInSeconds] will crash if the input is far out of | |
289 // the valid range we do a preliminary test that weeds out values that can | |
290 // not become valid even with timezone adjustments. | |
291 // The timezone adjustment is always less than a day, so adding a security | |
292 // margin of one day should be enough. | |
293 if (microsecondsSinceEpoch.abs() > | |
294 _MAX_MILLISECONDS_SINCE_EPOCH * 1000 + Duration.MICROSECONDS_PER_DAY) { | |
295 return null; | |
296 } | |
297 | |
298 if (!isUtc) { | |
299 // Note that we need to remove the local timezone adjustment before | |
300 // asking for the correct zone offset. | |
301 int adjustment = _localTimeZoneAdjustmentInSeconds() * | |
302 Duration.MICROSECONDS_PER_SECOND; | |
303 // The adjustment is independent of the actual date and of the daylight | |
304 // saving time. It is positive east of the Prime Meridian and negative | |
305 // west of it, e.g. -28800 sec for America/Los_Angeles timezone. | |
306 | |
307 int zoneOffset = | |
308 _timeZoneOffsetInSeconds(microsecondsSinceEpoch - adjustment); | |
309 // The zoneOffset depends on the actual date and reflects any daylight | |
310 // saving time and/or historical deviation relative to UTC time. | |
311 // It is positive east of the Prime Meridian and negative west of it, | |
312 // e.g. -25200 sec for America/Los_Angeles timezone during DST. | |
313 microsecondsSinceEpoch -= zoneOffset * Duration.MICROSECONDS_PER_SECOND; | |
314 // The resulting microsecondsSinceEpoch value is therefore the calculated | |
315 // UTC value decreased by a (positive if east of GMT) timezone adjustment | |
316 // and decreased by typically one hour if DST is in effect. | |
317 } | |
318 if (microsecondsSinceEpoch.abs() > | |
319 _MAX_MILLISECONDS_SINCE_EPOCH * Duration.MICROSECONDS_PER_MILLISECOND) { | |
320 return null; | |
321 } | |
322 return microsecondsSinceEpoch; | |
323 } | |
324 | |
325 static int _weekDay(y) { | |
326 // 1/1/1970 was a Thursday. | |
327 return (_dayFromYear(y) + 4) % 7; | |
328 } | |
329 | |
330 /** | |
331 * Returns a year in the range 2008-2035 matching | |
332 * * leap year, and | |
333 * * week day of first day. | |
334 * | |
335 * Leap seconds are ignored. | |
336 * Adapted from V8's date implementation. See ECMA 262 - 15.9.1.9. | |
337 */ | |
338 static int _equivalentYear(int year) { | |
339 // Returns year y so that _weekDay(y) == _weekDay(year). | |
340 // _weekDay returns the week day (in range 0 - 6). | |
341 // 1/1/1956 was a Sunday (i.e. weekday 0). 1956 was a leap-year. | |
342 // 1/1/1967 was a Sunday (i.e. weekday 0). | |
343 // Without leap years a subsequent year has a week day + 1 (for example | |
344 // 1/1/1968 was a Monday). With leap-years it jumps over one week day | |
345 // (e.g. 1/1/1957 was a Tuesday). | |
346 // After 12 years the weekdays have advanced by 12 days + 3 leap days = | |
347 // 15 days. 15 % 7 = 1. So after 12 years the week day has always | |
348 // (now independently of leap-years) advanced by one. | |
349 // weekDay * 12 gives thus a year starting with the wanted weekDay. | |
350 int recentYear = (_isLeapYear(year) ? 1956 : 1967) + (_weekDay(year) * 12); | |
351 // Close to the year 2008 the calendar cycles every 4 * 7 years (4 for the | |
352 // leap years, 7 for the weekdays). | |
353 // Find the year in the range 2008..2037 that is equivalent mod 28. | |
354 return 2008 + (recentYear - 2008) % 28; | |
355 } | |
356 | |
357 /** | |
358 * Returns the UTC year for the corresponding [secondsSinceEpoch]. | |
359 * It is relatively fast for values in the range 0 to year 2098. | |
360 * | |
361 * Code is adapted from V8. | |
362 */ | |
363 static int _yearsFromSecondsSinceEpoch(int secondsSinceEpoch) { | |
364 const int DAYS_IN_4_YEARS = 4 * 365 + 1; | |
365 const int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1; | |
366 const int DAYS_YEAR_2098 = DAYS_IN_100_YEARS + 6 * DAYS_IN_4_YEARS; | |
367 | |
368 int days = secondsSinceEpoch ~/ Duration.SECONDS_PER_DAY; | |
369 if (days > 0 && days < DAYS_YEAR_2098) { | |
370 // According to V8 this fast case works for dates from 1970 to 2099. | |
371 return 1970 + (4 * days + 2) ~/ DAYS_IN_4_YEARS; | |
372 } | |
373 int micros = secondsSinceEpoch * Duration.MICROSECONDS_PER_SECOND; | |
374 return _computeUpperPart(micros)[_YEAR_INDEX]; | |
375 } | |
376 | |
377 /** | |
378 * Returns a date in seconds that is equivalent to the given | |
379 * date in microseconds [microsecondsSinceEpoch]. An equivalent | |
380 * date has the same fields (`month`, `day`, etc.) as the given | |
381 * date, but the `year` is in the range [1901..2038]. | |
382 * | |
383 * * The time since the beginning of the year is the same. | |
384 * * If the given date is in a leap year then the returned | |
385 * seconds are in a leap year, too. | |
386 * * The week day of given date is the same as the one for the | |
387 * returned date. | |
388 */ | |
389 static int _equivalentSeconds(int microsecondsSinceEpoch) { | |
390 const int CUT_OFF_SECONDS = 0x7FFFFFFF; | |
391 | |
392 int secondsSinceEpoch = _flooredDivision( | |
393 microsecondsSinceEpoch, Duration.MICROSECONDS_PER_SECOND); | |
394 | |
395 if (secondsSinceEpoch.abs() > CUT_OFF_SECONDS) { | |
396 int year = _yearsFromSecondsSinceEpoch(secondsSinceEpoch); | |
397 int days = _dayFromYear(year); | |
398 int equivalentYear = _equivalentYear(year); | |
399 int equivalentDays = _dayFromYear(equivalentYear); | |
400 int diffDays = equivalentDays - days; | |
401 secondsSinceEpoch += diffDays * Duration.SECONDS_PER_DAY; | |
402 } | |
403 return secondsSinceEpoch; | |
404 } | 276 } |
405 | 277 |
406 static int _timeZoneOffsetInSeconds(int microsecondsSinceEpoch) { | 278 static int _timeZoneOffsetInSeconds(int microsecondsSinceEpoch) { |
407 int equivalentSeconds = _equivalentSeconds(microsecondsSinceEpoch); | 279 int seconds = _flooredDivision( |
408 return _timeZoneOffsetInSecondsForClampedSeconds(equivalentSeconds); | 280 microsecondsSinceEpoch, Duration.MICROSECONDS_PER_SECOND); |
| 281 return _timeZoneOffsetInSecondsForSeconds(seconds); |
409 } | 282 } |
410 | 283 |
411 static String _timeZoneName(int microsecondsSinceEpoch) { | 284 static String _timeZoneName(int microsecondsSinceEpoch) { |
412 int equivalentSeconds = _equivalentSeconds(microsecondsSinceEpoch); | 285 int seconds = _flooredDivision( |
413 return _timeZoneNameForClampedSeconds(equivalentSeconds); | 286 microsecondsSinceEpoch, Duration.MICROSECONDS_PER_SECOND); |
| 287 return _timeZoneNameForSeconds(seconds); |
414 } | 288 } |
415 } | 289 } |
OLD | NEW |