-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Timezone support #118
Comments
times ones should just be a matter of what dates you provide, the date math shouldn't need to know what tz you want to work... what bits aren't working? |
So now As I said earlier, I can get halfway there by telling moment to always convert to a given timezone. That causes the times events to show up in the correct timezone, but because the time column start and end dates are computed using I think you're assuming that you can just entirely ignore the timezone and treat all datetimes as timezone-naive, but in reality, there is no such thing as a timezone-naive datetime in javascript. I'd much rather be able to explicitly state the timezone I'd like the calendar to render in than try to manually convert to local time as if the event was already in the local timezone. |
Given that fullcalendar actually explicitly deals in terms of ambiguous time, there seems to be precedent for the difficulty of integrating it explicitly into the core. I can imagine ways to integrate it as a plugin, or at least with minimal footprint in the calendar core by doing something like wrapping a sketch of what the wrapper might look like: const ZonedCalendar = React.createClass({
onSelectSlot(slotInfo) {
return this.props.onSelectSlot({
start: forceZone(slotInfo.start, this.props.timezone),
end: forceZone(slotInfo.end, this.props.timezone),
});
},
onSelectEvent(event) {
return this.props.onSelectEvent(event._originalEvent);
},
render() {
const events = this.props.events.map(event =>
toAmbiguousEvent(event, this.props.timezone));
return <BigCalendar
{ ...this.props }
onSelectSlot={ this.onSelectSlot }
onSelectEvent={ this.onSelectEvent }
events={ events }
/>;
},
});
function toAmbiguousEvent(event, tz) {
return {
...event,
start: toAmbiguousDate(event.start, tz),
end: toAmbiguousDate(event.end, tz),
// Thinking a scheme like this is best: it means that the event object you
// put in the event object you get out, making equality comparison later
// on easier.
_originalEvent: event,
};
}
function toAmbiguousDate(dt, tz) {
// just wrote this one, so it's a bit of a guess
// the sign of getTimezoneOffset is opposite of moment
const browserOffset = -1 * new Date().getTimezoneOffset();
const result = moment(dt).tz(tz);
// adding moment's offsets takes you back to UTC, so we add the utcOffset
// of the input and subtract the offset of the browser to render the time
// for the browser.
result.add(result.utcOffset() - browserOffset, 'minutes');
return result.toDate();
}
function forceZone(dt, tz) {
// pulled this from our fullcalendar integration
const result = moment(dt).tz(tz);
result.add(result.utcOffset(), 'minutes');
return result;
} |
I haven't looked at what methods BigCalendar uses internally from the localiser, but I was thinking it might work if you could bind moment with the timezone. Something like...
Then pass UPDATE: this doesn't work #sadface |
we are explicitly deferring to date libraries like moment to handle this sort of thing via localizers. I haven't delta with TZ issues myself but I'd be interested to know where we aren't being ambiguous about it. I'm guessing that's mostly a matter of date math creating date objects |
Let me articulate the problem a bit better. When Big Calendar renders dates it is displaying these on the calendar using your browser's timezone (which comes from their device configuration whatever that may be). If you change your timezone setting the dates in Big Calendar will move to reflect that change. This is what I don't want to happen, I want to render dates in the calendar in a specific timezone irrespective of the users browser's timezone. The reason for this, is the particular project I'm working on, the users will have a timezone associated with their profile. When they see dates in the calendar they want it to be based on this defined timezone. This is relevant because if they take a trip overseas and their device timezone changes they don't want the dates in the calendar to change as they haven't changed their timezone preference. Does that make sense? I've managed to hack around this by applying an offset to the start / end date of each event that is the delta between their timezone preference and the browser's timezone (though detecting this is possibly inaccurate according to moment-timezone). |
I've implemented a similar workaround which I'd prefer not to have to do. In my component that wraps Big Calendar, I have a couple of methods:
Dates that come out of BigCalendar (like the one passed to |
I understand the issue y'all, I just don't know why and how RBC would be to blame. it defers all rendering of dates to the localizer. What I'd like to happen is that you just configure Moment or Globalize to handle timezones and it should work, and RBC just renders whatever you give it. the only place I think this might break is where RBC creates dates but I think those places are fairly limited and in most cases it could use the offset of the dates it already has. if someone wants to dig more into it I'm sure I'd accept a PR |
Ah ok, yes my main concern is the dependency on |
@dgouldin I don't think it needs to be timezone aware though. all the dates should be the same TZ and it should maintain them during any Math yeah? It may need to be adjusted to do that though, thats fine as well (I own that dep anyway) |
The problem is as soon as you start working with native |
That is only true if you don't specify an offset, we can have any date math maintain the input date's offset fairly straight forwardly I think |
But offsets are not a constant. Daylight savings affects the offset depending on the date. |
wanna circle back to this issue, I am trying to set default timezone in moment but it doesn't seem like BigCalendar honoring the moment.defaultZone value Fwiw, when I set the system timezone to Asia/Tokyo which works like a charm but below code snippets simply doesn't work. any thoughts?
While react-datepicker seems working as expected with
|
@wendykwok I'm using it this way: import moment_timezone from 'moment-timezone';
moment_timezone.tz.setDefault('Asia/Yerevan');
BigCalendar.momentLocalizer(moment_timezone); And that works correctly for me. |
Tried both solutions from @wendykwok and @fxfactorial however none of them worked. BigCalendar still displays the event in user local timezone instead of the timezone specified in moment-timezone using setDefault |
I have a workaround by setting the date components manually into the Javascript Date() startdate is the moment date object of the event in the target timezone. event.startdate = new Date(startdate.year(), startdate.month(), startdate.date(), startdate.hour(), startdate.minute(), 0);
event.enddate = new Date(enddate.year(), enddate.month(), enddate.date(), enddate.hour(), enddate.minute(), 0); |
I am using the moment-timezone setDefault method posted by @fxfactorial and it is working for setting the time bar but not the correct day in week or month view. Creating a new moment() object does create with the correct date, not sure why the calendar isn't using it. |
@pencilcheck I would love to see a more detailed explanation about that because I don't understand how the calendar picks startdate instead of start/end on the events. |
@pencilcheck workaround works for me. Just not sure if it works correctly with the DST changes and so on, because it's a "fake move": we are changing the date's timezone. While 2:30am might exist in startdate's timezone, it may not exist in |
However... there's a small bug in week/day views when during the shown week/day there has been a DST change. Example: My local browser's timezone is Europe/Warsaw . We have DST change on 26th march at 2am (it goes forward to 3am). I entered 4 events:
When passing
(so times are given in a correct timezone) However, this is how ReactBigCalendar renders it: So, any events after 2am on 26th march are shown in a wrong row (3am event is shown in 2am row). However when I switch to a next week, everything is back to normal: Day view problem (buggy placement is only on 26th after 2am): Month view is okay: Any ideas how to tackle it? ;) --- edit --- Just seen it now it's already mentioned in #334 , #313 , and #221 . |
Another problem appeared for me, when I started to use
Of course I could just not use moment.tz.setDefault() to avoid the problems... but seems like I like to complicate things ;> Anyway, I made it work correctly. Clicking, dropping the events, showing the datetimes - works well, without any changes in BigCalendar's code. Here's the code:
BigCalendar component wrapper:
|
This may be too obvious to be helpful, but if you have times as datetime strings with a UTC offset, eg "2017-04-26T11:55:54-04:00", they will render in the local time (eg "11:55:54 am") when formatted with Moment.js. |
@jtomaszewski you are a legend. Your piece of magic worked for me. I made a few modifications to make use of the startAccessor and endAccessor instead of mapping all events import React, {Component} from 'react'
import PropTypes from 'prop-types'
import BigCalendar from 'react-big-calendar'
import {accessor} from 'react-big-calendar/lib/utils/accessors'
import moment from 'moment'
import 'moment-timezone'
import 'react-big-calendar/lib/css/react-big-calendar.css';
// remember the browser's local timezone, as it might by used later on
BigCalendar.tz = moment.tz.guess();
// format all dates in BigCalendar as they would be rendered in browser's local timezone (even if later on they won't)
const m = (...args) => moment.tz(...args, BigCalendar.tz);
m.localeData = moment.localeData;
BigCalendar.setLocalizer(
BigCalendar.momentLocalizer(m)
);
export const convertDateTimeToDate = (datetime, timeZoneName) => {
const m = moment.tz(datetime, timeZoneName);
return new Date(m.year(), m.month(), m.date(), m.hour(), m.minute(), 0)
};
export const convertDateToDateTime = (date, timeZoneName) => {
const dateM = moment.tz(date, BigCalendar.tz);
return moment.tz({
year: dateM.year(),
month: dateM.month(),
date: dateM.date(),
hour: dateM.hour(),
minute: dateM.minute(),
}, BigCalendar.tz);
};
class TimeZoneAgnosticBigCalendar extends Component {
static propTypes = {
events: PropTypes.array,
onSelectSlot: PropTypes.func,
onEventDrop: PropTypes.func,
timeZoneName: PropTypes.string,
startAccessor: PropTypes.func,
endAccessor: PropTypes.func,
};
static defaultProps = {
startAccessor: 'start',
endAccessor:'end'
};
startAccessor = (event) => {
const start = accessor(event, this.props.startAccessor);
return convertDateTimeToDate(start, this.props.timeZoneName);
};
endAccessor = (event) => {
const end = accessor(event, this.props.endAccessor);
return convertDateTimeToDate(end, this.props.timeZoneName);
};
render() {
const { onSelectSlot, onEventDrop, timeZoneName, ...props } = this.props;
const bigCalendarProps = {
...props,
startAccessor: this.startAccessor,
endAccessor: this.endAccessor,
onSelectSlot: onSelectSlot && (({start, end, slots}) => {
onSelectSlot({
start: convertDateToDateTime(start, timeZoneName),
end: convertDateToDateTime(end, timeZoneName),
slots: slots.map(date => convertDateToDateTime(date, timeZoneName)),
})
}),
onEventDrop: onEventDrop && (({event, start, end}) => {
onEventDrop({
event,
start: convertDateToDateTime(start, timeZoneName),
end: convertDateToDateTime(end, timeZoneName),
})
}),
};
return <BigCalendar {...bigCalendarProps} />
}
}
export default TimeZoneAgnosticBigCalendar; |
@peterox @jtomaszewski Thank you two very much for your code, that saved me a few hours. However I'm a bit confused about Am I missing something here or is that a bug? |
Oh, true. AFAIK everything works okay with this code. So, that |
So I'm using react-big-calendar version 0.14.4, moment version 2.18.1, moment-timezone version 0.5.13, and for me, the simple solution worked, i.e.:
Just throwing that out there for any new users of this library. EDIT: Never mind, it works in the sense that the events are in UTC time, but the 'Day' view starts at 7 AM and ends at 7 AM of the next day, which could be confusing to the user, i.e. an event at 4:00 AM GMT Friday August 4th is shown on Thursday August 3rd. |
I am trying to address the issue raised in @ramkandasamy edit - when I pass a localizer with a different time zone to BigCalendar, the events display correctly, but the Day and Week views still show the hours of the day (and organize events into day columns) based on my browser local time. Has anyone figured out a good fix for this? |
using the tweaks suggested by @peterox and @jtomaszewski with a user-defined timezone different than the browser's almost works, but the current time indicator on the day and week views shows in the "wrong" place. Imho, the only way to actually solve this issue without hacking is to use moment for calculations instead of date-arithmetic, as the current .startOf(date,'day') and endOf(date,'day') functions used by the date-arithmetic don't take into account a desired timezone, resulting in 0:00 of browser timezone, that as others have mentioned will result in the times being rendered very weirdly. |
What's the plan for solving this issue? We may be able to help as long as the devs recommend a plan so that our PR actually gets merged. |
@ehahn9 Thanks for that gist - very helpful! Are the referenced files available somewhere -- /client/utils/timezones, /client/utils/date? |
@nileshtrivedi There isn't a specific plan, I've outlined above a few steps I think are probably required to support TZ's, and others have contributed as well, nothing is probably sufficient tho, just first steps. If you wanna jump in we are happy to support, tho admittedly it's not an issue i've a lot of familiarity in doing correctly. In terms of constraints tho:
|
@jquense |
I'm having the same problem @khoaanh2212 plus: Errors
** I'm using moment-timezone |
@ehahn9 Thanks for that gist - very helpful! Are the referenced files available somewhere -- /client/utils/timezones, /client/utils/date? |
@jtomaszewski helped solve our problem. Use his wrapper. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
First, thank you @dgouldin Then, don't forget to add : slotPropGetter: slotPropGetter && ((date) => {
return slotPropGetter(
convertDateToDateTime(date, timeZoneName)
)
}), On my case, I want to manipulate the dates more easily in full ISO. Thx for the lib and this extended timezone support ! 😄 |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
What's the version this patch works on? |
@diegoarcega I am having the same issue as you. The problem is that the call to |
This still working! It is a very nice approach to set the timezone! |
For those using
and then in your calendar component:
|
I had this issue and solved it with the following code (start and end):
|
When needing timezone specific implementations, I would use either the moment or luxon localizers. We have examples for this in our documentation. |
It would be nice if there was a way to display a calendar in a timezone other than the browser's local one.
moment-timezone
supports timezone conversion using a named zone:http://momentjs.com/timezone/docs/#/using-timezones/converting-to-zone/
I attempted to make this work by setting moment's default timezone, but it looks like all of the date "math" to determine the range of days and weeks is done by
date-arithmetic
, a library that AFAICT has no awareness of timezones.The text was updated successfully, but these errors were encountered: