Skip to content

Commit

Permalink
feat(legacy): InputTime & InputDateTime support AM / PM formats
Browse files Browse the repository at this point in the history
  • Loading branch information
nsbarsukov committed Oct 23, 2024
1 parent d178edf commit af255ba
Show file tree
Hide file tree
Showing 37 changed files with 418 additions and 129 deletions.
75 changes: 64 additions & 11 deletions projects/cdk/date-time/test/time.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,36 @@ describe('TuiTime', () => {
expect(time.seconds).toBe(0);
expect(time.ms).toBe(888);
});

describe('mode with AM / PM', () => {
(
[
['12:00 AM', {hours: 0, minutes: 0}],
['12:34 AM', {hours: 0, minutes: 34}],
['12:59 AM', {hours: 0, minutes: 59}],
['12:59 AM', {hours: 0, minutes: 59}],
['01:00 AM', {hours: 1, minutes: 0}],
['11:00 AM', {hours: 11, minutes: 0}],
['11:59 AM', {hours: 11, minutes: 59}],
['12:00 PM', {hours: 12, minutes: 0}],
['12:01 PM', {hours: 12, minutes: 1}],
['12:59 PM', {hours: 12, minutes: 59}],
['01:00 PM', {hours: 13, minutes: 0}],
['11:00 PM', {hours: 23, minutes: 0}],
['11:59 PM', {hours: 23, minutes: 59}],
['04:59', {hours: 4, minutes: 59}],
] as const
).forEach(([timeString, {hours, minutes}]) => {
it(`from ${timeString}`, () => {
const time = TuiTime.fromString(timeString);

expect(time.hours).toBe(hours);
expect(time.minutes).toBe(minutes);
expect(time.seconds).toBe(0);
expect(time.ms).toBe(0);
});
});
});
});

describe('current', () => {
Expand Down Expand Up @@ -334,22 +364,45 @@ describe('TuiTime', () => {
});
});

it('stringify', () => {
const time = new TuiTime(6, 36, 1, 1);
describe('toString(mode) method', () => {
it('without mode parameter', () => {
const time = new TuiTime(6, 36, 1, 1);

expect(time.toString()).toBe('06:36:01.001');
});
expect(time.toString()).toBe('06:36:01.001');
});

it('stringify and fill zeros for seconds', () => {
const time = new TuiTime(6, 36, 0, 0);
it('stringify and fill zeros for seconds', () => {
const time = new TuiTime(6, 36, 0, 0);

expect(time.toString('HH:MM:SS')).toBe('06:36:00');
});
expect(time.toString('HH:MM:SS')).toBe('06:36:00');
});

it('stringify and fill zeros for seconds and ms', () => {
const time = new TuiTime(6, 36, 0, 0);
it('stringify and fill zeros for seconds and ms', () => {
const time = new TuiTime(6, 36, 0, 0);

expect(time.toString('HH:MM:SS.MSS')).toBe('06:36:00.000');
expect(time.toString('HH:MM:SS.MSS')).toBe('06:36:00.000');
});

describe('HH:MM AA', () => {
(
[
[new TuiTime(0, 0), '12:00 AM'],
[new TuiTime(0, 30), '12:30 AM'],
[new TuiTime(0, 59), '12:59 AM'],
[new TuiTime(1, 1), '01:01 AM'],
[new TuiTime(11, 11), '11:11 AM'],
[new TuiTime(11, 59), '11:59 AM'],
[new TuiTime(12, 0), '12:00 PM'],
[new TuiTime(13, 0), '01:00 PM'],
[new TuiTime(16, 0), '04:00 PM'],
[new TuiTime(23, 59), '11:59 PM'],
] as const
).forEach(([time, timeString]) => {
it(`{hours: ${time.hours}, minutes: ${time.minutes}} => ${timeString}`, () => {
expect(time.toString('HH:MM AA')).toBe(timeString);
});
});
});
});

describe('valueOf returns', () => {
Expand Down
37 changes: 34 additions & 3 deletions projects/cdk/date-time/time.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="@taiga-ui/tsconfig/ng-dev-mode" />

import {CHAR_NO_BREAK_SPACE} from '@taiga-ui/cdk/constants';
import {tuiInRange} from '@taiga-ui/cdk/utils/math';

import {
Expand Down Expand Up @@ -113,7 +114,7 @@ export class TuiTime implements TuiTimeLike {
* Parses string into TuiTime object
*/
public static fromString(time: string): TuiTime {
const hours = Number(time.slice(0, 2));
const hours = this.parseHours(time);
const minutes = Number(time.slice(3, 5)) || 0;
const seconds = Number(time.slice(6, 8)) || 0;
const ms = Number(time.slice(9, 12)) || 0;
Expand All @@ -134,6 +135,22 @@ export class TuiTime implements TuiTimeLike {
);
}

private static parseHours(time: string): number {
const hours = Number(time.slice(0, 2));
const [meridiem = ''] =
/[ap]m/.exec(time.toLowerCase().replaceAll(/\W/g, '')) || [];

if (!meridiem) {
return hours;
}

if (hours === 12) {
return meridiem === 'am' ? 0 : 12;
}

return meridiem === 'pm' ? hours + 12 : hours;
}

/**
* Shifts time by hours and minutes
*/
Expand Down Expand Up @@ -168,11 +185,15 @@ export class TuiTime implements TuiTimeLike {
const needAddMs = mode === 'HH:MM:SS.MSS' || (!mode && this.ms > 0);
const needAddSeconds =
needAddMs || mode === 'HH:MM:SS' || (!mode && this.seconds > 0);
const hhMm = `${this.formatTime(this.hours)}:${this.formatTime(this.minutes)}`;
const {hours = this.hours, meridiem = ''} = mode?.includes('AA')
? this.toTwelveHour(this.hours)
: {};
const hhMm = `${this.formatTime(hours)}:${this.formatTime(this.minutes)}`;
const ss = needAddSeconds ? `:${this.formatTime(this.seconds)}` : '';
const mss = needAddMs ? `.${this.formatTime(this.ms, 3)}` : '';
const aa = meridiem && `${CHAR_NO_BREAK_SPACE}${meridiem}`;

return `${hhMm}${ss}${mss}`;
return `${hhMm}${ss}${mss}${aa}`;
}

public valueOf(): number {
Expand Down Expand Up @@ -203,4 +224,14 @@ export class TuiTime implements TuiTimeLike {
private formatTime(time: number, digits = 2): string {
return String(time).padStart(digits, '0');
}

private toTwelveHour(hours: number): {hours: number; meridiem: string} {
const meridiem = hours >= 12 ? 'PM' : 'AM';

if (hours === 0 || hours === 12) {
return {meridiem, hours: 12};
}

return {meridiem, hours: hours % 12};
}
}
8 changes: 7 additions & 1 deletion projects/cdk/date-time/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
* * YMD - yyyy.mm.dd
*/
export type TuiDateMode = 'DMY' | 'MDY' | 'YMD';
export type TuiTimeMode = 'HH:MM:SS.MSS' | 'HH:MM:SS' | 'HH:MM';
export type TuiTimeMode =
| 'HH:MM AA'
| 'HH:MM:SS AA'
| 'HH:MM:SS.MSS AA'
| 'HH:MM:SS.MSS'
| 'HH:MM:SS'
| 'HH:MM';

/**
* Optionally has year and/or month and/or day
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,10 @@ test.describe('InputDateTime', () => {
await expect(inputDateTime.host).toHaveScreenshot('03-timeMode=HH:MM.SS.png');

await timeModeSelect.textfield.click();
await timeModeSelect.selectOptions([2]);
await timeModeSelect.selectOptions([4]);

await expect(timeModeSelect.textfield).toHaveValue('HH:MM:SS.MSS');

await inputDateTime.textfield.focus();

await expect(inputDateTime.host).toHaveScreenshot(
Expand All @@ -196,6 +199,27 @@ test.describe('InputDateTime', () => {

await expect(inputDateTime.textfield).toHaveValue('07.06.2024, 23:59:00.000');
});

test.describe('AM / PM', () => {
test.beforeEach(async ({page}) => {
await tuiGoto(page, `${DemoRoute.InputDateTime}/API?timeMode=HH:MM%20AA`);
await inputDateTime.textfield.pressSequentially('2092020');

await expect(inputDateTime.textfield).toHaveValue('20.09.2020');
});

test('330a => 03:30 AM', async () => {
await inputDateTime.textfield.pressSequentially('330a');

await expect(inputDateTime.textfield).toHaveValue('20.09.2020, 03:30 AM');
});

test('330p => 03:30 PM', async () => {
await inputDateTime.textfield.pressSequentially('330p');

await expect(inputDateTime.textfield).toHaveValue('20.09.2020, 03:30 PM');
});
});
});

test.describe('invalid date', () => {
Expand Down
Loading

0 comments on commit af255ba

Please sign in to comment.