Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue when parsing a date in western timezones #138

Closed
regebro opened this issue Apr 17, 2014 · 7 comments
Closed

Issue when parsing a date in western timezones #138

regebro opened this issue Apr 17, 2014 · 7 comments

Comments

@regebro
Copy link

regebro commented Apr 17, 2014

We have date inputs that look like this, which we use Pikaday 1.2.0 with:

I've noticed that Pikaday will parse the date 2014-04-17 and convert it into a JS Date object. But if you have a timezone that is west of UTC, it will intepret it as 2014-04-17 00:00, UTC, and promptly fill in the field with "Wed Apr 16 2014". Saving the form will submit 2014-04-16 and the date will change.

This has been replicated on IE10 and FF28.

One solution could be to change the line

date = new Date(Date.parse(opts.field.value))

to

date = new Date(Date.parse(opts.field.value) + 43200000)

To add 12 hours to the date. But it is a bit hackish, and there are UTC-13 timezones out there where it still would not work.

(We do use moment.js as well, but for some reason Pikaday doesn't seem to detect it).

@cwesterlund
Copy link

I'm also struggeling with the same problem which seems to be that since Pikaday is using the Javascript date object the entered date will be converted to local time zone.
That is why you get the date before the entered one.

I thought I could get past this by using moment.js:

// Testing in timezone -12
var momentDate = moment.utc("2014-03-02");
var momentDateString = momentDate.format("YYYY-MM-DD");
// momentDateString is now 2014-03-02              . Correct
picker.setMoment(momentDate);
// pikaday picker shows 2014-03-01                 . Incorrect, should be 2014-03-02
var pickerMomentDateString = picker.getMoment().format("YYYY-MM-DD");
// pickerMomentDateString is now 2014-03-01        . Incorrect,  should be 2014-03-02

This is because of Pikaday is using the Date object I would assume. Don't know how to get around this. I've tried Jquery.UI.datepicker and it has the same problem.

(The reason why Pikaday doesn't detect moment.js might be that you aren't loading moment.js before you load pikaday.js?)

@cwesterlund
Copy link

My workaround is now to set the local timezone when setting the date to PikaDay:

setDateStringToDatePicker: function(strDate, datePicker){      
  var timezone = moment(new Date()).format("ZZ");        
  datePicker.setMoment(moment(strDate + " 10:00 " + timezone));        
},

Have tested this in -1200, +1000, +2000, +1200 and it seems to work.

@rikkert
Copy link
Member

rikkert commented May 16, 2014

Yes, Pikaday uses local dates not UTC dates.
First read your source date into the correct local date before passing it to Pikaday.
Does that make sense?

@regebro
Copy link
Author

regebro commented May 16, 2014

The source date IS the correct local date. The problem is that is is displayed as the day before, because JS date object will apparently assume it's UTC.

@nmcgann
Copy link
Contributor

nmcgann commented Oct 24, 2014

I have a fix for this that avoids the inherent javascript timezone problem with the date object. I was selecting a date in a form field but it was giving the previous day's date and a bit of research revealed this was a common problem.

  1. Modify pikaday to allow a callback on toString.
 // callback function
 onSelect: null,
onOpen: null,
onClose: null,
onDraw: null,
onToString: null // ***  added to create callback for toString function

and modify toString to use the callback:

 /**
* return a formatted string of the current selection (using Moment.js if available)
 */
 toString: function(format) // *** modified to allow onToString callback to re-format output
 {
    return !isDate(this._d) ? '' : hasMoment ? moment(this._d).format(format || this._o.format) : (this._o.onToString) ? this._o.onToString(this._d.toDateString()) : this._d.toDateString();
 },
  1. Re-format and return the output of toDateString() in the callback

The callback can take the toDateString() output and re-format it to display the date without the enforced timezone correction that things like toISOString() give.

(output of toString() is like: Wed Oct 01 2014 00:00:00 GMT+0100 (GMT Standard Time) so some string manipulation can easily extract the un-timezoned date)

It's a bit hack-ish (and I'm not a javascript expert so my modification of toString to detect if the callback is there doesn't check if it is a callable function properly), but the solution works like a charm for me.

@derduher
Copy link

@regebro et al after struggling with this problem myself for several hours it dawned on me that YYYY-MM-DD is in fact ISO-8601. While ISO-8601 specifies that strings without zone specifiers are to be treated as local time Date.parse in javascript deviates from this in ES5 and assumes UTC. ES6 is expected to correct this. In the mean time you can use setMoment to obtain the correct behavior.

Note however setMoment has inconsistencies with Date.parse. eg '' is not parsed as Invalid Date. I might open a tick for that.

@jkrasnay
Copy link

An easy workaround is to construct the date using integer fields, e.g. new Date(2015, 5, 16). Note that the month must be zero-based.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants