Skip to content

Instantly share code, notes, and snippets.

@ehahn9
Created June 16, 2018 00:06
Show Gist options
  • Save ehahn9/704bf086c058c74e3a5a43067d053089 to your computer and use it in GitHub Desktop.
Save ehahn9/704bf086c058c74e3a5a43067d053089 to your computer and use it in GitHub Desktop.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { accessor } from 'react-big-calendar/lib/utils/accessors';
import { accessor as accessorPropType } from 'react-big-calendar/lib/utils/propTypes';
import { noop } from 'lodash';
import { zonedToLocal, localToZoned } from '/client/utils/timezones';
import { hasTime, shiftDate, shiftHour } from '/client/utils/date';
/**
* withTimeZone - HOC to add time zone support to react-big-calendar
*
* import Calendar from 'react-big-calendar';
* import withTimeZone from './withTimeZone';
* const ZonedCalendar = withTimeZone(Calendar)
*
* render() {
* return <ZonedCalendar zone="Pacific/Honolulu" ... />
* }
*/
export default function withTimeZone(Calendar) {
return class TimeZoneCalendar extends Component {
static propTypes = {
events: PropTypes.array,
date: PropTypes.instanceOf(Date).isRequired,
zone: PropTypes.string.isRequired,
startAccessor: accessorPropType,
endAccessor: accessorPropType,
allDayAccessor: accessorPropType,
onEventDrop: PropTypes.func,
onEventResize: PropTypes.func,
onSelectSlot: PropTypes.func
};
static defaultProps = {
startAccessor: 'start',
endAccessor: 'end',
allDayAccessor: 'allDay',
onEventDrop: noop,
onEventResize: noop,
onSelectSlot: noop
};
startAccessor = (event) => this.timeAccessor(event, 'startAccessor');
endAccessor = (event) => this.timeAccessor(event, 'endAccessor');
timeAccessor = (event, accessorName) => {
const value = accessor(event, this.props[accessorName]);
const allDay = accessor(event, this.props.allDayAccessor);
const zone = allDay ? 'UTC' : this.props.zone;
// see discussion in timeszones.js about zonedToLocal. allDay events are in UTC
return zonedToLocal(value, zone);
}
handleSelectSlot = ({ start, end, slots }) => {
// FIXME: this is basically the same logic as toZonedUpdate! DRY it out
const allDay = !hasTime(start) && !hasTime(end);
const zone = allDay ? 'UTC' : this.props.zone;
// r-b-c has no time zones - whatever time it says is really in the
// local time zone and we need to convert it to the same time in
// the target zones (except allDay events are always in UTC).
//
// also, when selecting allDay events, r-b-c gives the end time
// as an INCLUSIVE date (e.g. a one day allDay will start and end
// on the same date), but events have the end as an EXCLUSIVE time
// (e.g. one day events have end on the NEXT day) - so fix that here.
start = localToZoned(start, zone);
if (allDay) end = shiftDate(end, 1); // FIXME: does this really belong in withTimeZone - fix up with new r-b-c allDay, etc.
end = localToZoned(end, zone);
slots = slots.map(date => localToZoned(date, zone)); // TODO: I don't understand the slots arg to onSelectSlot
this.props.onSelectSlot({ start, end, allDay, slots });
}
handleEventDrop = ({ event, start, end }) =>
this.props.onEventDrop(this.toZonedUpdate({ event, start, end }));
handleResize = (resizeType, { event, start, end }) =>
this.props.onEventResize(resizeType, this.toZonedUpdate({ event, start, end }));
toZonedUpdate = ({ event, start, end }) => {
// since we have nothing better, allDay is determined by the start date
// only because RBC computes the end date as start+duration
const allDay = !hasTime(start);
const zone = allDay ? 'UTC' : this.props.zone;
// if we're dropping an allDay event onto the grid (non-allDay),
// make a 1 hour event
if (event.allDay && !allDay) {
end = shiftHour(start, 1);
}
// TODO: there's a bug in rbc where dragging from a non-allDay
// to an allDay just returns the drag as if it was the same
// time on the new day, so alas, this case won't work:
// if (allDay && !event.allDay) {
// end = addOne(start, 'day');
// }
// TODO: fix this in r-b-c
// just like selection, if we're creating an allDay event,
// create it in UTC zone.
start = localToZoned(start, zone);
end = localToZoned(end, zone);
return { event, start, end, allDay };
}
render() {
const { date, zone, ...props } = this.props;
const bigCalendarProps = {
...props,
date: zonedToLocal(date, zone),
getNow: () => zonedToLocal(new Date(), zone),
startAccessor: this.startAccessor,
endAccessor: this.endAccessor,
onSelectSlot: this.handleSelectSlot,
onSelectEvent: this.handleSelectEvent,
onEventDrop: this.handleEventDrop,
onEventResize: this.handleResize
};
return <Calendar {...bigCalendarProps} />;
}
};
}
@cdimitroulas
Copy link

Would you be able to release the code for these imports as well?

import { zonedToLocal, localToZoned } from '/client/utils/timezones';
import { hasTime, shiftDate, shiftHour } from '/client/utils/date';

@ehahn9
Copy link
Author

ehahn9 commented Jan 7, 2020

Sure - this is really ancient stuff and alas, I'm not currently working on it any longer (I'd suggest using date-fns-tz instead)... but here you go!

https://github.com/ehahn9/rbc-timezone-utils.git

@cdimitroulas
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment