From 051b087c0f154f5a4363bd9d0899baaa635f7a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A1=E8=89=B2?= Date: Thu, 9 Nov 2023 20:29:56 +0800 Subject: [PATCH] feat(theme:_httpclient): add `timestampSecond` (#1670) --- .../theme/src/services/http/http.client.ts | 14 +++++--- packages/theme/src/services/http/http.spec.ts | 8 +++++ packages/util/config/theme/http.type.ts | 5 +-- packages/util/date-time/index.en-US.md | 22 ++++++++---- packages/util/date-time/index.zh-CN.md | 22 ++++++++---- packages/util/date-time/time.spec.ts | 5 +-- packages/util/date-time/time.ts | 36 ++++++++++++------- src/app/app.module.ts | 3 ++ 8 files changed, 80 insertions(+), 35 deletions(-) diff --git a/packages/theme/src/services/http/http.client.ts b/packages/theme/src/services/http/http.client.ts index 6e1d63032e..66f8a4a13e 100644 --- a/packages/theme/src/services/http/http.client.ts +++ b/packages/theme/src/services/http/http.client.ts @@ -54,15 +54,19 @@ export class _HttpClient { return params; } + const { nullValueHandling, dateValueHandling } = this.cog; Object.keys(params).forEach(key => { - let _data = params[key]; + let paramValue = params[key]; // 忽略空值 - if (this.cog.nullValueHandling === 'ignore' && _data == null) return; + if (nullValueHandling === 'ignore' && paramValue == null) return; // 将时间转化为:时间戳 (秒) - if (this.cog.dateValueHandling === 'timestamp' && _data instanceof Date) { - _data = _data.valueOf(); + if ( + paramValue instanceof Date && + (dateValueHandling === 'timestamp' || dateValueHandling === 'timestampSecond') + ) { + paramValue = dateValueHandling === 'timestamp' ? paramValue.valueOf() : Math.trunc(paramValue.valueOf() / 1000); } - newParams[key] = _data; + newParams[key] = paramValue; }); return new HttpParams({ fromObject: newParams }); } diff --git a/packages/theme/src/services/http/http.spec.ts b/packages/theme/src/services/http/http.spec.ts index 96c64e1c72..22daeb58f5 100644 --- a/packages/theme/src/services/http/http.spec.ts +++ b/packages/theme/src/services/http/http.spec.ts @@ -688,6 +688,14 @@ describe('theme: http.client', () => { const ret = backend.expectOne(() => true) as TestRequest; expect(ret.request.urlWithParams.length).toBeGreaterThan(URL.length + 15); })); + it('should be working second-level timestamps', fakeAsync(() => { + createModule({ dateValueHandling: 'timestampSecond' }); + const now = new Date(); + http.get(URL, { a: now }).subscribe(); + tick(); + const ret = backend.expectOne(() => true) as TestRequest; + expect(ret.request.urlWithParams).toContain(`${Math.trunc(+now / 1000)}`); + })); it('should be ingore null values', fakeAsync(() => { createModule({ nullValueHandling: 'ignore' }); http.get(URL, { a: 1, b: null, c: undefined }).subscribe(); diff --git a/packages/util/config/theme/http.type.ts b/packages/util/config/theme/http.type.ts index 7c8bf8772a..e3c8a2e0ec 100644 --- a/packages/util/config/theme/http.type.ts +++ b/packages/util/config/theme/http.type.ts @@ -7,8 +7,9 @@ export interface AlainThemeHttpClientConfig { nullValueHandling?: 'include' | 'ignore'; /** * 时间值处理,默认:`timestamp` - * - timestamp:时间戳 + * - timestamp:时间戳毫秒级 + * - timestampSecond:时间戳秒级 * - ignore:忽略处理,保持原始状态 */ - dateValueHandling?: 'timestamp' | 'ignore'; + dateValueHandling?: 'timestamp' | 'timestampSecond' | 'ignore'; } diff --git a/packages/util/date-time/index.en-US.md b/packages/util/date-time/index.en-US.md index 4c0de213f5..133135182a 100644 --- a/packages/util/date-time/index.en-US.md +++ b/packages/util/date-time/index.en-US.md @@ -4,6 +4,21 @@ subtitle: Date Time Conversion type: Tools --- +## toDate + +Convert to `Date` format, support `Date, number, string` types, If the argument is a number, it is treated as a timestamp. + +* `formatString` If parsing fails try to parse the date by pressing `formatString` +* `defaultValue` If parsing fails returned default value, default: `new Date(NaN)` +* `timestampSecond` Whether the incoming value is in seconds + +## formatDate + +Format date, supports `Date, number, string` types, If the argument is a number, it is treated as a timestamp. + +* Please refer to [date-fnd format](https://date-fns.org/v2.30.0/docs/format) for string format +* `dateLocale` Recommended to be consistent with NG-ZORRO by using `inject(NZ_DATE_LOCALE)` + ## dateTimePickerUtil 一组针对 [DatePicker](https://ng.ant.design/components/date-picker/en) 的工具类。 @@ -39,10 +54,3 @@ getTimeDistance('week') - `month`, `-month` This month or last month - `year`, `-year` This year or last year - `time` Specify start time, default is `now` - -## toDate - -Return the date parsed from string using the given format string, If the argument is a number, it is treated as a timestamp. - -* `formatString` If parsing fails try to parse the date by pressing `formatString` -* `defaultValue` If parsing fails returned default value, default: `new Date(NaN)` diff --git a/packages/util/date-time/index.zh-CN.md b/packages/util/date-time/index.zh-CN.md index 6a1708862b..1f6c85fd1c 100644 --- a/packages/util/date-time/index.zh-CN.md +++ b/packages/util/date-time/index.zh-CN.md @@ -4,6 +4,21 @@ subtitle: 日期时间转换 type: Tools --- +## toDate + +转换成 `Date` 格式,支持 `Date, number, string` 类型,如果是 `number` 表示 Unix timestamp。 + +* `formatString` 如果转换失败尝试根据 `formatString` 格式来转换 +* `defaultValue` 无效日期应返回的默认值,默认:`new Date(NaN)` +* `timestampSecond` 传入值是否秒级 + +## formatDate + +格式化日期,支持 `Date, number, string` 类型,如果是 `number` 表示 Unix timestamp)。 + +* 字符串格式请参考 [date-fnd format](https://date-fns.org/v2.30.0/docs/format) +* `dateLocale` 建议通过使用 `inject(NZ_DATE_LOCALE)` 与 NG-ZORRO 保持一致 + ## dateTimePickerUtil 一组针对 [DatePicker](https://ng.ant.design/components/date-picker/en) 的工具类。 @@ -39,10 +54,3 @@ getTimeDistance('week') - `month`、`-month` 本月或上月 - `year`、`-year` 今年或去年 - `time` 指定开始时间,默认为:`now` - -## toDate - -转换成 `Date` 格式,支持 `Date, number, string` 类型,如果是 `number` 表示 Unix timestamp。 - -* `formatString` 如果转换失败尝试根据 `formatString` 格式来转换 -* `defaultValue` 无效日期应返回的默认值,默认:`new Date(NaN)` diff --git a/packages/util/date-time/time.spec.ts b/packages/util/date-time/time.spec.ts index f9e932b926..cffda8b3c3 100644 --- a/packages/util/date-time/time.spec.ts +++ b/packages/util/date-time/time.spec.ts @@ -68,12 +68,13 @@ describe('util: time', () => { expect(toDate(null).toString()).toBe(`Invalid Date`); expect(f(toDate(NOW))).toBe(`2000-01-01 00:00:00`); expect(f(toDate(+NOW))).toBe(`2000-01-01 00:00:00`); + expect(f(toDate(Math.trunc(+NOW / 1000), { timestampSecond: true }))).toBe(`2000-01-01 00:00:00`); expect(f(toDate(`${+NOW}`))).toBe(`2000-01-01 00:00:00`); expect(f(toDate(f(NOW)))).toBe(`2000-01-01 00:00:00`); expect(isNaN(toDate(new String('') as NzSafeAny) as NzSafeAny)).toBe(true); }); - function f(d: Date): string { - return format(d, `yyyy-MM-dd HH:mm:ss`, { locale: zhCN }); + function f(d: Date, formatString = `yyyy-MM-dd HH:mm:ss`): string { + return format(d, formatString, { locale: zhCN }); } }); diff --git a/packages/util/date-time/time.ts b/packages/util/date-time/time.ts index 74d8358aff..726f25c2d4 100644 --- a/packages/util/date-time/time.ts +++ b/packages/util/date-time/time.ts @@ -18,7 +18,7 @@ import { } from 'date-fns'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; -import type { DateLocale } from 'ng-zorro-antd/i18n'; +import { DateLocale } from 'ng-zorro-antd/i18n'; /** * Get the time range, return `[ Date, Date]` for the start and end dates @@ -84,21 +84,25 @@ export function fixEndTimeOfRange(dates: [Date, Date]): [Date, Date] { return [startOfDay(dates[0]), endOfDay(dates[1])]; } -export type ToDateOptions = string | { formatString?: string; defaultValue?: NzSafeAny }; +export interface ToDateOptions { + /** If parsing fails try to parse the date by pressing `formatString` */ + formatString?: string; + /** If parsing fails returned default value, default: `new Date(NaN)` */ + defaultValue?: NzSafeAny; + timestampSecond?: boolean; +} /** - * Return the date parsed from string using the given format string - * - If the argument is a number, it is treated as a timestamp. + * Convert to `Date` format * - * @param formatString If parsing fails try to parse the date by pressing `formatString` - * @param defaultValue If parsing fails returned default value, default: `new Date(NaN)` + * @param value When is a number, it's treated as a timestamp; If it's seconds, you need to provide the `options.timestampSecond` parameter. */ -export function toDate(value?: Date | string | number | null, options?: ToDateOptions): Date { - if (typeof options === 'string') options = { formatString: options }; - const { formatString, defaultValue } = { +export function toDate(value?: Date | string | number | null, options?: string | ToDateOptions): Date { + const { formatString, defaultValue, timestampSecond } = { formatString: 'yyyy-MM-dd HH:mm:ss', defaultValue: new Date(NaN), - ...options + timestampSecond: false, + ...(typeof options === 'string' ? { formatString: options } : options) }; if (value == null) { return defaultValue; @@ -106,8 +110,9 @@ export function toDate(value?: Date | string | number | null, options?: ToDateOp if (value instanceof Date) { return value; } - if (typeof value === 'number' || (typeof value === 'string' && /[0-9]{10,13}/.test(value))) { - return new Date(+value); + if (typeof value === 'number' || (typeof value === 'string' && /^[0-9]+$/.test(value))) { + const valueNumber = +value; + return new Date(timestampSecond ? valueNumber * 1000 : valueNumber); } let tryDate = parseISO(value); if (isNaN(tryDate as NzSafeAny)) { @@ -117,6 +122,13 @@ export function toDate(value?: Date | string | number | null, options?: ToDateOp return isNaN(tryDate as NzSafeAny) ? defaultValue : tryDate; } +/** + * Format date, supports `Date, number, string` types + * + * @param value When is a number, it is treated as a timestamp (Support seconds and milliseconds timestamp). + * @param formatString Please refer to [date-fnd format](https://date-fns.org/v2.30.0/docs/format) for string format + * @param dateLocale Recommended to be consistent with NG-ZORRO by using `inject(NZ_DATE_LOCALE)` + */ export function formatDate(value: Date | string | number, formatString: string, dateLocale?: DateLocale): string { value = toDate(value); if (isNaN(value as NzSafeAny)) return ''; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dd6133cfeb..27a3ff17bd 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -7,6 +7,8 @@ import localeZh from '@angular/common/locales/zh'; import { APP_INITIALIZER, ErrorHandler, Inject, Injector, NgModule, PLATFORM_ID } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NZ_DATE_LOCALE } from 'ng-zorro-antd/i18n'; +import { zhCN as dateLang } from 'date-fns/locale'; // angular i18n registerLocaleData(localeZh); @@ -82,6 +84,7 @@ function registerElements(injector: Injector, platformId: {}): void { // deps: [ReuseTabService], // }, { provide: ALAIN_I18N_TOKEN, useClass: I18NService, multi: false }, + { provide: NZ_DATE_LOCALE, useValue: dateLang }, StartupService, { provide: APP_INITIALIZER,