cibernox / ember-power-calendar

Powerful and customizable calendar component for Ember
http://www.ember-power-calendar.com
Other
211 stars 118 forks source link

Date number doesn't accommodate zone #237

Closed patrickberkeley closed 7 months ago

patrickberkeley commented 4 years ago

Steps

  1. Set the timezone for moment globally: https://momentjs.com/timezone/docs/#/using-timezones/default-timezone/ to America/New_York
  2. Set you computer's clock to America/Los_Angeles (Pacific time)
  3. Observe the calendar day numbers are shifted off by one:

Power Calendar:

Google Calendar:

Proposed solution

If the date is November 7 2019, this line: https://github.com/cibernox/ember-power-calendar/blob/c0f1046b8d1d64d165ee28150ddd3d072b84fffb/addon/components/power-calendar/days.js#L190

produces: 6 as the date. This is because JS Date objects always use the local computer's zone. And 2019-11-07T12:00:00 in America/New_York is 2019-11-06T09:00:00 in America/Los_Angeles. Note the date number is different.

moment(date).date() produces: 7. Here moment's date method accounts for the zone that is set to the default.

In short a fix would be to replace:

number: date.toDate(),

with:

number: moment(date).date(),

Anywhere else in the code that uses native JS Date functions will possibly introduce this same issue since they won't account for the zone that is set for moment.

pauldoerwald commented 4 years ago

This issue is definitely biting us in our application.

jamesdixon commented 4 years ago

Just came across this ourselves.

alexlafroscia commented 4 years ago

I think I'm seeing a similar issue when it comes to the "centered" month.

I am providing a UTC date as the center. Because toDate is called on a moment object, it is automatically converted to the browser's timezone. Depending on what the date provided as the center is, this can shift the date to the previous day, which can shift the whole calendar to the previous month.

It feels like the calendar should respect the timezone of the moment object provided.

patrickberkeley commented 4 years ago

@alexlafroscia that sounds like the same issue.

For anyone who wants to tackle this, it's not quite as straightforward as using a moment object because ember-power-calendar can work with either luxon or moment. So both these repos would need to be PRed: ember-power-calendar-moment, ember-power-calendar-luxon with a method that returns the date using the function from the respective library.

adambedford commented 4 years ago

I'm running into this issue as well

jamesdixon commented 4 years ago

anyone manage to find a workaround for this?

patrickberkeley commented 4 years ago

@jamesdixon yeah we monkey patch it:

// components/power-calendar/days.ts
import moment from 'moment';
// @ts-ignore
import EmberPowerCalendarDaysComponent from 'ember-power-calendar/components/power-calendar/days';

export default class PowerCalendarDaysComponent extends EmberPowerCalendarDaysComponent {
  buildDay(...args) {
    const day = super.buildDay(...args);
    day.number = moment(day.date).date();
    return day;
  }
}

And if you are using range you'll need:

// components/power-calendar-range/days.ts
import moment from 'moment';
// @ts-ignore
import EmberPowerCalendarRangeDaysComponent from 'ember-power-calendar/components/power-calendar-range/days';

export default class PowerCalendarRangeDaysComponent extends EmberPowerCalendarRangeDaysComponent {
  buildDay(...args) {
    const day = super.buildDay(...args);
    day.number = moment(day.date).date();
    return day;
  }
}

Initialize the components (only needed if your overrides are done in one of your own addons):

// instance-initializers/power-calendar-overrides.ts
import ApplicationInstance from '@ember/application/instance';
import PowerCalendarDaysComponent from 'path/to/your/components/power-calendar/days';
import PowerCalendarRangeDaysComponent from 'path/to/your/components/power-calendar-range/days';

export function initialize(appInstance: ApplicationInstance): void {
  appInstance.register('component:power-calendar/days', PowerCalendarDaysComponent);
  appInstance.register('component:power-calendar-range/days', PowerCalendarRangeDaysComponent);
}

export default {
  initialize,
};

We are using moment, so run the date through it, but I'm assuming the above will work if you are using luxon and replace moment with luxon.

jamesdixon commented 4 years ago

@patrickberkeley thanks! will give it a go. very much appreciate it!

Bouke commented 4 years ago

Beware that the datetime you're provided with in the action specified in onSelect is also in the browser's local date. So when working with this date, make sure you convert it to whatever timezone your app is using.

Ideally we could set the display timezone of the calendar component, and work with either luxor or moment instances as well.

mkszepp commented 7 months ago

This bug is now fixed in