jquense / react-big-calendar

gcal/outlook like calendar component
http://jquense.github.io/react-big-calendar/examples/index.html
MIT License
7.82k stars 2.23k forks source link

Wrong range of dates on WorkWeek view when using Timezones #2524

Open rodrigolungui opened 7 months ago

rodrigolungui commented 7 months ago

Check that this is really a bug

Reproduction link

https://codesandbox.io/p/sandbox/react-big-calendar-example-forked-nx9wqj

Bug description

I'm facing some problems when using a different timezone than local with the WorkWeek view. Sometimes, depending on the timezone selected, the work week range gets Tuesday to Saturday rather than Monday to Friday. The usual week view works appropriately, though (Sunday to Saturday).

I was able to reproduce it within RBC storybook, and dug around it, but I'm still unsure if it's indeed a problem and the best way to solve it.

https://github.com/jquense/react-big-calendar/assets/2953678/c84467cc-9a7d-4917-b447-48ff1e54acff

Expected Behavior

Wrong range of dates on WorkWeek view when using a different timezone. In my case, the problem starts happening from GMT-1 to the east.

Actual Behavior

The range dates on the WorkWeek view should always be from Monday to Friday.

react-big-calendar version

latest

React version

^17.0.2

Platform/Target and Browser Versions

macOS

Validations

Would you like to open a PR for this bug?

cutterbl commented 7 months ago

@rodrigolungui You bring up a very interesting bug. I'm not overly familiar with the WorkWeek code, but I do wonder if it's because in certain timezones a 'week' starts on Monday instead of Sunday? Thanks for pointing this out. Hopefully someone has some bandwidth to dive into it soon (I'm a little swamped right now).

rodrigolungui commented 7 months ago

Hey @cutterbl! Thanks for the quick response! As far as I understand, this is the code responsible for getting the date range for the workweek's view:

https://github.com/jquense/react-big-calendar/blob/master/src/WorkWeek.js#L7-L11

function workWeekRange(date, options) {
  return Week.range(date, options).filter(
    (d) => [6, 0].indexOf(d.getDay()) === -1
  )
}

And I think the problem happens because the Date.prototype.getDay() gives us the weekday according to the local timezone. Ref

I played a little around it and was able to fix the problem using the DateTime(date).weekday from the Luxon library. This change seems to fix the problem:

function workWeekRange(date, options) {
  const weekRange = Week.range(date, options)

  const SUNDAY = 7;
  const SATURDAY = 6;

  return weekRange.filter(
    (day) =>
      DateTime.fromJSDate(day).weekday !== SUNDAY ||
      DateTime.fromJSDate(day).weekday !== SATURDAY
  )
}

I'm not sure, but it seems this piece of code should be agnostic to frameworks, so I'm thinking of a way to use the localizers to get it. 🤔

cutterbl commented 7 months ago

@rodrigolungui It does, but unfortunately only for those using Luxon. We would require something localizer agnostic.

rodrigolungui commented 7 months ago

Yeah, I know... It's just unclear what's going on and what would be a good solution in this case.

As we are using the Luxon localizer in my project, I solved it by overwriting the WorkWeek view. In case it's something useful for someone else, here's the code:


import React, { useMemo } from 'react';
import Week from 'react-big-calendar/lib/Week';
import TimeGrid from 'react-big-calendar/lib/TimeGrid';
import { DateTime } from 'luxon';
import { TitleOptions } from 'react-big-calendar';

function workWeekRange(date: Date, options) {
    const weekRange = Week.range(date, options);

    const SUNDAY = 7;
    const SATURDAY = 6;

    return weekRange.filter((day: Date) => {
        return DateTime.fromJSDate(day).weekday !== SUNDAY && DateTime.fromJSDate(day).weekday !== SATURDAY;
    });
}

export function CustomWeekView({
    date,
    localizer,
    max = localizer.endOf(new Date(), 'day'),
    min = localizer.startOf(new Date(), 'day'),
    scrollToTime = localizer.startOf(new Date(), 'day'),
    ...props
}) {
    const currRange = useMemo(() => CustomWeekView.range(date, { localizer }), [date, localizer]);

    return (
        <TimeGrid
            date={date}
            eventOffset={15}
            localizer={localizer}
            max={max}
            min={min}
            range={currRange}
            scrollToTime={scrollToTime}
            {...props}
        />
    );
}

CustomWeekView.range = workWeekRange;
CustomWeekView.navigate = Week.navigate;
CustomWeekView.title = (date: Date, { localizer }: TitleOptions) => {
    const [start, ...rest] = workWeekRange(date, { localizer });
    return localizer.format({ start, end: rest.pop() }, 'dayRangeHeaderFormat');
};
Zumega commented 6 months ago

I too have come across this bug when changing the timezone and using Work_Week; in my case I'm using MomentJs. This issue only happens when changing to a past timezone; example: from America/Los_Angeles (-7/-8 offsets) to America/New_York (-4/-5 offsets)