fix: fix date format (#686)

* fix: fix date format

* fix: fix date format

* fix: export date

* fix: get current timezone

* fix: datetime render

* fix: gmt

* fix: gmt date

* fix: utf offset

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
SemmyWong 2022-07-28 17:32:31 +08:00 committed by GitHub
parent 95e799880c
commit 165ab2b876
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 112 additions and 86 deletions

View File

@ -37,6 +37,12 @@ export interface ApplicationOptions {
plugins?: any[];
}
export const getCurrentTimezone = () => {
const timezoneOffset = new Date().getTimezoneOffset() / -60;
const timezone = String(timezoneOffset).padStart(2, '0') + ':00';
return (timezoneOffset > 0 ? '+' : '-') + timezone;
};
export type PluginCallback = () => Promise<any>;
export class Application {
@ -51,6 +57,7 @@ export class Application {
baseURL: process.env.API_BASE_URL,
headers: {
'X-Hostname': window?.location?.hostname,
'X-Timezone': getCurrentTimezone(),
},
...options.apiClient,
});

View File

@ -1,10 +1,10 @@
import React from 'react';
import { connect, mapProps, mapReadPretty } from '@formily/react';
import { DatePicker as AntdDatePicker } from 'antd';
import type {
DatePickerProps as AntdDatePickerProps,
RangePickerProps as AntdRangePickerProps,
RangePickerProps as AntdRangePickerProps
} from 'antd/lib/date-picker';
import { connect, mapProps, mapReadPretty } from '@formily/react';
import React from 'react';
import { ReadPretty } from './ReadPretty';
import { mapDateFormat } from './util';

View File

@ -1,5 +1,6 @@
import { usePrefixCls } from '@formily/antd/lib/__builtins__';
import { isArr } from '@formily/shared';
import { getDefaultFormat, str2moment } from '@nocobase/utils/client';
import type {
DatePickerProps as AntdDatePickerProps,
RangePickerProps as AntdRangePickerProps
@ -7,7 +8,6 @@ import type {
import cls from 'classnames';
import moment from 'moment';
import React from 'react';
import { getDefaultFormat, str2moment } from './util';
type Composed = {
DatePicker: React.FC<AntdDatePickerProps>;
@ -38,7 +38,7 @@ ReadPretty.DateRangePicker = (props: any) => {
if (!m) {
return '';
}
const labels = m.map(m => m.format(format));
const labels = m.map((m) => m.format(format));
return isArr(labels) ? labels.join('~') : labels;
};
return (

View File

@ -1,5 +1,6 @@
import { str2moment } from '@nocobase/utils';
import moment from 'moment';
import { moment2str, str2moment } from '../util';
import { moment2str } from '../util';
describe('str2moment', () => {
describe('string value', () => {
@ -7,12 +8,12 @@ describe('str2moment', () => {
const m = str2moment('2022-06-21T00:00:00.000Z', { gmt: true });
expect(m.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-06-21 00:00:00');
});
test('local date', async () => {
const m = str2moment('2022-06-21T00:00:00.000Z');
expect(m.toISOString()).toBe('2022-06-21T00:00:00.000Z');
});
test('value is null', async () => {
const m = str2moment(null);
expect(m).toBeNull();
@ -31,7 +32,7 @@ describe('str2moment', () => {
expect(m.format('YYYY-MM-DD HH:mm:ss')).toBe('2022-06-21 00:00:00');
}
});
test('local date', async () => {
const arr = str2moment(['2022-06-21T00:00:00.000Z', '2022-06-21T00:00:00.000Z']);
for (const m of arr) {

View File

@ -1,18 +1,6 @@
import type { DatePickerProps } from 'antd/lib/date-picker';
import { getDefaultFormat, str2moment, toGmt, toLocal } from '@nocobase/utils/client';
import moment from 'moment';
const toGmt = (value: moment.Moment | moment.Moment[]) => {
if (!value) {
return value;
}
if (Array.isArray(value)) {
return value.map((val) => `${val.format('YYYY-MM-DD')}T${val.format('HH:mm:ss.SSS')}Z`);
}
if (moment.isMoment(value)) {
return `${value.format('YYYY-MM-DD')}T${value.format('HH:mm:ss.SSS')}Z`;
}
};
const toStringByPicker = (value, picker) => {
if (picker === 'year') {
return value.format('YYYY') + '-01-01T00:00:00.000Z';
@ -41,45 +29,6 @@ const toGmtByPicker = (value: moment.Moment | moment.Moment[], picker?: any) =>
}
};
const toLocal = (value: moment.Moment | moment.Moment[]) => {
if (!value) {
return value;
}
if (Array.isArray(value)) {
return value.map((val) => val.toISOString());
}
if (moment.isMoment(value)) {
return value.toISOString();
}
};
export interface Str2momentOptions {
gmt?: boolean;
picker?: 'year' | 'month' | 'week' | 'quarter';
}
const toMoment = (val: any, options?: Str2momentOptions) => {
if (moment.isMoment(val)) {
return val;
}
const { gmt, picker } = options;
if (gmt || picker) {
val = val.replace('T', ' ').replace('Z', '');
return moment(val);
}
return moment(val);
};
export const str2moment = (value?: string | string[], options: Str2momentOptions = {}): any => {
return Array.isArray(value)
? value.map((val) => {
return toMoment(val, options);
})
: value
? toMoment(value, options)
: value;
};
export interface Moment2strOptions {
showTime?: boolean;
gmt?: boolean;
@ -97,28 +46,6 @@ export const moment2str = (value?: moment.Moment | moment.Moment[], options: Mom
return toGmtByPicker(value, picker);
};
export const getDefaultFormat = (props: DatePickerProps & { dateFormat: string; timeFormat: string }) => {
if (props.format) {
return props.format;
}
if (props.dateFormat) {
if (props['showTime']) {
return `${props.dateFormat} ${props.timeFormat || 'HH:mm:ss'}`;
}
return props.dateFormat;
}
if (props['picker'] === 'month') {
return 'YYYY-MM';
} else if (props['picker'] === 'quarter') {
return 'YYYY-\\QQ';
} else if (props['picker'] === 'year') {
return 'YYYY';
} else if (props['picker'] === 'week') {
return 'YYYY-wo';
}
return props['showTime'] ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD';
};
export const mapDateFormat = function () {
return (props: any) => {
const format = getDefaultFormat(props) as any;

View File

@ -14,5 +14,9 @@
"deepmerge": "^4.2.2",
"flat-to-nested": "^1.1.1"
},
"peerDependencies": {
"moment": "2.x",
"rc-input-number": "7.x"
},
"gitHead": "7e9556e489007577fc0ed89063b3a9ce2f9aae53"
}

View File

@ -1,3 +1,4 @@
export * from './date';
export * from './merge';
export * from './number';
export * from './registry';

View File

@ -0,0 +1,78 @@
import moment from 'moment';
export interface Str2momentOptions {
gmt?: boolean;
picker?: 'year' | 'month' | 'week' | 'quarter';
utcOffset?: any;
}
export const getDefaultFormat = (props: any) => {
if (props.format) {
return props.format;
}
if (props.dateFormat) {
if (props['showTime']) {
return `${props.dateFormat} ${props.timeFormat || 'HH:mm:ss'}`;
}
return props.dateFormat;
}
if (props['picker'] === 'month') {
return 'YYYY-MM';
} else if (props['picker'] === 'quarter') {
return 'YYYY-\\QQ';
} else if (props['picker'] === 'year') {
return 'YYYY';
} else if (props['picker'] === 'week') {
return 'YYYY-wo';
}
return props['showTime'] ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD';
};
export const toGmt = (value: moment.Moment | moment.Moment[]) => {
if (!value) {
return value;
}
if (Array.isArray(value)) {
return value.map((val) => `${val.format('YYYY-MM-DD')}T${val.format('HH:mm:ss.SSS')}Z`);
}
if (moment.isMoment(value)) {
return `${value.format('YYYY-MM-DD')}T${value.format('HH:mm:ss.SSS')}Z`;
}
};
export const toLocal = (value: moment.Moment | moment.Moment[]) => {
if (!value) {
return value;
}
if (Array.isArray(value)) {
return value.map((val) => val.toISOString());
}
if (moment.isMoment(value)) {
return value.toISOString();
}
};
const toMoment = (val: any, options?: Str2momentOptions) => {
if (!val) {
return;
}
const offset = options.utcOffset || -1 * new Date().getTimezoneOffset();
if (moment.isMoment(val)) {
return val.utcOffset(offset);
}
const { gmt, picker } = options;
if (gmt || picker) {
return moment(val).utcOffset(0);
}
return moment(val).utcOffset(offset);
};
export const str2moment = (value?: string | string[], options: Str2momentOptions = {}): any => {
return Array.isArray(value)
? value.map((val) => {
return toMoment(val, options);
})
: value
? toMoment(value, options)
: value;
};

View File

@ -1,3 +1,4 @@
export * from './date';
export * from './merge';
export * from './mixin';
export * from './mixin/AsyncEmitter';

View File

@ -1,3 +1,4 @@
export * from './date';
export * from './merge';
export * from './mixin';
export * from './mixin/AsyncEmitter';

View File

@ -1,5 +1,4 @@
import { toFixedByStep } from '@nocobase/utils';
import moment from 'moment';
import { getDefaultFormat, str2moment } from '@nocobase/utils';
export async function _(field, row, ctx, column?: any) {
if (column?.dataIndex.length > 1) {
@ -34,7 +33,14 @@ export async function _(field, row, ctx, column?: any) {
export async function datetime(field, row, ctx) {
const value = row.get(field.name);
return moment(value).format(field.showTime ? `${field.dateFormat} ${field.timeFormat}` : field.dateFormat);
if (!value) {
return '';
}
const utcOffset = ctx.get('X-Timezone');
const props = field.options?.uiSchema?.['x-component-props'] ?? {};
const format = getDefaultFormat(props);
const m = str2moment(value, { ...props, utcOffset });
return m ? m.format(format) : '';
}
export async function percent(field, row, ctx) {