From 6f6d503546693c62c653dd56021c6d7270ccdbcd Mon Sep 17 00:00:00 2001 From: hiker90 Date: Mon, 17 Oct 2022 18:13:08 +0800 Subject: [PATCH] feat: support timezone setting (#2102) Co-authored-by: YanHui --- package.json | 2 ++ src/date-picker/Picker.tsx | 4 +-- src/date-picker/demos/DatePicker.stories.tsx | 6 ++-- src/date-range-picker/Picker.tsx | 4 +-- src/legacy/date-picker/DatePicker.stories.tsx | 11 +++--- src/legacy/date-picker/hook/useDatePicker.tsx | 5 +-- .../date-picker/hook/useDateRangePicker.tsx | 7 ++-- .../components/DateAttrSelect/index.tsx | 35 ++++++++++--------- .../Expression/FilterCondition/utils/index.ts | 22 ++++++------ src/legacy/time-picker/Panel.tsx | 7 ++-- src/legacy/time-picker/TimePicker.tsx | 5 +-- src/legacy/time-picker/interface.ts | 2 +- src/past-time-picker/PastTimePicker.tsx | 10 +++--- .../SinceRangePicker.tsx | 5 +-- src/time-picker/TimePicker.tsx | 4 +-- src/utils/timeHelper.ts | 18 ++++++++++ yarn.lock | 17 +++++++++ 17 files changed, 104 insertions(+), 60 deletions(-) create mode 100644 src/utils/timeHelper.ts diff --git a/package.json b/package.json index 14e6bc8253..b505461893 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,11 @@ "@types/react-resizable": "^1.7.3", "classnames": "^2.2.6", "date-fns": "^2.21.3", + "date-fns-tz": "^1.3.7", "immutability-helper": "^3.1.1", "lodash": "^4.17.14", "moment": "^2.26.0", + "moment-timezone": "^0.5.38", "pinyin-match": "^1.2.2", "raf": "^3.4.1", "rc-animate": "^3.1.0", diff --git a/src/date-picker/Picker.tsx b/src/date-picker/Picker.tsx index f07d8c920c..243b0fccdd 100644 --- a/src/date-picker/Picker.tsx +++ b/src/date-picker/Picker.tsx @@ -1,8 +1,8 @@ import React from 'react'; import classnames from 'classnames'; import { usePrefixCls, useControlledState } from '@gio-design/utils'; -import { format } from 'date-fns/fp'; import { CalendarOutlined } from '@gio-design/icons'; +import { parseFnsTimeZone } from '../utils/timeHelper' import Popover from '../popover'; import { InputButton } from '../input'; import StaticDatePicker from '../static-date-picker'; @@ -38,7 +38,7 @@ export const DatePicker: React.FC = (props: DatePickerProps) => const [controlledValue, setControlledValue] = useControlledState(value, defaultValue); - const formatDate = (date: Date) => format(formatString, date); + const formatDate = (date: Date) => parseFnsTimeZone(date, formatString); const handleVisibleChange = (current: boolean) => { setVisible(current); diff --git a/src/date-picker/demos/DatePicker.stories.tsx b/src/date-picker/demos/DatePicker.stories.tsx index 661f958e5b..de796e6d04 100644 --- a/src/date-picker/demos/DatePicker.stories.tsx +++ b/src/date-picker/demos/DatePicker.stories.tsx @@ -3,7 +3,7 @@ import { Meta, Story } from '@storybook/react/types-6-0'; import { withDesign } from 'storybook-addon-designs'; import { DownFilled } from '@gio-design/icons'; import { addMonths, isBefore, startOfToday } from 'date-fns'; -import { format } from 'date-fns/fp'; +import { parseFnsTimeZone } from '../../utils/timeHelper' import Docs from './DatePickerPage'; import Button from '../../button'; import Toast from '../../toast'; @@ -84,9 +84,9 @@ export const ControlledStatic: Story = () => { const [value, setValue] = useState(new Date(2022, 4, 0)); const [pickerMode, setMode] = useState('date'); return (
-

pickerMode:{pickerMode},current value: {format('yyyy/MM/dd HH:MM:SS', value)}

+

pickerMode:{pickerMode},current value: {parseFnsTimeZone(value, 'yyyy/MM/dd HH:MM:SS')}

setValue(v)} viewDate={value} onPanelChange={(val, mode) => { setMode(mode); // setValue(val); }} />
) -}; \ No newline at end of file +}; diff --git a/src/date-range-picker/Picker.tsx b/src/date-range-picker/Picker.tsx index ec456c9c9c..ff0b194e61 100644 --- a/src/date-range-picker/Picker.tsx +++ b/src/date-range-picker/Picker.tsx @@ -1,15 +1,15 @@ import React from 'react'; import classnames from 'classnames'; import { usePrefixCls, useControlledState } from '@gio-design/utils'; -import format from 'date-fns/format'; import { CalendarOutlined } from '@gio-design/icons'; +import { parseFnsTimeZone } from '../utils/timeHelper' import Popover from '../popover'; import { InputButton } from '../input'; import StaticDateRangePicker from '../static-date-range-picker'; import { DateRangePickerProps, NullableDate, NullableString } from './interfaces'; export const formatDates = (dates: [NullableDate, NullableDate], formatString = 'yyyy/MM/dd'): NullableString => { - const strongFormat = (date: NullableDate) => (date ? format(date, formatString) : undefined); + const strongFormat = (date: NullableDate) => (date ? parseFnsTimeZone(date, formatString) : undefined); return `${strongFormat(dates[0]) || ''} - ${strongFormat(dates[1]) || ''}`; }; diff --git a/src/legacy/date-picker/DatePicker.stories.tsx b/src/legacy/date-picker/DatePicker.stories.tsx index 09ac1d3915..9d3b395db5 100644 --- a/src/legacy/date-picker/DatePicker.stories.tsx +++ b/src/legacy/date-picker/DatePicker.stories.tsx @@ -1,7 +1,8 @@ import React, { useState } from 'react'; import { Story, Meta } from '@storybook/react/types-6-0'; -import moment, { Moment } from 'moment'; +import { Moment } from 'moment'; import { withDesign } from 'storybook-addon-designs'; +import { parseTimeZone } from '../../utils/timeHelper'; import DatePicker, { DateRangePicker, DatePickerProps, DateRangePickerProps } from '.'; import './style'; import Docs from './DatePicker.mdx'; @@ -24,13 +25,13 @@ export default { } as Meta; const Template: Story = (args) => { - const [time, setTime] = useState(moment(new Date())); + const [time, setTime] = useState(parseTimeZone(new Date())); const onChange = (value: Moment | null) => { value && setTime(value); }; const { disabledDate } = args; const handleDisabledDate = (value: Moment) => { - const date = moment(new Date()).add(-1, 'days'); + const date = parseTimeZone(new Date()).add(-1, 'days'); return value.isBefore(date); }; return ( @@ -46,12 +47,12 @@ Default.args = { }; function disabledDates(current: Moment) { - return current && current < moment().endOf('day'); + return current && current < parseTimeZone().endOf('day'); } const RangeTemplate: Story = (args) => { const now = new Date(); - const [time, setTime] = useState([moment(now), moment(now)]); + const [time, setTime] = useState([parseTimeZone(now), parseTimeZone(now)]); const onChange = (value: Array | null) => { value && setTime(value); }; diff --git a/src/legacy/date-picker/hook/useDatePicker.tsx b/src/legacy/date-picker/hook/useDatePicker.tsx index c19d332d28..24abe11d88 100644 --- a/src/legacy/date-picker/hook/useDatePicker.tsx +++ b/src/legacy/date-picker/hook/useDatePicker.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; -import moment, { Moment } from 'moment'; +import { Moment } from 'moment'; +import { parseTimeZone } from '../../../utils/timeHelper'; import { DatePickerProps } from '../interface'; const useDatePicker = (props: DatePickerProps) => { @@ -39,7 +40,7 @@ const useDatePicker = (props: DatePickerProps) => { }; const debounceTimeChange = (e: string): void => { - const values = moment(e, props.format); + const values = parseTimeZone(e, props.format); setLocalValue(values.isValid() ? values : localValue); }; diff --git a/src/legacy/date-picker/hook/useDateRangePicker.tsx b/src/legacy/date-picker/hook/useDateRangePicker.tsx index e7d7983888..2f495581b5 100644 --- a/src/legacy/date-picker/hook/useDateRangePicker.tsx +++ b/src/legacy/date-picker/hook/useDateRangePicker.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useRef } from 'react'; -import moment, { Moment } from 'moment'; +import { Moment } from 'moment'; +import { parseTimeZone } from '../../../utils/timeHelper'; import { DateRangePickerProps } from '../interface'; const useDateRangePicker = (props: DateRangePickerProps) => { @@ -38,13 +39,13 @@ const useDateRangePicker = (props: DateRangePickerProps) => { const handleLeftInputChange = (e: React.ChangeEvent): void => { setLeftInputTimeRange(e.target.value); - const values = moment(e.target.value, props.format); + const values = parseTimeZone(e.target.value, props.format); debounceInputChange(values, 'left'); }; const handleRightInputChange = (e: React.ChangeEvent): void => { setRightInputTimeRange(e.target.value); - const values = moment(e.target.value, props.format); + const values = parseTimeZone(e.target.value, props.format); debounceInputChange(values, 'right'); }; diff --git a/src/legacy/filter-picker/components/FilterList/Expression/FilterCondition/components/DateAttrSelect/index.tsx b/src/legacy/filter-picker/components/FilterList/Expression/FilterCondition/components/DateAttrSelect/index.tsx index 399a83b99a..62474b7bdf 100644 --- a/src/legacy/filter-picker/components/FilterList/Expression/FilterCondition/components/DateAttrSelect/index.tsx +++ b/src/legacy/filter-picker/components/FilterList/Expression/FilterCondition/components/DateAttrSelect/index.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react'; -import moment, { Moment } from 'moment'; +import { Moment } from 'moment'; +import { parseTimeZone } from '../../../../../../../../utils/timeHelper'; import RelativeCurrent from './components/RelativeCurrent'; import RelativeBetween from './components/RelativeBetween'; import IncludeToday from './components/IncludeToday'; @@ -16,30 +17,30 @@ interface DateAttrSelectProps { function DateAttrSelect(props: DateAttrSelectProps) { const { attrSelect, attrChange, values, style } = props; const [time, setTime] = useState( - values?.[0] && parseFloat(values?.[0]).toString() !== 'NaN' ? moment(parseInt(values?.[0], 10)) : moment(Date.now()) + values?.[0] && parseFloat(values?.[0]).toString() !== 'NaN' ? parseTimeZone(parseInt(values?.[0], 10)) : parseTimeZone(Date.now()) ); const [timeRange, setTimeRange] = useState( values.length && values?.[0]?.includes?.('abs') ? [ - moment(parseInt(values?.[0].split(':')[1].split(',')[0], 10)), - moment(parseInt(values?.[0].split(':')[1].split(',')[1], 10)), + parseTimeZone(parseInt(values?.[0].split(':')[1].split(',')[0], 10)), + parseTimeZone(parseInt(values?.[0].split(':')[1].split(',')[1], 10)), ] - : [moment(Date.now()), moment(Date.now())] + : [parseTimeZone(Date.now()), parseTimeZone(Date.now())] ); useEffect(() => { setTime( values?.[0] && parseFloat(values?.[0]).toString() !== 'NaN' - ? moment(parseInt(values?.[0], 10)) - : moment(Date.now()) + ? parseTimeZone(parseInt(values?.[0], 10)) + : parseTimeZone(Date.now()) ); setTimeRange( values.length && values?.[0]?.includes?.('abs') ? [ - moment(parseInt(values?.[0].split(':')[1].split(',')[0], 10)), - moment(parseInt(values?.[0].split(':')[1].split(',')[1], 10)), + parseTimeZone(parseInt(values?.[0].split(':')[1].split(',')[0], 10)), + parseTimeZone(parseInt(values?.[0].split(':')[1].split(',')[1], 10)), ] - : [moment(Date.now()), moment(Date.now())] + : [parseTimeZone(Date.now()), parseTimeZone(Date.now())] ); }, [values]); @@ -75,32 +76,32 @@ function DateAttrSelect(props: DateAttrSelectProps) { } else if (attrSelect === 'between' || attrSelect === 'not between') { // 在。。。与。。。之间,值的初始化 attrChange([ - `abs:${moment(timeRange?.[0], 'YYYY-MM-DD').startOf('day').valueOf()},${moment(timeRange?.[1], 'YYYY-MM-DD') + `abs:${parseTimeZone(timeRange?.[0], 'YYYY-MM-DD').startOf('day').valueOf()},${parseTimeZone(timeRange?.[1], 'YYYY-MM-DD') .endOf('day') .valueOf()}`, ]); } else { - attrChange([`${moment(Date.now()).valueOf()}`]); + attrChange([`${parseTimeZone(Date.now()).valueOf()}`]); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [attrSelect]); const changeDate = (value: Date | null) => { - const date = moment(value); + const date = parseTimeZone(value); date && setTime(date); - attrChange([`${moment(date, 'YYYY-MM-DD').startOf('day').valueOf()}`]); + attrChange([`${parseTimeZone(date, 'YYYY-MM-DD').startOf('day').valueOf()}`]); }; const relativeDateChange = (v: string) => { attrChange([v]); }; const dateRangeChange = (value?: [NullableDate, NullableDate]) => { - if (!value || value.some((item) => !moment(item).isValid())) return; - const dateRange = [moment(value[0]), moment(value[1])]; + if (!value || value.some((item) => !parseTimeZone(item).isValid())) return; + const dateRange = [parseTimeZone(value[0]), parseTimeZone(value[1])]; dateRange && setTimeRange(dateRange); dateRange && attrChange([ - `abs:${moment(dateRange?.[0], 'YYYY-MM-DD').startOf('day').valueOf()},${moment(dateRange?.[1], 'YYYY-MM-DD') + `abs:${parseTimeZone(dateRange?.[0], 'YYYY-MM-DD').startOf('day').valueOf()},${parseTimeZone(dateRange?.[1], 'YYYY-MM-DD') .endOf('day') .valueOf()}`, ]); diff --git a/src/legacy/filter-picker/components/FilterList/Expression/FilterCondition/utils/index.ts b/src/legacy/filter-picker/components/FilterList/Expression/FilterCondition/utils/index.ts index 3b75f9add7..2dab4e273a 100644 --- a/src/legacy/filter-picker/components/FilterList/Expression/FilterCondition/utils/index.ts +++ b/src/legacy/filter-picker/components/FilterList/Expression/FilterCondition/utils/index.ts @@ -1,4 +1,4 @@ -import moment from 'moment'; +import { parseTimeZone } from '../../../../../../../utils/timeHelper'; import { listFormat } from '../../../../..'; import { TextObject } from '../../../../../FilterPicker'; import { attributeValue } from '../interfaces'; @@ -151,26 +151,26 @@ const parseDateValuesToText = (operation: string, value: string[], t: TextObject if (value[0] === ' ') { return t.hasValue; } - return `${t['!=']} ${moment(parseInt(value[0], 10)).format('YYYY-MM-DD')}`; + return `${t['!=']} ${parseTimeZone(parseInt(value[0], 10)).format('YYYY-MM-DD')}`; } case '=': { if (value[0] === ' ') { return t.noValue; } - return `${t['=']} ${moment(parseInt(value[0], 10)).format('YYYY-MM-DD')}`; + return `${t['=']} ${parseTimeZone(parseInt(value[0], 10)).format('YYYY-MM-DD')}`; } case '>': // 在 xxx 天之后 - return t.somedayAfter(moment(parseInt(value[0], 10)).format('YYYY-MM-DD')); + return t.somedayAfter(parseTimeZone(parseInt(value[0], 10)).format('YYYY-MM-DD')); case '>=': // 在 xxx 天之后包括当天 - return t.somedayAfterInclude(moment(parseInt(value[0], 10)).format('YYYY-MM-DD')); + return t.somedayAfterInclude(parseTimeZone(parseInt(value[0], 10)).format('YYYY-MM-DD')); case '<': // 在 xxx 天之前 - return t.somedayAgo(moment(parseInt(value[0], 10)).format('YYYY-MM-DD')); + return t.somedayAgo(parseTimeZone(parseInt(value[0], 10)).format('YYYY-MM-DD')); // 在 xxx 天之前包括当天 case '<=': - return t.somedayAgoInclude(moment(parseInt(value[0], 10)).format('YYYY-MM-DD')); + return t.somedayAgoInclude(parseTimeZone(parseInt(value[0], 10)).format('YYYY-MM-DD')); // 判断,在。。。与。。。之间 // 返回字符串 ---- ‘在(date1)与(data2)之间’ case 'between': @@ -178,7 +178,7 @@ const parseDateValuesToText = (operation: string, value: string[], t: TextObject const abs = value?.[0].split(':')[1].split(','); return t.between( listFormat( - [moment(parseInt(abs[0], 10)).format('YYYY-MM-DD'), moment(parseInt(abs[1], 10)).format('YYYY-MM-DD')], + [parseTimeZone(parseInt(abs[0], 10)).format('YYYY-MM-DD'), parseTimeZone(parseInt(abs[1], 10)).format('YYYY-MM-DD')], t.code ) ); @@ -189,7 +189,7 @@ const parseDateValuesToText = (operation: string, value: string[], t: TextObject const abs = value?.[0].split(':')[1].split(','); return t.notBetween( listFormat( - [moment(parseInt(abs[0], 10)).format('YYYY-MM-DD'), moment(parseInt(abs[1], 10)).format('YYYY-MM-DD')], + [parseTimeZone(parseInt(abs[0], 10)).format('YYYY-MM-DD'), parseTimeZone(parseInt(abs[1], 10)).format('YYYY-MM-DD')], t.code ) ); @@ -204,9 +204,9 @@ const parseDateValuesToText = (operation: string, value: string[], t: TextObject return parseDateValuesRelativeToText(relativeTime, t); } case 'hasValue': - return `${t.hasValue} ${moment(value[0]).format('YYYY-MM-DD')}`; + return `${t.hasValue} ${parseTimeZone(value[0]).format('YYYY-MM-DD')}`; default: - return moment(value[0]).format('YYYY-MM-DD'); + return parseTimeZone(value[0]).format('YYYY-MM-DD'); } }; diff --git a/src/legacy/time-picker/Panel.tsx b/src/legacy/time-picker/Panel.tsx index cca5f11363..9ee106e09c 100644 --- a/src/legacy/time-picker/Panel.tsx +++ b/src/legacy/time-picker/Panel.tsx @@ -1,6 +1,7 @@ import React, { Component } from 'react'; -import moment, { Moment } from 'moment'; +import { Moment } from 'moment'; import classNames from 'classnames'; +import { parseTimeZone } from '../../utils/timeHelper'; // import Header from './Header'; import Combobox from './Combobox'; import { PanelProps } from './interface'; @@ -20,7 +21,7 @@ function toNearestValidTime(time: Moment, hourOptions: number[], minuteOptions: const hour = hourOptions.slice().sort((a, b) => Math.abs(time.hour() - a) - Math.abs(time.hour() - b))[0]; const minute = minuteOptions.slice().sort((a, b) => Math.abs(time.minute() - a) - Math.abs(time.minute() - b))[0]; const second = secondOptions.slice().sort((a, b) => Math.abs(time.second() - a) - Math.abs(time.second() - b))[0]; - return moment(`${hour}:${minute}:${second}`, 'HH:mm:ss'); + return parseTimeZone(`${hour}:${minute}:${second}`, 'HH:mm:ss'); } interface PanelState { value: Moment; @@ -33,7 +34,7 @@ class Panel extends Component { disabledHours: noop, disabledMinutes: noop, disabledSeconds: noop, - defaultOpenValue: moment(), + defaultOpenValue: parseTimeZone() as Moment, use12Hours: false, addon: noop, onKeyDown: noop, diff --git a/src/legacy/time-picker/TimePicker.tsx b/src/legacy/time-picker/TimePicker.tsx index 3add9db0f8..8b70d70196 100644 --- a/src/legacy/time-picker/TimePicker.tsx +++ b/src/legacy/time-picker/TimePicker.tsx @@ -1,7 +1,8 @@ import React, { Component, ReactNode } from 'react'; import Trigger from 'rc-trigger'; -import moment, { Moment } from 'moment'; +import { Moment } from 'moment'; import classNames from 'classnames'; +import { parseTimeZone } from '../../utils/timeHelper'; import Panel from './Panel'; import placements, { defaultAlign } from './placements'; import { TimePickerProps } from './interface'; @@ -31,7 +32,7 @@ class Picker extends Component { popupClassName: '', popupStyle: {}, align: defaultAlign, - defaultOpenValue: moment(), + defaultOpenValue: parseTimeZone() as Moment, allowEmpty: true, showHour: true, showMinute: true, diff --git a/src/legacy/time-picker/interface.ts b/src/legacy/time-picker/interface.ts index 1283c9ad4f..6a3e3431b2 100644 --- a/src/legacy/time-picker/interface.ts +++ b/src/legacy/time-picker/interface.ts @@ -27,7 +27,7 @@ export interface TimePickerProps { defaultValue?: Moment; /** * defaultOpenValue - * @default moment() + * @default parseTimeZone() */ defaultOpenValue?: Moment; defaultOpen: boolean; diff --git a/src/past-time-picker/PastTimePicker.tsx b/src/past-time-picker/PastTimePicker.tsx index 6267cb7e59..081073a4cd 100644 --- a/src/past-time-picker/PastTimePicker.tsx +++ b/src/past-time-picker/PastTimePicker.tsx @@ -5,7 +5,7 @@ import classnames from 'classnames'; import has from 'lodash/has'; import get from 'lodash/get'; import omit from 'lodash/omit'; -import { format } from 'date-fns'; +import { parseFnsTimeZone } from '../utils/timeHelper' import Popover from '../popover'; import { InputButton } from '../input'; import { PastTimePickerProps } from './interfaces'; @@ -115,17 +115,17 @@ const PastTimePicker = (props: PastTimePickerProps) => { const items = time.split(':'); const times = items[1].split(',').map((str) => parseInt(str, 10)); if (items[0] === 'since') { - const start = format(times[0], 'yyyy/MM/dd'); + const start = parseFnsTimeZone(times[0], 'yyyy/MM/dd'); return `${FromText} ${start} ${toTodayText}`; } if (items[0] === 'since-lt-today') { - const start = format(times[0], 'yyyy/MM/dd'); + const start = parseFnsTimeZone(times[0], 'yyyy/MM/dd'); return `${FromText} ${start} ${toYesterdayText}`; } if (items[0] === 'abs') { - const start = format(times[0], 'yyyy/MM/dd'); - const end = format(times[1], 'yyyy/MM/dd'); + const start = parseFnsTimeZone(times[0], 'yyyy/MM/dd'); + const end = parseFnsTimeZone(times[1], 'yyyy/MM/dd'); return `${FromText} ${start} ${ToText} ${end}`; } if (items[0] === 'day') { diff --git a/src/static-past-time-picker/SinceRangePicker.tsx b/src/static-past-time-picker/SinceRangePicker.tsx index a699db47e0..78dcf95960 100644 --- a/src/static-past-time-picker/SinceRangePicker.tsx +++ b/src/static-past-time-picker/SinceRangePicker.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { format, getTime, startOfToday, startOfYesterday, startOfDay, isValid, isAfter } from 'date-fns'; +import { getTime, startOfToday, startOfYesterday, startOfDay, isValid, isAfter } from 'date-fns'; import { usePrefixCls, useLocale } from '@gio-design/utils'; +import { parseFnsTimeZone } from '../utils/timeHelper' import SwitchGroup from '../switchGroup'; import DatePicker from '../static-date-picker'; import InnerRangePanel from './InnerRangePanel'; @@ -30,7 +31,7 @@ function SinceRangePicker({ disabledDate, timeRange, onSelect, onCancel, experim }; const renderHeader = () => { - const startDateString = startDate ? format(startDate, DATE_FORMAT) : startDayText; + const startDateString = startDate ? parseFnsTimeZone(startDate, DATE_FORMAT) : startDayText; return ( <> {`${FromText} ${startDateString}`} diff --git a/src/time-picker/TimePicker.tsx b/src/time-picker/TimePicker.tsx index 2fcbc4c9a8..9b6be7471b 100644 --- a/src/time-picker/TimePicker.tsx +++ b/src/time-picker/TimePicker.tsx @@ -2,9 +2,9 @@ import React from 'react'; import classnames from 'classnames'; import { StopWatchOutlined } from '@gio-design/icons'; import { useLocale, useControlledState, usePrefixCls } from '@gio-design/utils'; -import { format } from 'date-fns'; import { Locale } from 'rc-picker/lib/interface'; import { isNil } from 'lodash'; +import { parseFnsTimeZone } from '../utils/timeHelper'; import Button from '../button'; import Popover from '../popover'; import { InputButton } from '../input'; @@ -45,7 +45,7 @@ export const TimePicker: React.FC = (props: TimePickerProps) => const { now, ok, timeSelect } = coalescedLocale; const overlayCls = classnames(`${prefixCls}-overlay`, overlayClassName); - const formatTime = (date: Date) => format(date, showSecond ? TIME_WITH_SECOND_FORMAT : TIME_FORMAT); + const formatTime = (date: Date) => parseFnsTimeZone(date, showSecond ? TIME_WITH_SECOND_FORMAT : TIME_FORMAT); const handleOnPickerSelect = (currentValue: Date) => { setTime(currentValue); diff --git a/src/utils/timeHelper.ts b/src/utils/timeHelper.ts new file mode 100644 index 0000000000..687947888d --- /dev/null +++ b/src/utils/timeHelper.ts @@ -0,0 +1,18 @@ +import { isNumber } from 'lodash'; +import moment from 'moment-timezone'; +import { format as dateFnsFormat } from 'date-fns-tz'; + +// 时间日期转换时区 moment +export const parseTimeZone = (data?: any, format?: string) => + moment(data as string, format).tz(localStorage.getItem('timezone') || 'UTC'); + +// 时间日期转换时区 date-fns +export const parseFnsTimeZone = (date: number | Date | string, format: string) => { + let finalDate = date; + if (isNumber(date)) { + finalDate = new Date(date); + } + return dateFnsFormat(finalDate, format, { + timeZone: localStorage.getItem('timezone') || 'UTC', + }); +}; diff --git a/yarn.lock b/yarn.lock index 95e4da544c..acc262c786 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7038,6 +7038,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-fns-tz@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.3.7.tgz#e8e9d2aaceba5f1cc0e677631563081fdcb0e69a" + integrity sha512-1t1b8zyJo+UI8aR+g3iqr5fkUHWpd58VBx8J/ZSQ+w7YrGlw80Ag4sA86qkfCXRBLmMc4I2US+aPMd4uKvwj5g== + date-fns@2.x, date-fns@^2.21.3, date-fns@^2.25.0: version "2.28.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" @@ -12949,11 +12954,23 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +moment-timezone@^0.5.38: + version "0.5.38" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.38.tgz#9674a5397b8be7c13de820fd387d8afa0f725aad" + integrity sha512-nMIrzGah4+oYZPflDvLZUgoVUO4fvAqHstvG3xAUnMolWncuAiLDWNnJZj6EwJGMGfb1ZcuTFE6GI3hNOVWI/Q== + dependencies: + moment ">= 2.9.0" + moment@2.x, moment@^2.24.0, moment@^2.26.0: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== +"moment@>= 2.9.0": + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + moo@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"