diff --git a/components/core/time/public-api.ts b/components/core/time/public-api.ts
index 69e5f0db587..b2c828f91e2 100644
--- a/components/core/time/public-api.ts
+++ b/components/core/time/public-api.ts
@@ -5,3 +5,4 @@
export * from './candy-date';
export * from './time';
+export * from './time-parser';
diff --git a/components/core/time/time-parser.spec.ts b/components/core/time/time-parser.spec.ts
new file mode 100644
index 00000000000..ff0d0abde4c
--- /dev/null
+++ b/components/core/time/time-parser.spec.ts
@@ -0,0 +1,68 @@
+import { Injector, LOCALE_ID } from '@angular/core';
+import { TestBed } from '@angular/core/testing';
+import { NgTimeParser, TimeParserResult } from './time-parser';
+
+describe('Parse time with angular format', () => {
+ let injector: Injector;
+ let localeId: string;
+ let parser: NgTimeParser;
+ let result: TimeParserResult | null;
+ let time: Date;
+
+ beforeEach(() => {
+ injector = TestBed.configureTestingModule({});
+ localeId = injector.get(LOCALE_ID);
+ });
+
+ it('should parse hh:mm:ss a', () => {
+ parser = new NgTimeParser('hh:mm:ss a', localeId);
+ result = parser.getTimeParserResult('12:30:22 AM');
+ expect(result?.hour).toBe(12);
+ expect(result?.minute).toBe(30);
+ expect(result?.second).toBe(22);
+ expect(result?.period).toBe(0);
+
+ result = parser.getTimeParserResult('12:30:22 PM');
+ expect(result?.period).toBe(1);
+
+ time = parser.getTime('12:30:22 PM');
+ expect(time.getHours()).toBe(12);
+ expect(time.getMinutes()).toBe(30);
+ expect(time.getSeconds()).toBe(22);
+
+ time = parser.getTime('05:30:22 PM');
+ expect(time.getHours()).toBe(17);
+ });
+
+ it('should parse hh:mm:ss aaaaa', () => {
+ parser = new NgTimeParser('hh:mm:ss aaaaa', localeId);
+ result = parser.getTimeParserResult('12:30:22 a');
+ expect(result?.hour).toBe(12);
+ expect(result?.minute).toBe(30);
+ expect(result?.second).toBe(22);
+ expect(result?.period).toBe(0);
+
+ result = parser.getTimeParserResult('12:30:22 p');
+ expect(result?.period).toBe(1);
+ });
+
+ it('should parse mm(ss) HH', () => {
+ parser = new NgTimeParser('mm(ss) HH', localeId);
+ result = parser.getTimeParserResult('30(22) 12');
+ expect(result?.period).toBe(null);
+
+ time = parser.getTime('30(22) 12');
+ expect(time.getHours()).toBe(12);
+ expect(time.getMinutes()).toBe(30);
+ expect(time.getSeconds()).toBe(22);
+ });
+
+ it('should parse ss + mm', () => {
+ parser = new NgTimeParser('ss + mm', localeId);
+ time = parser.getTime('10 + 42');
+ const now = new Date();
+ expect(time.getHours()).toBe(now.getHours());
+ expect(time.getMinutes()).toBe(42);
+ expect(time.getSeconds()).toBe(10);
+ });
+});
diff --git a/components/core/time/time-parser.ts b/components/core/time/time-parser.ts
new file mode 100644
index 00000000000..3c71d1021f1
--- /dev/null
+++ b/components/core/time/time-parser.ts
@@ -0,0 +1,143 @@
+/**
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
+ */
+
+// from https://github.com/hsuanxyz/ng-time-parser
+import { FormStyle, getLocaleDayPeriods, TranslationWidth } from '@angular/common';
+import { isNotNil } from 'ng-zorro-antd/core/util';
+
+export interface TimeParserResult {
+ hour: number | null;
+ minute: number | null;
+ second: number | null;
+ period: number | null;
+}
+
+export class NgTimeParser {
+ regex: RegExp = null!;
+ matchMap: { [key: string]: null | number } = {
+ hour: null,
+ minute: null,
+ second: null,
+ periodNarrow: null,
+ periodWide: null,
+ periodAbbreviated: null
+ };
+
+ constructor(private format: string, private localeId: string) {
+ this.genRegexp();
+ }
+
+ getTime(str: string): Date {
+ const result = this.getTimeParserResult(str);
+ const time = new Date();
+
+ if (result?.hour) {
+ time.setHours(result.hour);
+ }
+
+ if (result?.minute) {
+ time.setMinutes(result.minute);
+ }
+
+ if (result?.second) {
+ time.setSeconds(result.second);
+ }
+
+ if (result?.period === 1 && time.getHours() < 12) {
+ time.setHours(time.getHours() + 12);
+ }
+
+ return time;
+ }
+
+ getTimeParserResult(str: string): TimeParserResult | null {
+ const match = this.regex.exec(str);
+ let period = null;
+ if (match) {
+ if (isNotNil(this.matchMap.periodNarrow)) {
+ period = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Narrow).indexOf(
+ match[this.matchMap.periodNarrow + 1]
+ );
+ }
+ if (isNotNil(this.matchMap.periodWide)) {
+ period = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Wide).indexOf(match[this.matchMap.periodWide + 1]);
+ }
+ if (isNotNil(this.matchMap.periodAbbreviated)) {
+ period = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Abbreviated).indexOf(
+ match[this.matchMap.periodAbbreviated + 1]
+ );
+ }
+ return {
+ hour: isNotNil(this.matchMap.hour) ? Number.parseInt(match[this.matchMap.hour + 1], 10) : null,
+ minute: isNotNil(this.matchMap.minute) ? Number.parseInt(match[this.matchMap.minute + 1], 10) : null,
+ second: isNotNil(this.matchMap.second) ? Number.parseInt(match[this.matchMap.second + 1], 10) : null,
+ period
+ };
+ } else {
+ return null;
+ }
+ }
+
+ genRegexp(): void {
+ let regexStr = this.format.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$&');
+ const hourRegex = /h{1,2}/i;
+ const minuteRegex = /m{1,2}/;
+ const secondRegex = /s{1,2}/;
+ const periodNarrow = /aaaaa/;
+ const periodWide = /aaaa/;
+ const periodAbbreviated = /a{1,3}/;
+
+ const hourMatch = hourRegex.exec(this.format);
+ const minuteMatch = minuteRegex.exec(this.format);
+ const secondMatch = secondRegex.exec(this.format);
+ const periodNarrowMatch = periodNarrow.exec(this.format);
+ let periodWideMatch: null | RegExpExecArray = null;
+ let periodAbbreviatedMatch: null | RegExpExecArray = null;
+ if (!periodNarrowMatch) {
+ periodWideMatch = periodWide.exec(this.format);
+ }
+ if (!periodWideMatch && !periodNarrowMatch) {
+ periodAbbreviatedMatch = periodAbbreviated.exec(this.format);
+ }
+
+ const matchs = [hourMatch, minuteMatch, secondMatch, periodNarrowMatch, periodWideMatch, periodAbbreviatedMatch]
+ .filter(m => !!m)
+ .sort((a, b) => a!.index - b!.index);
+
+ matchs.forEach((match, index) => {
+ switch (match) {
+ case hourMatch:
+ this.matchMap.hour = index;
+ regexStr = regexStr.replace(hourRegex, '(\\d{1,2})');
+ break;
+ case minuteMatch:
+ this.matchMap.minute = index;
+ regexStr = regexStr.replace(minuteRegex, '(\\d{1,2})');
+ break;
+ case secondMatch:
+ this.matchMap.second = index;
+ regexStr = regexStr.replace(secondRegex, '(\\d{1,2})');
+ break;
+ case periodNarrowMatch:
+ this.matchMap.periodNarrow = index;
+ const periodsNarrow = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Narrow).join('|');
+ regexStr = regexStr.replace(periodNarrow, `(${periodsNarrow})`);
+ break;
+ case periodWideMatch:
+ this.matchMap.periodWide = index;
+ const periodsWide = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Wide).join('|');
+ regexStr = regexStr.replace(periodWide, `(${periodsWide})`);
+ break;
+ case periodAbbreviatedMatch:
+ this.matchMap.periodAbbreviated = index;
+ const periodsAbbreviated = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Abbreviated).join('|');
+ regexStr = regexStr.replace(periodAbbreviated, `(${periodsAbbreviated})`);
+ break;
+ }
+ });
+
+ this.regex = new RegExp(regexStr);
+ }
+}
diff --git a/components/i18n/date-helper.service.spec.ts b/components/i18n/date-helper.service.spec.ts
index e366ed26f8e..ad268fdabdc 100644
--- a/components/i18n/date-helper.service.spec.ts
+++ b/components/i18n/date-helper.service.spec.ts
@@ -38,8 +38,8 @@ describe('DateHelperService', () => {
});
it('should do parseTime correctly', () => {
- expect(dateHelper.parseTime('14:00')?.toTimeString().substr(0, 8)).toBe('14:00:00');
- expect(dateHelper.parseTime('4:00')?.toTimeString().substr(0, 8)).toBe('04:00:00');
+ expect(dateHelper.parseTime('14:00', 'HH:mm')?.toTimeString().substr(0, 8)).toBe('14:00:00');
+ expect(dateHelper.parseTime('4:00', 'H:mm')?.toTimeString().substr(0, 8)).toBe('04:00:00');
});
});
diff --git a/components/i18n/date-helper.service.ts b/components/i18n/date-helper.service.ts
index 9262f86f534..eb106bfa7fc 100644
--- a/components/i18n/date-helper.service.ts
+++ b/components/i18n/date-helper.service.ts
@@ -9,7 +9,7 @@ import fnsFormat from 'date-fns/format';
import fnsGetISOWeek from 'date-fns/getISOWeek';
import fnsParse from 'date-fns/parse';
-import { WeekDayIndex } from 'ng-zorro-antd/core/time';
+import { NgTimeParser, WeekDayIndex } from 'ng-zorro-antd/core/time';
import { convertTokens } from './convert-tokens';
import { mergeDateConfig, NzDateConfig, NZ_DATE_CONFIG, NZ_DATE_FNS_COMPATIBLE } from './date-config';
import { NzI18nService } from './nz-i18n.service';
@@ -117,9 +117,8 @@ export class DateHelperByDatePipe extends DateHelperService {
return new Date(text);
}
- parseTime(text: string): Date {
- // const formatReg = new RegExp(formatStr.replace(/h|hh|m|mm|s|ss/, '\w+'), 'gi');
- const now = new Date();
- return new Date(`${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ${text}`);
+ parseTime(text: string, formatStr: string): Date {
+ const parser = new NgTimeParser(formatStr, this.i18n.getLocaleId());
+ return parser.getTime(text);
}
}
diff --git a/components/time-picker/demo/use12-hours.ts b/components/time-picker/demo/use12-hours.ts
index 76243c29c54..3c9573124ed 100644
--- a/components/time-picker/demo/use12-hours.ts
+++ b/components/time-picker/demo/use12-hours.ts
@@ -3,8 +3,8 @@ import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-time-picker-use12-hours',
template: `
-
-
+
+
`,
styles: [
`
@@ -16,4 +16,8 @@ import { Component } from '@angular/core';
})
export class NzDemoTimePickerUse12HoursComponent {
time: Date | null = null;
+
+ log(value: Date): void {
+ console.log(value);
+ }
}