From c13bc7374afef9b828340641053509cbfc5b200f Mon Sep 17 00:00:00 2001 From: Joseph Mathew Date: Thu, 3 Oct 2024 10:42:43 +0530 Subject: [PATCH 1/3] Fixed datepicker timezone issue --- src/components/DatePicker/index.jsx | 16 +++++-- src/components/TimePicker/index.jsx | 3 +- src/utils/index.js | 51 ++++++++++++++++++++--- stories/Components/DatePicker.stories.jsx | 3 +- stories/Components/TimePicker.stories.jsx | 3 +- tests/DatePicker.test.jsx | 2 +- tests/TimePicker.test.jsx | 2 +- 7 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/components/DatePicker/index.jsx b/src/components/DatePicker/index.jsx index 5cd036f52..6d4591fc9 100644 --- a/src/components/DatePicker/index.jsx +++ b/src/components/DatePicker/index.jsx @@ -2,7 +2,6 @@ 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"; @@ -10,7 +9,13 @@ 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"; @@ -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)); diff --git a/src/components/TimePicker/index.jsx b/src/components/TimePicker/index.jsx index d4c7e6199..fc6855781 100644 --- a/src/components/TimePicker/index.jsx +++ b/src/components/TimePicker/index.jsx @@ -16,6 +16,7 @@ import { hyphenize, ANT_DESIGN_GLOBAL_TOKEN_OVERRIDES, getLocale, + getTimezoneAppliedDateTime, } from "utils"; import { TIME_PICKER_TYPES } from "./constants"; @@ -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 => ( diff --git a/src/utils/index.js b/src/utils/index.js index 219d12f35..756e70da3 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -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"; @@ -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); @@ -31,6 +33,45 @@ 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 (args.length > 0 && typeof args[0] === "string") { + if (hasTimezone(args[0])) { + args[0] = pureDayjs(args[0]); + } else { + const pureDayjsArgs = args.slice(0, Math.min(args.length, 2)); + + args[0] = pureDayjs(...pureDayjsArgs).format("YYYY-MM-DD HH:mm:ss"); + args[1] = "YYYY-MM-DD HH:mm:ss"; + } + } + + 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 => { diff --git a/stories/Components/DatePicker.stories.jsx b/stories/Components/DatePicker.stories.jsx index 9cc8ca439..87c423fd1 100644 --- a/stories/Components/DatePicker.stories.jsx +++ b/stories/Components/DatePicker.stories.jsx @@ -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"; diff --git a/stories/Components/TimePicker.stories.jsx b/stories/Components/TimePicker.stories.jsx index ccf89b2e4..76ca75601 100644 --- a/stories/Components/TimePicker.stories.jsx +++ b/stories/Components/TimePicker.stories.jsx @@ -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"; diff --git a/tests/DatePicker.test.jsx b/tests/DatePicker.test.jsx index 80ba893ce..890023dba 100644 --- a/tests/DatePicker.test.jsx +++ b/tests/DatePicker.test.jsx @@ -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)); diff --git a/tests/TimePicker.test.jsx b/tests/TimePicker.test.jsx index 94e061501..38393349e 100644 --- a/tests/TimePicker.test.jsx +++ b/tests/TimePicker.test.jsx @@ -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; From e5a2ee0b71bcadd595478d641007d251a3205544 Mon Sep 17 00:00:00 2001 From: Joseph Mathew Date: Sun, 6 Oct 2024 23:20:47 +0530 Subject: [PATCH 2/3] Handled invalid date edge cases --- src/utils/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/utils/index.js b/src/utils/index.js index 756e70da3..4269db32b 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -42,16 +42,18 @@ const hasTimezone = dateString => { // eslint-disable-next-line import/exports-last export const dayjs = (...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(args[0]); + args[0] = pureDayjs(...pureDayjsArgs); } else { - const pureDayjsArgs = args.slice(0, Math.min(args.length, 2)); - 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 From 994e89d78c221d958e167988b97ee9f90f1c4dad Mon Sep 17 00:00:00 2001 From: Joseph Mathew Date: Sun, 6 Oct 2024 23:54:29 +0530 Subject: [PATCH 3/3] Fixed jest test --- 0 | 0 src/utils/index.js | 7 +++++++ 2 files changed, 7 insertions(+) create mode 100644 0 diff --git a/0 b/0 new file mode 100644 index 000000000..e69de29bb diff --git a/src/utils/index.js b/src/utils/index.js index 4269db32b..2d85b22a1 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -41,6 +41,13 @@ const hasTimezone = 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));