Browse Source

fix: fixed DatePicker timeZone map logic and use Etc/GMT* id #1585 (#1586)

Co-authored-by: shijia.me <[email protected]>
走鹃 2 years ago
parent
commit
c58d34ecd8

+ 56 - 0
packages/semi-foundation/utils/date-fns-extra.ts

@@ -52,6 +52,40 @@ export const IANAOffsetMap = [
     [14, ['Pacific/Kiritimati']],
 ];
 
+/**
+ * Etc/GMT* no DST
+ * @see https://data.iana.org/time-zones/tzdb/etcetera
+ */
+const IANAEtcGMTOffsetMap = {
+    '0': 'Etc/GMT',
+    '1': 'Etc/GMT-1',
+    '2': 'Etc/GMT-2',
+    '3': 'Etc/GMT-3',
+    '4': 'Etc/GMT-4',
+    '5': 'Etc/GMT-5',
+    '6': 'Etc/GMT-6',
+    '7': 'Etc/GMT-7',
+    '8': 'Etc/GMT-8',
+    '9': 'Etc/GMT-9',
+    '10': 'Etc/GMT-10',
+    '11': 'Etc/GMT-11',
+    '12': 'Etc/GMT-12',
+    '13': 'Etc/GMT-13',
+    '14': 'Etc/GMT-14',
+    '-1': 'Etc/GMT+1',
+    '-2': 'Etc/GMT+2',
+    '-3': 'Etc/GMT+3',
+    '-4': 'Etc/GMT+4',
+    '-5': 'Etc/GMT+5',
+    '-6': 'Etc/GMT+6',
+    '-7': 'Etc/GMT+7',
+    '-8': 'Etc/GMT+8',
+    '-9': 'Etc/GMT+9',
+    '-10': 'Etc/GMT+10',
+    '-11': 'Etc/GMT+11',
+    '-12': 'Etc/GMT+12',
+};
+
 const GMTStringReg = /([\-\+]{1})(\d{2})\:(\d{2})/;
 
 /**
@@ -74,11 +108,33 @@ export const toIANA = (tz: string | number) => {
     }
 
     if (typeof tz === 'number') {
+        // if tz can be transformed to a Etc/GMT* and browser supports it
+        if (tz in IANAEtcGMTOffsetMap) {
+            const etcGMTtimeZone = IANAEtcGMTOffsetMap[tz];
+            if (isValidTimezoneIANAString(etcGMTtimeZone)) {
+                return etcGMTtimeZone;
+            }
+        }
         const found = IANAOffsetMap.find(item => item[0] === tz);
         return found && found[1][0];
     }
 };
 
+const validIANATimezoneCache = {};
+/**
+ * @see https://github.com/marnusw/date-fns-tz/blob/a92e0ad017d101a0c50e39a63ef5d322b4d849f6/src/_lib/tzParseTimezone/index.js#L137
+ */
+export function isValidTimezoneIANAString(timeZoneString: string) {
+    if (validIANATimezoneCache[timeZoneString]) return true;
+    try {
+        new Intl.DateTimeFormat(undefined, { timeZone: timeZoneString });
+        validIANATimezoneCache[timeZoneString] = true;
+        return true;
+    } catch (error) {
+        return false;
+    }
+}
+
 /**
  *
  * @param {string | number | Date} date

+ 27 - 0
packages/semi-ui/datePicker/_story/v2/FeatEtcGMT.tsx

@@ -0,0 +1,27 @@
+import React, { useMemo } from 'react';
+import { DatePicker, Space } from '@douyinfe/semi-ui';
+
+App.storyName = 'timeZone Etc/GMT*';
+
+/**
+ * test with chromatic
+ * @see https://github.com/DouyinFE/semi-design/issues/1585
+ */
+export default function App(props = {}) {
+    const gmtList = useMemo(() => {
+        const list = [];
+        for (let hourOffset = -11; hourOffset <= 14; hourOffset++) {
+            const prefix = hourOffset >= 0 ? '+' : '-';
+            const hOffset = Math.abs(parseInt(hourOffset, 10));
+            list.push(`GMT${prefix}${String(hOffset).padStart(2, '0')}:00`);
+        }
+        return list;
+    }, []);
+    return (
+        <Space vertical>
+            {gmtList.map(gmt => (
+                <DatePicker key={gmt} prefix={gmt} timeZone={gmt} type={'dateTime'} defaultValue={1683084540000} />
+            ))}
+        </Space>
+    );
+}

+ 1 - 0
packages/semi-ui/datePicker/_story/v2/index.js

@@ -22,3 +22,4 @@ export { default as FeatOnClickOutside } from './FeatOnClickOutside';
 export { default as FeatRefClass } from './FeatRefClass';
 export { default as FixNeedConfirmInTabs } from './FixNeedConfirmInTabs';
 export { default as DynamicDisabledDate } from './dynamicDisabledDate';
+export { default as FeatEtcGMT } from './FeatEtcGMT';