PDA

View Full Version : [3.0+] Ext.ux.DateFormat / Ext.ux.TimeZone: Java-like dates and timezones



divestoclimb
13 May 2010, 6:10 AM
While I greatly appreciate the enhancements to the Date object included in Ext, they have a few limitations especially when it comes to support for time zones. The builtin methods only support the user's current geographic time zone, and can't tell me what time it is in time zones other than the local one or GMT. Some of this is based on Javascript limitations, but I've built some enhancements that make the situation at least a little better. The perfect solution is also in sight if anyone wants to pursue it.

First a class is needed to store information about the desired time zone:


Ext.ns("Ext.ux");

/**
* TimeZone class analogous to the one in Java.
* This class was written because, although Ext's date formatting and parsing
* functions are pretty good, they can't handle dates in timezones outside
* the local client timezone. This class allows specifying a time zone by its
* abbreviation so it can be attached to a DateFormat object for output.
* @param zone The string abbreviation for this time zone. If omitted, tries
* to set using the client's time zone.
*
* WARNING: this class was written for a limited scope of North America. Time
* zone abbreviations are duplicated throughout the world and are not
* consistent. This class only recognizes time zones for North America and the
* military.
*/
Ext.ux.TimeZone = function(zone) {
this.zone = zone || new Date().getTimezone();
};

// Stores all recognized timezone abbreviations and their properties: if it's
// a daylight savings timezone and the difference in minutes between UTC and
// the time zone.
// Found at http://www.timeanddate.com/library/abbreviations/timezones/
// Yes, I know, this is the WRONG way to do this. Some abbreviations are
// duplicated around the world. However, I can't do this the right way and
// maintain backward compatibility with Ext's getTimezone() function so I'm
// using a subset of the abbreviations.
Ext.ux.TimeZone.ZONES = {
A: -60,
ADT: { offset: 180, isDaylight: true },
AKDT: { offset: 480, isDaylight: true },
AKST: 540,
AST: 240,
B: -120,
C: -180,
CDT: { offset: 300, isDaylight: true },
CST: 360,
D: -240,
E: -300,
EDT: { offset: 240, isDaylight: true },
EST: 300,
F: -360,
G: -420,
GMT: 0,
H: -480,
HAA: { offset: 180, isDaylight: true },
HAC: { offset: 300, isDaylight: true },
HADT: { offset: 540, isDaylight: true },
HAE: { offset: 240, isDaylight: true },
HAP: { offset: 420, isDaylight: true },
HAR: { offset: 360, isDaylight: true },
HAST: 600,
HAT: { offset: 150, isDaylight: true },
HAY: { offset: 480, isDaylight: true },
HNA: 240,
HNC: 360,
HNE: 300,
HNP: 480,
HNR: 420,
HNT: 210,
HNY: 540,
I: -540,
K: -600,
L: -660,
M: -720,
MDT: { offset: 360, isDaylight: true },
MST: 420,
N: 60,
NDT: { offset: 150, isDaylight: true },
O: 120,
P: 180,
PDT: { offset: 420, isDaylight: true },
PST: 480,
Q: 240,
R: 300,
S: 360,
T: 420,
U: 480,
UTC: 0,
V: 540,
W: 600,
X: 660,
Y: 720,
Z: 0
};

Ext.apply(Ext.ux.TimeZone.prototype, {
/**
* Set the time zone to use for this object.
* @param zone A string representing the time zone abbreviation to use
*/
setTimezone: function(zone) {
this.zone = zone;
},
/**
* Retrieve the time zone abbreviation in use on this object.
* @return A string containing the abbreviation
*/
getTimezone: function() {
return this.zone;
},
/**
* Get the number of minutes offset from UTC for this time zone
* @return The number of minutes
*/
getTimezoneOffset: function() {
var z = Ext.ux.TimeZone.ZONES[this.zone];
if(typeof z === "object") {
return z.offset;
} else {
return z;
}
},
/**
* Determine whether the current timezone is in daylight savings.
* @return true if the timezone is a daylight savings timezone, false
* if not
*/
isDST: function() {
var z = Ext.ux.TimeZone.ZONES[this.zone];
if(typeof z === "object") {
return z.isDaylight || false;
} else {
return false;
}
}
});

/* Test suite

console.log("-- Ext.ux.TimeZone tests --");
console.log("1. EST");
var est = new Ext.ux.TimeZone("EST");
console.log(" timezone: "+est.getTimezone());
console.log(" offset: "+est.getTimezoneOffset());
console.log(" isDST: "+est.isDST());
console.log("2. EDT");
var edt = new Ext.ux.TimeZone("EDT");
console.log(" timezone: "+edt.getTimezone());
console.log(" offset: "+edt.getTimezoneOffset());
console.log(" isDST: "+edt.isDST());
*/
Since Ext can only retrieve timezone information based on the three-letter abbreviation, I chose to use those abbreviations as they apply to North America. If timezones like "US/Eastern" were used it would be nontrivial to map "EDT" or "EST" to it.

The right way to do this would be to index time zones as strings like "US/Eastern" or "America/New York", then use the non-unique abbreviation returned by Date.getTimezone (e.g. "EST"), together with the offset from Date.getTimezoneOffset to determine which of the possible EST time zones the user is currently in. This would require encapsulating the world's timezone data into Javascript objects and is beyond my needs for the class so I haven't bothered with it, but such a class could almost be a drop-in replacement for this simpler one.

Ext.ux.DateFormat:


Ext.ns("Ext.ux");

/**
* DateFormat class analogouos to the one in Java.
* This class was written because, although Ext's date formatting and parsing
* functions are pretty good, they can't handle outputting dates in timezones
* outside the local client timezone. This class circumvents that restriction.
* @param format The format string to use for parsing & output. See Ext's
* Date class for documentation.
* @param tz A Timezone object to use when outputting a Date, or the string
* abbreviation to pass to create a new TimeZone object. If omitted, creates a
* new TimeZone object under the client's current time zone.
*/
Ext.ux.DateFormat = function(format, tz) {
this.fmt = format;
if(typeof tz === "object") {
this.tz = tz;
} else {
this.tz = new Ext.ux.TimeZone(tz);
}
};

Ext.apply(Ext.ux.DateFormat.prototype, {
/**
* Set the TimeZone object to use when outputting a Date.
* @param tz The TimeZone object to use.
*/
setTimezone: function(tz) {
if(typeof tz === "object") {
this.tz = tz;
} else {
this.tz = new Ext.ux.TimeZone(tz);
}
},
/**
* Retrieve the current TimeZone in use for this object.
* @return the DateFormat's current TimeZone object.
*/
getTimezone: function() {
return this.tz;
},
/**
* Format the given date.
* @param d The Date object to format
* @return The formatted Date
*/
format: function(d) {
var dateInZone = new Date(d.getTime());
if(this.tz) {
dateInZone = dateInZone.add(Date.MINUTE, d.getTimezoneOffset() - this.tz.getTimezoneOffset());
// Override timezone functions on this object before
// calling its format() method
Ext.apply(dateInZone, {
getTimezone: this.tz.getTimezone.createDelegate(this.tz),
getTimezoneOffset: this.tz.getTimezoneOffset.createDelegate(this.tz)
});
}
return dateInZone.format(this.fmt);
},
/**
* Parse the given date string as a Javascript Date object according
* to this formatter's configuration (format, timezone)
* @param datestr The string from which to parse the date
* @return The date parsed as a Date object, or null if the parsing
* failed
*
* WARNING: Date.parseDate does its own timezone conversion if it's
* explicitly defined in the format. This method should only be used
* when parsing a date from a format which does not specify a
* timezone (i.e. not with ISO8601 dates).
*/
parse: function(datestr) {
var d = Date.parseDate(datestr, this.fmt);
if(! d) {
return null;
} else {
// Convert to the current timezone
return d.add(Date.MINUTE, this.tz.getTimezoneOffset() - d.getTimezoneOffset());
}
}
});

/* Test suite
console.log("-- Ext.ux.DateFormat tests --");
var d = new Date();
var df = new Ext.ux.DateFormat("c");
console.log(df);
console.log("Today's date: "+df.format(d));
var dfZulu = new Ext.ux.DateFormat("c", new Ext.ux.TimeZone("MDT"));
console.log("Today's date in MDT: "+dfZulu.format(d));
*/

faridadhami
19 May 2010, 12:15 PM
The format object you posted is interesting, but I noticed a gap trying to use it. The problem is does not recognize if it should apply the standard or daylight timezone for are date. So I should recognize the right timezone by other means before applying.
i was wondering if there is any other JavaScript library that does that?

Thanks

Farid

divestoclimb
24 May 2010, 7:31 AM
It could be done and I'd like to see it as well, but it would be rather complex. You'd either have to use a server-side component to do the time zone lookup or embed the tzinfo database in Javascript and keep it updated as time zone information changes. That would demand something that can take a standard tzinfo database and automatically export it as JSON, and it may significantly increase the page size.

A better option I just thought of would be to retrieve the tzinfo database in bulk and cache it in DOM storage, then redownload it whenever an update is made. Regardless, it's a design challenge that I would need help to overcome, that's why I didn't do it that way.