adopted-ember-addons / ember-pikaday

A datepicker component for Ember CLI projects.
MIT License
158 stars 169 forks source link

In IE11, picking a date sets the value to the day before #151

Open jdhines opened 7 years ago

jdhines commented 7 years ago

Everything works fine in Chrome, but in IE, as soon as you click a day, or if entering the day manually, say "5/6/2017" and tabbing to the next field, the day becomes the previous day. I'm using the useUTC=true prop, which fixed this issue upon saving to the backend in Chrome, but in IE, this is happening as soon as a day is selected, so I don't know what's going on or how to correct it.

duizendnegen commented 7 years ago

Welcoming a PR, the problem comes from UTC and non-UTC to use different methods. We need to normalise this.

See https://github.com/edgycircle/ember-pikaday/blob/master/addon/mixins/pikaday.js#L106

Could be combined with #106

ghost commented 7 years ago

This is also not working for me in Safari.

jdhines commented 7 years ago

I don't know how I'd change ember-pikaday to solve this, so I'm not the one to do a PR, but I did solve this in my project by adding a computed field in my model, using that field in my template, and saving that computed field to the actual field when saving the model. Seems like a lot of work to me, but it's not all this add-in's fault; I don't understand why dates are so difficult to do right in my application; may be in part because the database has just a date, and in JavaScript it's all date and time. If I could just use strings, it would be easier. Does this plugin support that?

app/models/my-model.js

//uses the dates mixin
export default DS.Model.extend({
  // ...
  date: attr('date'),
  dateTransform: function() {
    var d = this.get('date');
    if (d) {
      //if it is a valid date, then we want to convert to UTC or else we'll have "day before" issues
      //set the time to noon; this will account for all US time zones
      d = convert(d);
      d = moment(d).utc().hours(12).toISOString();
    }
    return d;
  }.property('date'),
});

app/controllers/my-component.js

save: function() {
      this.get('model').set('date',this.get('dateTransform'));
      get(this, 'model').save().then(function(){
        //stuff
      });
    }

mixins/dates.js

//mixins/dates.js
import Ember from 'ember';

function convert(d) {
  // Converts the date in d to a date-object. The input can be:
  //   a date object: returned without modification
  //  an array      : Interpreted as [year,month,day]. NOTE: month is 0-11.
  //   a number     : Interpreted as number of milliseconds
  //                  since 1 Jan 1970 (a timestamp)
  //   a string     : Any format supported by the javascript engine, like
  //                  "YYYY/MM/DD", "MM/DD/YYYY", "Jan 31 2009" etc.
  //  an object     : Interpreted as an object with year, month and date
  //                  attributes.  **NOTE** month is 0-11.
  return (
    d.constructor === Date ? d :
    d.constructor === Array ? new Date(d[0],d[1],d[2]) :
    d.constructor === Number ? new Date(d) :
    d.constructor === String ? new Date(d) :
    typeof d === "object" ? new Date(d.year,d.month,d.date) :
    NaN
  );
}

function compare(a,b) {
  // Compare two dates (could be of any type supported by the convert
  // function above) and returns:
  //  -1 : if a < b
  //   0 : if a = b
  //   1 : if a > b
  // NaN : if a or b is an illegal date
  // NOTE: The code inside isFinite does an assignment (=).
  return (
    isFinite(a=convert(a).valueOf()) &&
    isFinite(b=convert(b).valueOf()) ?
    (a>b)-(a<b) :
    NaN
  );
}

function inRange(d,start,end) {
  // Checks if date in d is between dates in start and end.
  // Returns a boolean or NaN:
  //    true  : if d is between start and end (inclusive)
  //    false : if d is before start or after end
  //    NaN   : if one or more of the dates is illegal.
  // NOTE: The code inside isFinite does an assignment (=).
  return (
    isFinite(d=convert(d).valueOf()) &&
    isFinite(start=convert(start).valueOf()) &&
    isFinite(end=convert(end).valueOf()) ?
    start <= d && d <= end :
    NaN
    );
}

var Dates = Ember.Mixin.create({
  convert: convert,
  compare: compare,
  inRange: inRange
});

export {compare, convert, inRange};
export default Dates;