Question regarding parameters of getTzInfo

148 views
Skip to first unread message

Jan Niehusmann

unread,
Mar 31, 2010, 2:55:40 PM3/31/10
to timezone-js
To completely understand some subtle behaviour close to DST boundaries,
I need to know how timezoneJS.timezone is supposed to work exactly.

When calling timezoneJS.timezone.getTzInfo(dt, tz), what is the
parameter dt supposed to contain?

I guess it should be a Date object, which contains the UTC timestamp of
some instance in time.
It then returns an object containing the tzOffset and tzAbbrev of the
DST rule which is valid for that instance in time, in time zone tz.

Am I correct?

mde

unread,
Mar 31, 2010, 7:31:35 PM3/31/10
to timezone-js
Jan,

On Mar 31, 11:55 am, Jan Niehusmann <j...@gondor.com> wrote:
> When calling timezoneJS.timezone.getTzInfo(dt, tz), what is the
> parameter dt supposed to contain?
>
> I guess it should be a Date object, which contains the UTC timestamp of
> some instance in time.

Specifically, the dt variable contains a JavaScript Date object where
the results of the UTC getters (getUTCFullYear, getUTCHours, etc.) all
return the correct values for the date that you're setting a timezone
on.

The idea is that with a timezone-enabled date, the canonical
representation of the date is the collection of values (e.g., 2010, 3,
31, 14, 30, 0) used to represent the date parts, plus its timezone
(instead of being a numeric timestamp with an offset).

So you can have a date with a timezone of America/Chicago, and if you
change its timezone to America/Los_Angeles, it gives you a different
timestamp/offset:

>>> var dt = new timezoneJS.Date(2010, 2, 31, 'America/Chicago');
>>> dt.getTime();
1270011600000
>>> dt.getTimezoneOffset();
300
>>> dt.setTimezone('America/Los_Angeles');
>>> dt.getTime();
1270018800000
>>> dt.getTimezoneOffset();
420

The point is that this date is conceptually still "March 31st, 2010,"
regardless of what timezone you set on it. Its timestamp and offset
depend on what timezone it's in.

So, normalizing to a UTC date with those values, and then using the
UTC methods to get them means you get the benefits of all the ECMA
Date behaviors (like wrap-around), without the timezone baggage.

> It then returns an object containing the tzOffset and tzAbbrev of the
> DST rule which is valid for that instance in time, in time zone tz.

That's correct. The DST rule and offset is based on the local time.
That's why everything has to be based on the collection of numeric
values for each part of the date.

Does that make sense?


Matthew

Jan Niehusmann

unread,
Apr 1, 2010, 3:12:01 AM4/1/10
to timez...@googlegroups.com
Hi Matthew,

On Wed, Mar 31, 2010 at 04:31:35PM -0700, mde wrote:
> On Mar 31, 11:55�am, Jan Niehusmann <j...@gondor.com> wrote:
> > When calling timezoneJS.timezone.getTzInfo(dt, tz), what is the
> > parameter dt supposed to contain?

> Specifically, the dt variable contains a JavaScript Date object where


> the results of the UTC getters (getUTCFullYear, getUTCHours, etc.) all
> return the correct values for the date that you're setting a timezone
> on.

Ok. Using the UTC setters/getters is fine, in fact it's the only way to
sanely work with Date objects not containing dates of arbitrary time
zones.

So in fact, getTzInfo does what I wanted to do with
getTzInfoFromLocalTime, and instead, some function getTzInfoUTC is
missing for cases where one needs the local time for a given UTC time
stamp.

> The idea is that with a timezone-enabled date, the canonical
> representation of the date is the collection of values (e.g., 2010, 3,
> 31, 14, 30, 0) used to represent the date parts, plus its timezone
> (instead of being a numeric timestamp with an offset).

That's the way timezoneJS.Date works, sure. And it's a very good
approach if one is interested in single components most of the time.
(See Joda Time, http://joda-time.sourceforge.net/, for an example of a
Java library using a similar approach.)

But in fact what I need is a little bit different: I need a Date object
which behaves as much as a pure javascript Date object as possible, with
the little difference that it doesn't use the browser time zone, a
different, user-specified one.

Therefore I'm not using timezoneJS.Date but my own Date implementation,
with timezoneJS.timezone to get the required details about the time zone
offsets.

This is why I need a well-defined interface to timezoneJS.Date.

BTW, I hope I'll find time to publish my Date object implementation
soon. Perhaps it's even possible to integrate it into
timezoneJS.Date, to get best of both worlds.


Jan

Jan Niehusmann

unread,
Apr 1, 2010, 4:39:36 AM4/1/10
to timez...@googlegroups.com
On Thu, Apr 01, 2010 at 09:12:01AM +0200, Jan Niehusmann wrote:
> Ok. Using the UTC setters/getters is fine, in fact it's the only way to
> sanely work with Date objects not containing dates of arbitrary time
> zones.

s/not//

And this brings me to another (long, sorry for that) remark:

timezoneJS.Date uses 'new Date(...)' in several places. This leads to
incorrect results in some cases.

Consider the case I want to create a date object for 2010-03-28 2:30 in
time zone Americy/New_York.

A way to do that would be calling
timezoneJS.Date(2010,02,28,2,30,"America/New_York"), right?

And calling getHours() on this object should of course return 2, as I
didn't change time zones, and getHours() should return the hours of the
time in America/New_York, exactly as set.

Let's try it. I wrote this little js program:

load('tz.js'); // tz.js loads the time zone definitions into object 'zi'
load('date.js');
timezoneJS.timezone.loadZoneDataFromObject(zi);
timezoneJS.timezone.loadingScheme =
timezoneJS.timezone.loadingSchemes.MANUAL_LOAD;

var d = new timezoneJS.Date(2010,02,28,2,30,"America/New_York")

print(d.getHours());


Now, running this with rhino should print '2':

$ rhino -opt -1 test.js
1

(the argument -opt -1 is only necessary because otherwise, rhino has
problems with the large object 'zi' for some reason).

What the heck is wrong here? In fact, if you try it out, you'll most
probably see a 2 and wonder what's wrong with me :-)

Well the only thing wrong with me is that I live in Germany. Let's see
what happens in New York:

$ TZ=America/New_York rhino -opt -1 test2.js
2

Or using GMT:

TZ=GMT rhino -opt -1 test2.js
2

And, to verify that this doesn't work in Germany, you could run:

$ TZ=Europe/Berlin rhino -opt -1 test2.js
1

So what's the problem here? It's simple: We had a time zone change on
March 28th in Europe, and in central european time, the local time
2010-03-28 2:30 didn't exist at all!

Unfortunately, the timezoneJS.Date constructor calls
dt = new Date(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); on its
arguments, and later, in setFromDateObjProxy, tries to recover the
original values using dt.get*().

But called with 'impossible' values, new Date() just choses a different
value using some obscure ECMAScript defined algorithm, and dt.get*()
returns this different value instead of the original one.

Based on this, I conclude that one cannot use any of the local-time
based functions of the javascript Date object when dealing with
arbitrary time zones. This includes, unfortunately, the Date constructor
and Date.parse().

I'll post my solution for an arbitrary-time-zone Date replacement later.
It has a different API than timezoneJS.Date, so it's not a plug-in
replacement, but it's probably not too difficult to take the concepts
from that implementation and apply them to timezoneJS.Date.

Additionally, I suggest testing timezoneJS.Date and timezoneJS.timezone
independently, ie. calling timezoneJS.timezone functions directly from
the test cases instead of going through timezoneJS.Date. Otherwise it's
difficult to decide which of these classes is to blame for failing tests.

Regards,
Jan


mde

unread,
Apr 1, 2010, 6:46:45 PM4/1/10
to timezone-js
Jan,

Replies below.

> But called with 'impossible' values, new Date() just choses a different
> value using some obscure ECMAScript defined algorithm, and dt.get*()
> returns this different value instead of the original one.
>
> Based on this, I conclude that one cannot use any of the local-time
> based functions of the javascript Date object when dealing with
> arbitrary time zones. This includes, unfortunately, the Date constructor
> and Date.parse().

Using a proxy Date in the background was the simplest way to arrive at
a solution that was completely API-compatible with the existing JS
Date that *mostly works.* For a lot of people, that's sufficient. But
as you point out, it has two significant problems:

1. There are issues around DST leaps caused by the arbitrary way JS
Date handles the switchover.
2. Dates can be generated from a dateparts/timezone combination, but
not from a timestamp/timezone combination.

> I'll post my solution for an arbitrary-time-zone Date replacement later.
> It has a different API than timezoneJS.Date, so it's not a plug-in
> replacement, but it's probably not too difficult to take the concepts
> from that implementation and apply them to timezoneJS.Date.

Anything that fixes these problems would be a huge win. The use of a
proxy Date was just the easiest way to get to a mostly working
solution. The end goal is a timezone-enabled, API-compatible
replacement for Dates that Just WorksTM, and how we get there isn't
very important.

There might be considerations of performance if lots of calculations
that were handled by the proxy Date have to be done in JS, but some
simple benchmarking would give us a good idea of the tradeoff there.

> Additionally, I suggest testing timezoneJS.Date and timezoneJS.timezone
> independently, ie. calling timezoneJS.timezone functions directly from
> the test cases instead of going through timezoneJS.Date. Otherwise it's
> difficult to decide which of these classes is to blame for failing tests.

Nice work with the changes to the tests in your
use_isodates_in_testcases branch. I've merged these to master. Looks
like a good start on getting separate sets of tests.

Thanks!


Matthew

Reply all
Reply to author
Forward
0 new messages