| 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 |