Reapply "Add support for timezone offset and timezone name."

This reapplies commit 7800.

Review URL: https://chromiumcodereview.appspot.com//10417009

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@7805 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
floitsch@google.com 2012-05-21 16:11:20 +00:00
parent 77ade89e4a
commit cc15d26cf7
12 changed files with 239 additions and 0 deletions

View file

@ -112,6 +112,23 @@ interface Date extends Comparable, Hashable default DateImplementation {
*/
Date changeTimeZone(TimeZone targetTimeZone);
/**
* Returns the abbreviated time-zone name.
*
* Examples: [:"CET":] or [:"CEST":].
*/
String get timeZoneName();
/**
* The time-zone offset is the difference between local time and UTC. That is,
* the offset is positive for time zones west of UTC.
*
* Note, that JavaScript, Python and C return the difference between UTC and
* local time. Java, C# and Ruby return the difference between local time and
* UTC.
*/
Duration get timeZoneOffset();
/**
* Returns the year.
*/

View file

@ -328,6 +328,19 @@ class Primitives {
return JS('String', @'String.fromCharCode.apply(#, #)', null, charCodes);
}
static String getTimeZoneName(receiver) {
// When calling toString on a Date it will emit the timezone in parenthesis.
// Example: "Wed May 16 2012 21:13:00 GMT+0200 (CEST)".
// We extract this name using a regexp.
var d = lazyAsJsDate(receiver);
return JS('String', @'/\((.*)\)/.exec(#.toString())[1]', d);
}
static int getTimeZoneOffsetInMinutes(receiver) {
// Note that JS and Dart disagree on the sign of the offset.
return -JS('int', @'#.getTimezoneOffset()', lazyAsJsDate(receiver));
}
static valueFromDecomposedDate(years, month, day, hours, minutes, seconds,
milliseconds, isUtc) {
checkInt(years);

View file

@ -297,6 +297,16 @@ class DateImplementation implements Date {
return new Date.fromEpoch(value, targetTimeZone);
}
String get timeZoneName() {
if (isUtc()) return "UTC";
return Primitives.getTimeZoneName(this);
}
Duration get timeZoneOffset() {
if (isUtc()) return new Duration(0);
return new Duration(minutes: Primitives.getTimeZoneOffsetInMinutes(this));
}
int get year() => Primitives.getYear(this);
int get month() => Primitives.getMonth(this);

View file

@ -139,6 +139,32 @@ DEFINE_NATIVE_ENTRY(DateNatives_brokenDownToSecondsSinceEpoch, 7) {
}
DEFINE_NATIVE_ENTRY(DateNatives_timeZoneName, 1) {
GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0));
int64_t seconds = dart_seconds.AsInt64Value();
const char* name;
bool succeeded = OS::GetTimeZoneName(seconds, &name);
if (!succeeded) {
UNIMPLEMENTED();
}
const String& dart_name = String::Handle(String::New(name));
arguments->SetReturn(dart_name);
}
DEFINE_NATIVE_ENTRY(DateNatives_timeZoneOffsetInSeconds, 1) {
GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->At(0));
int64_t seconds = dart_seconds.AsInt64Value();
int offset;
bool succeeded = OS::GetTimeZoneOffsetInSeconds(seconds, &offset);
if (!succeeded) {
UNIMPLEMENTED();
}
const Integer& dart_offset = Integer::Handle(Integer::New(offset));
arguments->SetReturn(dart_offset);
}
DEFINE_NATIVE_ENTRY(DateNatives_currentTimeMillis, 0) {
const Integer& time = Integer::Handle(
Integer::New(OS::GetCurrentTimeMillis()));

View file

@ -130,6 +130,18 @@ class DateImplementation implements Date {
return new Date.fromEpoch(value, targetTimeZone);
}
String get timeZoneName() {
if (isUtc()) return "UTC";
return _timeZoneName(_equivalentSeconds(_secondsSinceEpoch));
}
Duration get timeZoneOffset() {
if (isUtc()) return new Duration(0);
int offsetInSeconds =
_timeZoneOffsetInSeconds(_equivalentSeconds(_secondsSinceEpoch));
return new Duration(seconds: offsetInSeconds);
}
int get year() {
int secondsSinceEpoch = _secondsSinceEpoch;
// According to V8 some library calls have troubles with negative values.
@ -401,6 +413,12 @@ class DateImplementation implements Date {
static int _getCurrentMs() native "DateNatives_currentTimeMillis";
static String _timeZoneName(int secondsSinceEpoch)
native "DateNatives_timeZoneName";
static int _timeZoneOffsetInSeconds(int secondsSinceEpoch)
native "DateNatives_timeZoneOffsetInSeconds";
// TODO(floitsch): it would be more efficient if we didn't call the native
// function for every member, but cached the broken-down date.
static int _getYear(int secondsSinceEpoch, bool isUtc)

View file

@ -100,6 +100,8 @@ namespace dart {
V(DateNatives_getHours, 2) \
V(DateNatives_getMinutes, 2) \
V(DateNatives_getSeconds, 2) \
V(DateNatives_timeZoneName, 1) \
V(DateNatives_timeZoneOffsetInSeconds, 1) \
V(AssertionError_throwNew, 2) \
V(TypeError_throwNew, 5) \
V(FallThroughError_throwNew, 1) \

View file

@ -42,6 +42,17 @@ class OS {
// Returns true if the conversion succeeds, false otherwise.
static bool MkTime(tm* tm, int64_t* seconds_result);
// Returns the abbreviated time-zone name for the given instant.
// For example "CET" or "CEST".
static bool GetTimeZoneName(int64_t seconds_since_epoch,
const char** name_result);
// Returns the difference in seconds between local time and UTC for the given
// instant.
// For example 3600 for CET, and 7200 for CEST.
static bool GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch,
int* offset_result);
// Returns the current time in milliseconds measured
// from midnight January 1, 1970 UTC.
static int64_t GetCurrentTimeMillis();

View file

@ -59,6 +59,27 @@ bool OS::MkTime(tm* tm, int64_t* seconds_result) {
}
bool OS::GetTimeZoneName(int64_t seconds_since_epoch,
const char** name_result) {
tm decomposed;
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
if (!succeeded) return false;
*name_result = decomposed.tm_zone;
return true;
}
bool OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch,
int* offset_result) {
tm decomposed;
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
if (!succeeded) return false;
// Even if the offset was 24 hours it would still easily fit into 32 bits.
*offset_result = static_cast<int>(decomposed.tm_gmtoff);
return true;
}
int64_t OS::GetCurrentTimeMillis() {
return GetCurrentTimeMicros() / 1000;
}

View file

@ -60,6 +60,27 @@ bool OS::MkTime(tm* tm, int64_t* seconds_result) {
}
bool OS::GetTimeZoneName(int64_t seconds_since_epoch,
const char** name_result) {
tm decomposed;
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
if (!succeeded) return false;
*name_result = decomposed.tm_zone;
return true;
}
bool OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch,
int* offset_result) {
tm decomposed;
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
if (!succeeded) return false;
// Even if the offset was 24 hours it would still easily fit into 32 bits.
*offset_result = static_cast<int>(decomposed.tm_gmtoff);
return true;
}
int64_t OS::GetCurrentTimeMillis() {
return GetCurrentTimeMicros() / 1000;
}

View file

@ -18,9 +18,11 @@ bool OS::GmTime(int64_t seconds_since_epoch, tm* tm_result) {
}
// As a side-effect sets the globals _timezone, _daylight and _tzname.
bool OS::LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
time_t seconds = static_cast<time_t>(seconds_since_epoch);
if (seconds != seconds_since_epoch) return false;
// localtime_s implicitly sets _timezone, _daylight and _tzname.
errno_t error_code = localtime_s(tm_result, &seconds);
return error_code == 0;
}
@ -54,6 +56,53 @@ bool OS::MkTime(tm* tm, int64_t* seconds_result) {
}
static int GetDaylightSavingBiasInSeconds() {
TIME_ZONE_INFORMATION zone_information;
memset(&zone_information, 0, sizeof(zone_information));
if (GetTimeZoneInformation(&zone_information) == TIME_ZONE_ID_INVALID) {
// By default the daylight saving offset is an hour.
return -60 * 60;
} else {
return static_cast<int>(zone_information.DaylightBias * 60);
}
}
bool OS::GetTimeZoneName(int64_t seconds_since_epoch,
const char** name_result) {
tm decomposed;
// LocalTime will set _tzname.
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
if (!succeeded) return false;
int inDaylightSavingsTime = decomposed.tm_isdst;
if (inDaylightSavingsTime != 0 && inDaylightSavingsTime != 1) {
return false;
}
*name_result = _tzname[inDaylightSavingsTime];
return true;
}
bool OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch,
int* offset_result) {
tm decomposed;
// LocalTime will set _timezone.
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
if (!succeeded) return false;
int inDaylightSavingsTime = decomposed.tm_isdst;
if (inDaylightSavingsTime != 0 && inDaylightSavingsTime != 1) {
return false;
}
// Dart and Windows disagree on the sign of the bias.
*offset_result = static_cast<int>(-_timezone);
if (inDaylightSavingsTime == 1) {
static int daylight_bias = GetDaylightSavingBiasInSeconds();
// Subtract because windows and Dart disagree on the sign.
*offset_result = *offset_result - daylight_bias;
}
return true;
}
int64_t OS::GetCurrentTimeMillis() {
return GetCurrentTimeMicros() / 1000;
}

View file

@ -34,6 +34,9 @@ string_substring_test: Fail
string_test: Fail # Needs index out of range checks.
string_replace_dollar_test: Fail
# New methods (timeZoneName and timeZoneOffset) were not ported to frog.
date_time7_test: Skip
[ $compiler == frog && $runtime == drt ]
list_sort_test: Fail, Pass # Issue 2667

View file

@ -0,0 +1,48 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Test Date timeZoneName and timeZoneOffset getters.
testUtc() {
var d = new Date.fromString("2012-03-04T03:25:38.123Z");
Expect.equals("UTC", d.timeZoneName);
Expect.equals(0, d.timeZoneOffset.inSeconds);
}
testLocal() {
checkOffset(String name, Duration offset) {
// Timezone abbreviations are not in bijection with their timezones.
// For example AST stands for "Arab Standard Time" (UTC+03), as well as
// "Arabian Standard Time" (UTC+04), or PST stands for Pacific Standard Time
// and Philippine Standard Time.
//
// Hardcode some common timezones.
if (name == "CET") {
Expect.equals(1, offset.inHours);
} else if (name == "CEST") {
Expect.equals(2, offset.inHours);
} else if (name == "GMT") {
Expect.equals(0, offset.inSeconds);
} else if (name == "EST") {
Expect.equals(-5, offset.inHours);
} else if (name == "EDT") {
Expect.equals(-4, offset.inHours);
} else if (name == "PDT") {
Expect.equals(-7, offset.inHours);
}
}
var d = new Date.fromString("2012-01-02T13:45:23");
String name = d.timeZoneName;
checkOffset(name, d.timeZoneOffset);
d = new Date.fromString("2012-07-02T13:45:23");
name = d.timeZoneName;
checkOffset(name, d.timeZoneOffset);
}
main() {
testUtc();
testLocal();
}