Skip to content

Commit

Permalink
Fixed datepicker timezone issue (#2325)
Browse files Browse the repository at this point in the history
* Fixed datepicker timezone issue

* Handled invalid date edge cases

* Fixed jest test
  • Loading branch information
josephmathew900 authored Oct 7, 2024
1 parent 4c7b5de commit 4186aac
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 15 deletions.
Empty file added 0
Empty file.
16 changes: 13 additions & 3 deletions src/components/DatePicker/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ import React, { forwardRef, useState, useEffect } from "react";

import { DatePicker as AntDatePicker } from "antd";
import classnames from "classnames";
import dayjs from "dayjs";
import { isNotPresent } from "neetocist";
import { Left, Right, Calendar, Close } from "neetoicons";
import PropTypes from "prop-types";

import { Tag } from "components";
import Label from "components/Label";
import { useSyncedRef, useId } from "hooks";
import { convertToDayjsObjects, noop, hyphenize } from "utils";
import {
convertToDayjsObjects,
noop,
hyphenize,
dayjs,
getTimezoneAppliedDateTime,
} from "utils";

import IconOverride from "./IconOverride";
import Provider from "./Provider";
Expand Down Expand Up @@ -77,7 +82,12 @@ const DatePicker = forwardRef(
if (type === "range" && isNotPresent(date)) {
return onChange([], dateString);
}
const allowed = getAllowed(date, minDate, maxDate);

const allowed = getAllowed(
getTimezoneAppliedDateTime(date),
minDate,
maxDate
);
setValue(allowed);

return onChange(allowed, formattedString(allowed, dateFormat));
Expand Down
3 changes: 2 additions & 1 deletion src/components/TimePicker/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
hyphenize,
ANT_DESIGN_GLOBAL_TOKEN_OVERRIDES,
getLocale,
getTimezoneAppliedDateTime,
} from "utils";

import { TIME_PICKER_TYPES } from "./constants";
Expand Down Expand Up @@ -77,7 +78,7 @@ const TimePicker = forwardRef(
const handleOnChange = (time, timeString) => {
type === "range" && !time
? onChange([], timeString)
: onChange(time, timeString);
: onChange(getTimezoneAppliedDateTime(time), timeString);
};

const panelRender = originalPanel => (
Expand Down
60 changes: 55 additions & 5 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import dayjs from "dayjs";
import pureDayjs from "dayjs";
import localeData from "dayjs/plugin/localeData";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
import weekday from "dayjs/plugin/weekday";
import weekOfYear from "dayjs/plugin/weekOfYear";
Expand All @@ -10,10 +11,11 @@ import { complement, equals, isEmpty, omit, pipe, toPairs } from "ramda";
// eslint-disable-next-line import/extensions
import en from "src/translations/en.json";

dayjs.extend(weekOfYear);
dayjs.extend(weekday);
dayjs.extend(localeData);
dayjs.extend(utc);
pureDayjs.extend(weekOfYear);
pureDayjs.extend(weekday);
pureDayjs.extend(localeData);
pureDayjs.extend(utc);
pureDayjs.extend(timezone);

const getEnTranslationValue = translationKey =>
translationKey.split(".").reduce((acc, key) => acc[key], en);
Expand All @@ -31,6 +33,54 @@ const getScrollbarWidth = () => {
return scrollbarWidth;
};

const hasTimezone = dateString => {
const timezoneRegex = /Z|[+-]\d{2}:\d{2}$|GMT([+-]\d{4})?$/;

return timezoneRegex.test(dateString);
};

// eslint-disable-next-line import/exports-last
export const dayjs = (...args) => {
if (
pureDayjs.tz().$x.$timezone === pureDayjs.tz.guess() ||
pureDayjs.tz().$x.$timezone === undefined
) {
return pureDayjs(...args);
}

if (args.length > 0 && typeof args[0] === "string") {
const pureDayjsArgs = args.slice(0, Math.min(args.length, 2));

if (hasTimezone(args[0])) {
args[0] = pureDayjs(...pureDayjsArgs);
} else {
args[0] = pureDayjs(...pureDayjsArgs).format("YYYY-MM-DD HH:mm:ss");
args[1] = "YYYY-MM-DD HH:mm:ss";
}
}

if (args[0]?.toString() === "Invalid Date") return pureDayjs(...args);

const timezone = pureDayjs.tz().$x.$timezone || pureDayjs.tz.guess();

return args.length === 2
? pureDayjs.tz(...args, timezone)
: pureDayjs.tz(...args);
};

Object.assign(dayjs, { ...pureDayjs });

export const getTimezoneAppliedDateTime = inputDateTime => {
if (!inputDateTime) return null;

const timezoneAppliedDateTime = date =>
dayjs(date.format("YYYY-MM-DD HH:mm:ss"));

return Array.isArray(inputDateTime)
? inputDateTime.map(timezoneAppliedDateTime)
: timezoneAppliedDateTime(inputDateTime);
};

export const noop = () => {};

export const hyphenize = input => {
Expand Down
3 changes: 1 addition & 2 deletions stories/Components/DatePicker.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from "react";

import dayjs from "dayjs";

import { Modal, Typography, Pane, DatePicker } from "components";
import Button from "components/Button";
import { dayjs } from "utils";

import { disabledDateTime } from "./constants";

Expand Down
3 changes: 1 addition & 2 deletions stories/Components/TimePicker.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from "react";

import dayjs from "dayjs";

import { Modal, Typography, Pane } from "components";
import Button from "components/Button";
import TimePicker from "components/TimePicker";
import { dayjs } from "utils";

import { disabledDateTime } from "./constants";

Expand Down
2 changes: 1 addition & 1 deletion tests/DatePicker.test.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";

import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import dayjs from "dayjs";

import DatePicker from "components/DatePicker";
import { dayjs } from "utils";

const today = dayjs();
const theDate = dayjs(new Date(1999, 7, 16));
Expand Down
2 changes: 1 addition & 1 deletion tests/TimePicker.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React from "react";

import { screen, render, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import dayjs from "dayjs";

import { TimePicker } from "components";
import { dayjs } from "utils";

const currentTime = dayjs();
const { getByRole, getByText, getAllByText, getAllByRole } = screen;
Expand Down

0 comments on commit 4186aac

Please sign in to comment.