PDA

View Full Version : Date convert timezone issue



mdavis6890
6 Feb 2012, 7:26 AM
I'm having trouble with the DATE convert function, apparently due to timezone issues.

Ext.data.Types, line 159:


parsed = Date.parse(v);
return parsed ? new Date(parsed) : null;


Date.parse('2012-01-11');
// 1326240000000

new Date(1326240000000);
// Date {Tue Jan 10 2012 16:00:00 GMT-0800 (Pacific Standard Time)}
// Oops, lost a day here. In fact, it looks like the first date was treated as midnight, and I lost exactly 8 hours - equal to my timezone offset.

// My crappy fix:
parsed = Date.parse(v);
return parsed ? new Date(parsed + 8 * 3600 * 1000) : null;//Would work if I could figure out how to override the convert function properly.


Two questions:
- What's a better fix?
- How do I override the date convert function?

friend
6 Feb 2012, 7:35 AM
You can do this instead:



var dateObject = Ext.Date.parse('2012-01-11', 'Y-m-d');


Then to output in some preferred format:



var formattedStringDate = Ext.util.Format.date(dateObject, 'm/d/Y');

mdavis6890
6 Feb 2012, 7:54 AM
This is inside the DATE type internal to ExtJS, which wants to return an actual JS Date object. In particular, this is the function used by the reader when getting data from the server and populating a date field in a datastore.

I don't think returning a string that looks like a data achieves the desired result.

friend
6 Feb 2012, 12:09 PM
If you're talking about date formatting as it relates to an Ext.data.Store, all you have to do is be sure to include the dateFormat attribute in your Model config. This give the Model a 'heads-up' on how to parse the incoming date String. In the example below, you're instructing the Model that it will be parsing just a date, no time components involved. However, if the JSON fed to your Store contained ISO 8601 formatted date/time strings (yyyy-mm-dd hh:mm:ss), you would change the dateFormat to 'c'.



Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{name: 'birthDate', type: 'date', dateFormat: 'Y-m-d'},
{name: 'firstName', type: 'string'},
{name: 'lastName', type: 'string'},
{name: 'age', type: 'int'},
{name: 'eyeColor', type: 'string'}
]
});

var myStore = Ext.create('Ext.data.Store', {
model: 'User',
proxy: {
type: 'ajax',
url : '/users.json',
reader: {
type: 'json',
root: 'users'
}
},
autoLoad: true
});

mdavis6890
8 Feb 2012, 6:57 AM
Here's my hack that works for me. I wish I had something better:




Ext.require('Ext.data.Types', function () {
Ext.apply(Ext.data.Types, {
DATE: {
convert: function(v) {
var df = this.dateFormat,
parsed;

if (!v) {
return null;
}
if (Ext.isDate(v)) {
return v;
}
if (df) {
if (df == 'timestamp') {
return new Date(v*1000);
}
if (df == 'time') {
return new Date(parseInt(v, 10));
}
return Ext.Date.parse(v, df);
}

parsed = Date.parse(v);
// Add PST timezone offset in milliseconds.
return parsed ? new Date(parsed + 8 * 3600 * 1000) : null;
}
}
});
});

mystix
8 Feb 2012, 7:56 AM
Date.parse() is a native javascript method available on the js Date object.

from http://www.w3schools.com/jsref/jsref_parse.asp:


The parse() method parses a date string and returns the number of milliseconds between the date string and midnight of January 1, 1970.


In the absence of a 3/4-letter timezone code (e.g. EST, PST, CST etc) in the date string to be parsed, Date.parse() assumes GMT i.e. GMT+0000 (or UTC -- they're the same thing).
In the absence of a time string, Date.parse() assumes midnight.

This is why


// milliseconds since Jan 1, 1970 and midnight of 11th Jan 2012 UTC
Date.parse('2012-01-11'); // returns 1326240000000

is treated like it was midnight, GMT (i.e. 2012-01-11 00:00:00 GMT+0000).

And on conversion back to a Date object


// returns 2012-01-10 16:00:00 GMT-0800 (Pacific Standard Time)
// which is 2012-01-11 00:00:00 GMT+0000
new Date(1326240000000);

i.e. you get the original date you parsed at midnight of GMT.
You haven't lost any days -- the browser's js engine is simply returning what you asked for.

Unless your application only needs to deal with 1 timezone, the "correct" way of parsing Date strings is to always make sure timezone information is included in the Date strings you're parsing:


// returns 1326268800000, an exact point in time
Date.parse('2012-01-11 GMT-0800');

// returns that exact point in time relative to your timezone
new Date(1326268800000);


No "crappy fix" required. ;)

~o)


[ additional note ]
If you're storing / retrieving dates on / from the server, it's best to pass them around as UTC date strings / unix timestamps.

mdavis6890
8 Feb 2012, 10:13 AM
"If you're storing / retrieving dates on / from the server, it's best to pass them around as UTC date strings / unix timestamps."
- Yes, but this is trumped by my desire to simply use whatever comes out of the PostgreSQL date automatically. That's a lot cleaner/easier than building custom date formatting into all my queries.

Besides, a DATE is different than a TIME, TIMESTAMP or DATETIME, and javascript seems to confuse them a little bit. '2012-01-11' should refer to a specific date only, without regard for time or timezones. '2012-01-11 GMT-0800' is a specific point in time, not a date. Postgres correctly recognizes the difference, and has different data types for each. I'm using type 'date,' because that's what I want, and it doesn't have a time component. I'd have to fudge one in. (I'm digressing a little bit here. I know that javascript is not going to suddenly have a true date.)

How can I pull the user's local timezone and build that into the parser like this:



Date.parse(v + getTimeZoneString());

mystix
8 Feb 2012, 11:06 AM
That's easy -- use Ext.Date.parseDate() instead of js's native Date.parse() method:


// returns a js Date object representing
// midnight, 11th Jan 2012 in your (browser's) timezone
Ext.Date.parseDate('2012-01-11', 'Y-m-d');


p.s. the javascript Date object has always represented a specific point in time.
i.e. full date + time + timezone information. No confusion there (for me at least), though I do agree PostgreSQL's naming convention + implementation makes things crystal clear.

mdavis6890
11 Feb 2012, 9:44 AM
That would work, but then I'd be making assumptions about the format of the incoming date. Not something I want to do in such an internal place.

In the end, I think this will work:




// Get timezone offset in ms
var timezoneOffset = (new Date()).getTimezoneOffset() * 60000;

parsed = Date.parse(v);

// Add PST timezone offset in milliseconds.
return parsed ? new Date(parsed + timezoneOffset) : null;

mercuri
23 Feb 2012, 1:08 PM
This was exactly what I needed. Thanks.


If you're talking about date formatting as it relates to an Ext.data.Store, all you have to do is be sure to include the dateFormat attribute in your Model config. This give the Model a 'heads-up' on how to parse the incoming date String. In the example below, you're instructing the Model that it will be parsing just a date, no time components involved. However, if the JSON fed to your Store contained ISO 8601 formatted date/time strings (yyyy-mm-dd hh:mm:ss), you would change the dateFormat to 'c'.



Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{name: 'birthDate', type: 'date', dateFormat: 'Y-m-d'},
{name: 'firstName', type: 'string'},
{name: 'lastName', type: 'string'},
{name: 'age', type: 'int'},
{name: 'eyeColor', type: 'string'}
]
});

var myStore = Ext.create('Ext.data.Store', {
model: 'User',
proxy: {
type: 'ajax',
url : '/users.json',
reader: {
type: 'json',
root: 'users'
}
},
autoLoad: true
});