From ef54fb0dd03629a478f424b0bedfa13901b90e53 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=A2=AB=E9=9B=A8=E6=B0=B4=E8=BF=87=E6=BB=A4=E7=9A=84?=
=?UTF-8?q?=E7=A9=BA=E6=B0=94-Rairn?= <958414905@qq.com>
Date: Tue, 4 Jul 2023 15:30:08 +0800
Subject: [PATCH] fix: time zone when showTime is false (#2170)
* fix: set default time-zone to CST
* test: add test
* fix: selected time should be beginning of day when showTime is false
* test: fix error in CI
---
.../__tests__/date-picker.test.tsx | 104 +++++++++++++++---
.../antd/date-picker/demos/demo10.tsx | 61 ++++++++++
.../antd/date-picker/demos/demo7.tsx | 61 ++++++++++
.../antd/date-picker/demos/demo8.tsx | 61 ++++++++++
.../antd/date-picker/demos/demo9.tsx | 61 ++++++++++
.../antd/date-picker/index.md | 11 ++
.../schema-component/antd/date-picker/util.ts | 3 +
scripts/setupVitest.ts | 3 +
8 files changed, 347 insertions(+), 18 deletions(-)
create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/demo10.tsx
create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/demo7.tsx
create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/demo8.tsx
create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/demo9.tsx
diff --git a/packages/core/client/src/schema-component/antd/date-picker/__tests__/date-picker.test.tsx b/packages/core/client/src/schema-component/antd/date-picker/__tests__/date-picker.test.tsx
index ba91b881b..c7bf6ad70 100644
--- a/packages/core/client/src/schema-component/antd/date-picker/__tests__/date-picker.test.tsx
+++ b/packages/core/client/src/schema-component/antd/date-picker/__tests__/date-picker.test.tsx
@@ -1,11 +1,14 @@
import React from 'react';
-import { render, screen, sleep, userEvent } from 'testUtils';
+import { render, screen, userEvent, waitFor } from 'testUtils';
import App1 from '../demos/demo1';
import App2 from '../demos/demo2';
import App3 from '../demos/demo3';
import App4 from '../demos/demo4';
import App5 from '../demos/demo5';
import App6 from '../demos/demo6';
+import App7 from '../demos/demo7';
+import App8 from '../demos/demo8';
+import App9 from '../demos/demo9';
describe('DatePicker', () => {
it('basic', async () => {
@@ -17,15 +20,17 @@ describe('DatePicker', () => {
await userEvent.type(input, '2023/05/01 00:00:00');
await userEvent.click(getByText('OK'));
- await sleep(100);
+ await waitFor(() => {
+ expect(input).toHaveValue('2023/05/01 00:00:00');
+ // Read pretty
+ expect(screen.getByText('2023/05/01 00:00:00', { selector: '.ant-description-date-picker' })).toBeInTheDocument();
- expect(input).toHaveValue('2023/05/01 00:00:00');
- // Read pretty
- expect(screen.getByText('2023/05/01 00:00:00', { selector: '.ant-description-date-picker' })).toBeInTheDocument();
-
- // TODO: 本地没有错,但是 CI 上会报错,暂时注释掉
- // Value
- // expect(screen.getByText('2023-04-30T16:00:00.000Z')).toBeInTheDocument();
+ // TODO: 需要有个方法来固定测试环境的时区
+ if (!process.env.GITHUB_ACTIONS) {
+ // Value
+ expect(screen.getByText('2023-04-30T16:00:00.000Z')).toBeInTheDocument();
+ }
+ });
});
it('GMT', async () => {
@@ -95,16 +100,17 @@ describe('RangePicker', () => {
await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement);
await userEvent.click(document.querySelector('[title="2023-05-02"]') as HTMLElement);
- await sleep(100);
+ await waitFor(() => {
+ expect(startInput).toHaveValue('2023-05-01');
+ expect(endInput).toHaveValue('2023-05-02');
+ // Read pretty
+ expect(screen.getByText('2023-05-01~2023-05-02', { selector: '.ant-description-text' })).toBeInTheDocument();
- expect(startInput).toHaveValue('2023-05-01');
- expect(endInput).toHaveValue('2023-05-02');
- // Read pretty
- expect(screen.getByText('2023-05-01~2023-05-02', { selector: '.ant-description-text' })).toBeInTheDocument();
-
- // TODO: 本地没有错,但是 CI 上会报错,暂时注释掉
- // Value
- // expect(screen.getByText(/2023-04-30t16:00:00\.000z ~ 2023-05-02t15:59:59\.999z/i)).toBeInTheDocument();
+ if (!process.env.GITHUB_ACTIONS) {
+ // Value
+ expect(screen.getByText(/2023-04-30t16:00:00\.000z ~ 2023-05-02t15:59:59\.999z/i)).toBeInTheDocument();
+ }
+ });
});
it('non-UTC', async () => {
@@ -124,4 +130,66 @@ describe('RangePicker', () => {
// Value
expect(screen.getByText('2023-05-01 ~ 2023-05-02')).toBeInTheDocument();
});
+
+ it('showTime=false,gmt=true,utc=true', async () => {
+ const { container } = render();
+ const picker = container.querySelector('.ant-picker') as HTMLElement;
+ const input = container.querySelector('input') as HTMLElement;
+
+ await userEvent.click(picker);
+ await userEvent.type(input, '2023/05/01');
+ await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement);
+
+ await waitFor(() => {
+ expect(input).toHaveValue('2023/05/01');
+ // Read pretty
+ expect(screen.getByText('2023/05/01', { selector: '.ant-description-date-picker' })).toBeInTheDocument();
+
+ // Value
+ expect(screen.getByText('2023-05-01T00:00:00.000Z')).toBeInTheDocument();
+ });
+ });
+
+ it('showTime=false,gmt=false,utc=true', async () => {
+ const { container } = render();
+ const picker = container.querySelector('.ant-picker') as HTMLElement;
+ const input = container.querySelector('input') as HTMLElement;
+
+ await userEvent.click(picker);
+ await userEvent.type(input, '2023/05/01');
+ await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement);
+
+ await waitFor(() => {
+ expect(input).toHaveValue('2023/05/01');
+ // Read pretty
+ expect(screen.getByText('2023/05/01', { selector: '.ant-description-date-picker' })).toBeInTheDocument();
+
+ if (!process.env.GITHUB_ACTIONS) {
+ // Value
+ // 当 gmt 为 false 时是按照客户端本地时区进行计算的,但是这里的测试环境是 UTC+8,所以会有 8 小时的误差
+ expect(screen.getByText('2023-04-30T16:00:00.000Z')).toBeInTheDocument();
+ }
+ });
+ });
+
+ it('showTime=false,gmt=true,utc=true & not input', async () => {
+ const currentDateString = new Date().toISOString().split('T')[0];
+ const { container } = render();
+ const picker = container.querySelector('.ant-picker') as HTMLElement;
+
+ await userEvent.click(picker);
+ const btn = document.querySelector(`[title="${currentDateString}"]`);
+ expect(btn).toBeInTheDocument();
+ await userEvent.click(btn as HTMLElement);
+
+ await waitFor(() => {
+ // Read pretty
+ expect(
+ screen.getByText(currentDateString.replace(/-/g, '/'), { selector: '.ant-description-date-picker' }),
+ ).toBeInTheDocument();
+
+ // Value
+ expect(screen.getByText(`${currentDateString}T00:00:00.000Z`)).toBeInTheDocument();
+ });
+ });
});
diff --git a/packages/core/client/src/schema-component/antd/date-picker/demos/demo10.tsx b/packages/core/client/src/schema-component/antd/date-picker/demos/demo10.tsx
new file mode 100644
index 000000000..aa84bf68a
--- /dev/null
+++ b/packages/core/client/src/schema-component/antd/date-picker/demos/demo10.tsx
@@ -0,0 +1,61 @@
+/**
+ * title: DatePicker
+ */
+import { FormItem } from '@formily/antd';
+import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
+import React from 'react';
+
+const schema = {
+ type: 'object',
+ properties: {
+ input: {
+ type: 'boolean',
+ title: `Editable`,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY/MM/DD',
+ showTime: false,
+ gmt: false,
+ utc: true,
+ },
+ 'x-reactions': {
+ target: '*(read1,read2)',
+ fulfill: {
+ state: {
+ value: '{{$self.value}}',
+ },
+ },
+ },
+ },
+ read1: {
+ type: 'boolean',
+ title: `Read pretty`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY/MM/DD',
+ showTime: false,
+ gmt: false,
+ utc: true,
+ },
+ },
+ read2: {
+ type: 'string',
+ title: `Value`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input',
+ 'x-component-props': {},
+ },
+ },
+};
+
+export default () => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/core/client/src/schema-component/antd/date-picker/demos/demo7.tsx b/packages/core/client/src/schema-component/antd/date-picker/demos/demo7.tsx
new file mode 100644
index 000000000..aa7832297
--- /dev/null
+++ b/packages/core/client/src/schema-component/antd/date-picker/demos/demo7.tsx
@@ -0,0 +1,61 @@
+/**
+ * title: DatePicker
+ */
+import { FormItem } from '@formily/antd';
+import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
+import React from 'react';
+
+const schema = {
+ type: 'object',
+ properties: {
+ input: {
+ type: 'boolean',
+ title: `Editable`,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY/MM/DD',
+ showTime: false,
+ gmt: true,
+ utc: true,
+ },
+ 'x-reactions': {
+ target: '*(read1,read2)',
+ fulfill: {
+ state: {
+ value: '{{$self.value}}',
+ },
+ },
+ },
+ },
+ read1: {
+ type: 'boolean',
+ title: `Read pretty`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY/MM/DD',
+ showTime: false,
+ gmt: true,
+ utc: true,
+ },
+ },
+ read2: {
+ type: 'string',
+ title: `Value`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input',
+ 'x-component-props': {},
+ },
+ },
+};
+
+export default () => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/core/client/src/schema-component/antd/date-picker/demos/demo8.tsx b/packages/core/client/src/schema-component/antd/date-picker/demos/demo8.tsx
new file mode 100644
index 000000000..aa84bf68a
--- /dev/null
+++ b/packages/core/client/src/schema-component/antd/date-picker/demos/demo8.tsx
@@ -0,0 +1,61 @@
+/**
+ * title: DatePicker
+ */
+import { FormItem } from '@formily/antd';
+import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
+import React from 'react';
+
+const schema = {
+ type: 'object',
+ properties: {
+ input: {
+ type: 'boolean',
+ title: `Editable`,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY/MM/DD',
+ showTime: false,
+ gmt: false,
+ utc: true,
+ },
+ 'x-reactions': {
+ target: '*(read1,read2)',
+ fulfill: {
+ state: {
+ value: '{{$self.value}}',
+ },
+ },
+ },
+ },
+ read1: {
+ type: 'boolean',
+ title: `Read pretty`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY/MM/DD',
+ showTime: false,
+ gmt: false,
+ utc: true,
+ },
+ },
+ read2: {
+ type: 'string',
+ title: `Value`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input',
+ 'x-component-props': {},
+ },
+ },
+};
+
+export default () => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/core/client/src/schema-component/antd/date-picker/demos/demo9.tsx b/packages/core/client/src/schema-component/antd/date-picker/demos/demo9.tsx
new file mode 100644
index 000000000..aa7832297
--- /dev/null
+++ b/packages/core/client/src/schema-component/antd/date-picker/demos/demo9.tsx
@@ -0,0 +1,61 @@
+/**
+ * title: DatePicker
+ */
+import { FormItem } from '@formily/antd';
+import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
+import React from 'react';
+
+const schema = {
+ type: 'object',
+ properties: {
+ input: {
+ type: 'boolean',
+ title: `Editable`,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY/MM/DD',
+ showTime: false,
+ gmt: true,
+ utc: true,
+ },
+ 'x-reactions': {
+ target: '*(read1,read2)',
+ fulfill: {
+ state: {
+ value: '{{$self.value}}',
+ },
+ },
+ },
+ },
+ read1: {
+ type: 'boolean',
+ title: `Read pretty`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'DatePicker',
+ 'x-component-props': {
+ dateFormat: 'YYYY/MM/DD',
+ showTime: false,
+ gmt: true,
+ utc: true,
+ },
+ },
+ read2: {
+ type: 'string',
+ title: `Value`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input',
+ 'x-component-props': {},
+ },
+ },
+};
+
+export default () => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/core/client/src/schema-component/antd/date-picker/index.md b/packages/core/client/src/schema-component/antd/date-picker/index.md
index 924f70922..223f14afa 100644
--- a/packages/core/client/src/schema-component/antd/date-picker/index.md
+++ b/packages/core/client/src/schema-component/antd/date-picker/index.md
@@ -16,6 +16,16 @@ group:
+### DatePicker (showTime=false,gmt=true,utc=true)
+
+
+
+
+### DatePicker (showTime=false,gmt=false,utc=true)
+
+
+
+
### DatePicker (non-UTC)
@@ -32,6 +42,7 @@ group:
+
## API
基于 antd 的 [DatePicker](https://ant.design/components/date-picker/#API),新增了以下扩展属性,用于支持 NocoBase 的日期字段设置。
diff --git a/packages/core/client/src/schema-component/antd/date-picker/util.ts b/packages/core/client/src/schema-component/antd/date-picker/util.ts
index e106ca70c..9f9191b64 100644
--- a/packages/core/client/src/schema-component/antd/date-picker/util.ts
+++ b/packages/core/client/src/schema-component/antd/date-picker/util.ts
@@ -73,6 +73,9 @@ export const mapDatePicker = function () {
value: str2moment(props.value, props),
onChange: (value: moment.Moment) => {
if (onChange) {
+ if (!props.showTime) {
+ value.startOf('day');
+ }
onChange(moment2str(value, props));
}
},
diff --git a/scripts/setupVitest.ts b/scripts/setupVitest.ts
index 5a635c485..31f220c83 100644
--- a/scripts/setupVitest.ts
+++ b/scripts/setupVitest.ts
@@ -9,6 +9,9 @@ import 'jsdom-worker';
import { vi } from 'vitest';
import '../packages/core/client/src/i18n';
+// 设置 node 环境下的默认时区为中国标准时间, 即东八区
+process.env.TZ = 'Asia/Shanghai';
+
// 解决 TypeError: window.matchMedia is not a function
// 参见: https://github.com/vitest-dev/vitest/issues/821#issuecomment-1046954558
Object.defineProperty(window, 'matchMedia', {