Skip to content

Commit

Permalink
feat: add Time Zone support using localizer date math (jquense#2023)
Browse files Browse the repository at this point in the history
* full timezone support with moment

* chore: remove comment

* chore: Add timeswitching example

* chore: continued tz efforts

* chore(timezones): Finalize core bits for handling timezones

* docs: Add documentation

* chore: CI tests don't like modern syntax

* chore: Move DST calculations in to the localizers

* chore: Further refine math from localizer

* chore: Further refine localizer date math around same date comparison

* chore: Finalize all date methods being called from localizer

* chore: Corrections to momentLocalizer getEventRange

* chore: Refine and use moment comparison operations

* chore: general cleanup

* chore: Rebuild examples

* fix: Correct moment localizer range() method, to ensure all dates handled as tz

* chore: Use clearer variable name

* fix: Correct method return

* fix: Correct usages of let and const for better GC and immutability

* feat: Add a new luxonLocalizer and expand documentation

* tests: Tested for the new luxonLocalizer

Still have to figure out how to run all tests for each localizer in a single test run

* chore: Bundle examples

* chore: Reset the examples script from local ip usage

* fix: startOfWeek/endOfWeek calculations

According to the CLDR supplemental data, locales can have the first day of the week as
Friday, Saturday, Sunday or Monday, so we need to properly calculate the beginning of
our calendar week.

* chore: fix comment

* fix: Remove that localIp bit again

* chore: General cleanup

* fix: Correct luxon endOfWeek

* fix: Correct endOfDTWeek
  • Loading branch information
cutterbl authored Sep 28, 2021
1 parent d695d0a commit ad8defa
Show file tree
Hide file tree
Showing 46 changed files with 61,118 additions and 31,886 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
],
"plugins": [
["babel-plugin-transform-rename-import", {
replacements: [{ original: "lodash", replacement: "lodash-es" }]
"replacements": [{ "original": "lodash", "replacement": "lodash-es" }]
}]
]
}
Expand Down
24 changes: 12 additions & 12 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
{
"./dist/react-big-calendar.js": {
"bundled": 497580,
"minified": 153472,
"gzipped": 47831
"bundled": 535365,
"minified": 165479,
"gzipped": 51024
},
"./dist/react-big-calendar.min.js": {
"bundled": 428485,
"minified": 132196,
"gzipped": 42237
"bundled": 463339,
"minified": 143714,
"gzipped": 45341
},
"dist/react-big-calendar.esm.js": {
"bundled": 185554,
"minified": 87500,
"gzipped": 21669,
"bundled": 219605,
"minified": 99669,
"gzipped": 24759,
"treeshaked": {
"rollup": {
"code": 62542,
"import_statements": 1590
"code": 63242,
"import_statements": 1412
},
"webpack": {
"code": 67195
"code": 66708
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion .storybook/rbc.theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ export default create({
inputBorderRadius: 4,

brandTitle: 'Big Calendar',
brandUrl: 'http://intljusticemission.github.io/react-big-calendar/examples',
brandUrl: 'http://jquense.github.io/react-big-calendar/examples',
})
130 changes: 62 additions & 68 deletions CHANGELOG.md

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ element has a height, or the calendar won't be visible. To provide your own cust
## Run examples locally

```sh
$ git clone git@github.com:intljusticemission/react-big-calendar.git
$ git clone git@github.com:jquense/react-big-calendar.git
$ cd react-big-calendar
$ yarn
$ yarn examples
Expand Down Expand Up @@ -89,7 +89,7 @@ import getDay from 'date-fns/getDay'
import enUS from 'date-fns/locale/en-US'

const locales = {
'en-US': enUS
'en-US': enUS,
}

const localizer = dateFnsLocalizer({
Expand Down Expand Up @@ -118,7 +118,6 @@ const MyCalendar = props => (
Out of the box, you can include the compiled CSS files and be up and running. But, sometimes, you may want to style
Big Calendar to match your application styling. For this reason, SASS files are included with Big Calendar.


```
@import 'react-big-calendar/lib/sass/styles';
@import 'react-big-calendar/lib/addons/dragAndDrop/styles'; // if using DnD
Expand Down
14 changes: 12 additions & 2 deletions examples/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import Api from './Api'
import Intro from './Intro.md'
import TimezoneIntro from './Timezones.md'
import { render } from 'react-dom'
import Layout from 'react-tackle-box/Layout'

Expand All @@ -20,6 +21,8 @@ import BackgroundEvents from './demos/backgroundEvents'
import Selectable from './demos/selectable'
import CreateEventWithNoOverlap from './demos/createEventWithNoOverlap'
import Cultures from './demos/cultures'
import Timezones from './demos/timezones'
import Luxon from './demos/luxon'
import Popup from './demos/popup'
import Rendering from './demos/rendering'
import CustomView from './demos/customView'
Expand All @@ -34,13 +37,15 @@ import MenuItem from 'react-bootstrap/lib/MenuItem'
const globalizeLocalizer = localizer(globalize)

let demoRoot =
'https://github.com/intljusticemission/react-big-calendar/tree/master/examples/demos'
'https://github.com/jquense/react-big-calendar/tree/master/examples/demos'

const EXAMPLES = {
basic: 'Basic Calendar',
selectable: 'Create events',
createEventWithNoOverlap: 'Create events with no-overlap algorithm',
timezones: 'Timezones',
cultures: 'Localization',
luxon: 'Luxon Localizer',
popup: 'Show more via a popup',
timeslots: 'Custom Time Grids',
rendering: 'Customized Component Rendering',
Expand Down Expand Up @@ -78,7 +83,9 @@ class Example extends React.Component {
basic: Basic,
backgroundEvents: BackgroundEvents,
selectable: Selectable,
timezones: Timezones,
cultures: Cultures,
luxon: Luxon,
popup: Popup,
rendering: Rendering,
customView: CustomView,
Expand Down Expand Up @@ -109,7 +116,7 @@ class Example extends React.Component {
{' | '}
<a
target="_blank"
href="https://github.com/intljusticemission/react-big-calendar"
href="https://github.com/jquense/react-big-calendar"
>
<i className="fa fa-github" /> github
</a>
Expand Down Expand Up @@ -163,6 +170,9 @@ class Example extends React.Component {
<div className="contain section">
<Intro />
</div>
<div className="contain section">
<TimezoneIntro />
</div>
<Api className="contain section" />
</div>
</div>
Expand Down
8 changes: 4 additions & 4 deletions examples/Intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ _npm:_ `npm install --save react-big-calendar`
`react-big-calendar` is a full featured Calendar component for managing events and dates. It uses modern `flexbox` for layout, making it super responsive and performant. Leaving most of the layout heavy lifting to the browser.

Styles can be found at: `react-big-calendar/lib/css/react-big-calendar.css`, and should be included on the page
with the calendar component. Alternatively, you can include the styles directly in your SASS build. See the [Custom Styling](https://github.com/intljusticemission/react-big-calendar/blob/master/README.md#custom-styling) section of the README file for more details.
with the calendar component. Alternatively, you can include the styles directly in your SASS build. See the [Custom Styling](https://github.com/jquense/react-big-calendar/blob/master/README.md#custom-styling) section of the README file for more details.

Also make sure that your calendar's container element has a height, or the calendar won't be visible (see why below).

Date internationalization and localization is **hard** and `react-big-calendar` doesn't even attempt to
solve that problem. Instead you can use one of the many excellent solutions already
out there, via adapters called "localizers". Big Calendar comes with two localizers for use
with [Globalize.js](https://github.com/jquery/globalize) or [Moment.js](http://momentjs.com/).
out there, via adapters called "localizers". Big Calendar comes with three localizers for use
with [Globalize.js](https://github.com/jquery/globalize), [Moment.js](http://momentjs.com/) or [Luxon](https://moment.github.io/luxon).

Choose the localizer that best suits your needs, or write your own. Whatever you do, you'll need to set it up
before you can use the calendar (you only need to set it up once).
Expand All @@ -25,7 +25,7 @@ before you can use the calendar (you only need to set it up once).
import { Calendar, momentLocalizer } from 'react-big-calendar'
import moment from 'moment'

// Setup the localizer by providing the moment (or globalize) Object
// Setup the localizer by providing the moment (or globalize, or Luxon) Object
// to the correct localizer.
const localizer = momentLocalizer(moment) // or globalizeLocalizer

Expand Down
48 changes: 48 additions & 0 deletions examples/TimezoneSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react'
import PropTypes from 'prop-types'
import Layout from 'react-tackle-box/Layout'
import moment from 'moment'
import 'moment-timezone'

import ExampleControlSlot from './ExampleControlSlot'

const allZones = moment.tz.names()
allZones.unshift('clear')

export default function TimezoneSelect({
title,
defaultTZ = moment.tz.guess(),
timezone,
setTimezone,
}) {
const onChange = ({ target: { value } }) =>
setTimezone(value ? value : defaultTZ)

return (
<ExampleControlSlot.Entry waitForOutlet>
<Layout direction="column" align="center">
{title ? <h4>{title}</h4> : null}
<label>Select a Timezone</label>{' '}
<select
className="form-control"
style={{ width: 200, display: 'inline-block' }}
value={timezone}
onChange={onChange}
>
{allZones.map((c, idx) => (
<option key={idx} value={c !== 'clear' ? c : ''}>
{c}
</option>
))}
</select>
</Layout>
</ExampleControlSlot.Entry>
)
}

TimezoneSelect.propTypes = {
title: PropTypes.string,
defaultTZ: PropTypes.string,
timezone: PropTypes.string,
setTimezone: PropTypes.func,
}
33 changes: 33 additions & 0 deletions examples/Timezones.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# <a id='timezonesIntro' href='#timezoneIntro'>Dealing With Time Zones</a>

Time Zones are... **hard**, and while changing the `culture` will help with internationalization and localization, it does not adjust the dates for a specific time zone. Javascript Date objects don't really support time zone switching natively, and date math gets **very** complicated. Thankfully `react-big-calender` does support `moment` as a localizer, so if you use [moment-timezone](https://momentjs.com/timezone/) you can get your events to display relevant to a time zone other than the browser native.

To change your events to display as a specific time zone, you must [change moment's default timezone](https://momentjs.com/timezone/docs/#/using-timezones/default-timezone/) for all dates, **using an IANA time zone**.

```jsx
import { Calendar, momentLocalizer } from 'react-big-calendar'
import moment from 'moment'
import 'moment-timezone' // or 'moment-timezone/builds/moment-timezone-with-data[-datarange].js'. See their docs

// Set the IANA time zone you want to use
moment.tz.setDefault('Europe/Paris')

// Setup the localizer by providing the moment Object
// to the correct localizer.
const localizer = momentLocalizer(moment) // or globalizeLocalizer

const MyCalendar = props => (
<div>
<Calendar
localizer={localizer}
events={myEventsList}
startAccessor="start"
endAccessor="end"
/>
</div>
)
```

The `momentLocalizer` will now handle all dates and date math as if the date is in the timezone you specified. It is important to note that [changing moment's default timezone](https://momentjs.com/timezone/docs/#/using-timezones/default-timezone/) affects all dates, created by moment, from that point forward, so you may want to reset the default when your component unmounts. And, if switching timezones 'on-the-fly', you want to update your 'localizer' and any Date based props (min, max, getNow, etc) at the same time.

**Note:** The new `luxonLocalizer` operates in a similar fashion. View the 'Luxon Localizer' demo and view it's source for an example of it's usage.
Loading

0 comments on commit ad8defa

Please sign in to comment.