Skip to content

Commit f5737f6

Browse files
committed
fix(calendar): add DayDigitPipe to handle non-numeric day characters in locales like zh-CN
- Introduced DayDigitPipe to strip non-numeric characters from localized day strings. - Updated `IgxDaysViewComponent` to use the new pipe in the day formatting logic. - Added a test to verify correct formatting for zh-CN locale.
1 parent 6bd8cf0 commit f5737f6

File tree

5 files changed

+60
-4
lines changed

5 files changed

+60
-4
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Pipe, PipeTransform } from '@angular/core';
2+
import { IFormattingViews } from "igniteui-angular/calendar";
3+
4+
@Pipe({ name: 'dayDigit' })
5+
export class DayDigitPipe implements PipeTransform {
6+
public transform(value: string, formatViews: IFormattingViews): string {
7+
if (!value) {
8+
return '';
9+
}
10+
11+
// strip non-numeric characters that might have been added by the locale formatter (e.g., "25日" -> "25").
12+
if (formatViews.day) {
13+
// Use regex to extract the numeric day value.
14+
// This handles locales that include non-numeric characters (e.g. '25日' in zh-CN).
15+
// match(/\d+/) is preferred over parseInt() as it robustly finds the digits regardless
16+
// of their position (prefix/suffix) in the localized string.
17+
const match = value.match(/\d+/);
18+
return match ? match[0] : value;
19+
}
20+
21+
return value;
22+
}
23+
}

projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
(mouseEnter)="changePreviewRange(day.native)"
8282
(mouseLeave)="clearPreviewRange()"
8383
>
84-
{{ formattedDate(day.native) }}
84+
{{ formattedDate(day.native) | dayDigit:formatViews }}
8585
</igx-day-item>
8686
}
8787
</div>

projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ScrollDirection } from "../calendar";
77
import { KeyboardNavigationService } from '../calendar.services';
88
import { CalendarDay } from 'igniteui-angular/core';
99
import { UIInteractions } from '../../../../test-utils/ui-interactions.spec';
10+
import { DayDigitPipe } from "igniteui-angular/calendar/src/calendar/day-digit.pipe";
1011

1112
const TODAY = new Date(2024, 6, 12);
1213

@@ -114,6 +115,37 @@ describe("Days View Component", () => {
114115
}
115116
});
116117

118+
it("should format date correctly for zh-CN locale programmatically vs template pipe", () => {
119+
const fixture = TestBed.createComponent(InitDaysViewComponent);
120+
const daysView = fixture.componentInstance.instance;
121+
const pipe = new DayDigitPipe();
122+
const date = new Date(2020, 10, 25); // Nov 25
123+
124+
// Initialize component
125+
daysView.formatViews = { day: true, month: true, year: true };
126+
fixture.detectChanges();
127+
128+
// Mock the formatter behavior
129+
// Simulate a locale (like zh-CN) that adds a suffix to the day number.
130+
// Cast to 'any' to overwrite the protected 'formatterDay' property used by formattedDate()
131+
(daysView as any).formatterDay = {
132+
format: () => '25日',
133+
} as Intl.DateTimeFormat;
134+
135+
// 1. Verify Programmatic Access (formattedDate method)
136+
// Should return the raw formatted string from the formatter (with suffix)
137+
const programmaticResult = daysView.formattedDate(date);
138+
expect(programmaticResult).toBe('25日', 'Programmatic API should return the full locale string (including suffix, in this case 日)');
139+
140+
// 2. Verify Pipe Logic
141+
// The pipe takes the formatted string "25日" and strips non-digits to return "25"
142+
const pipeResult = pipe.transform(programmaticResult, daysView.formatViews);
143+
expect(pipeResult).toBe('25', 'Pipe should strip non-numeric characters from the input string');
144+
145+
// 3. Confirm the difference implies the pipe did its job
146+
expect(programmaticResult).not.toEqual(pipeResult);
147+
});
148+
117149
describe("Keyboard navigation", () => {
118150
let fixture: ComponentFixture<InitDaysViewComponent>;
119151
let el: HTMLElement;

projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
import { IgxCalendarBaseDirective } from '../calendar-base';
3838
import { IViewChangingEventArgs } from './days-view.interface';
3939
import { KeyboardNavigationService } from '../calendar.services';
40+
import { DayDigitPipe } from "igniteui-angular/calendar/src/calendar/day-digit.pipe";
4041

4142
let NEXT_ID = 0;
4243

@@ -52,7 +53,7 @@ let NEXT_ID = 0;
5253
selector: 'igx-days-view',
5354
templateUrl: 'days-view.component.html',
5455
changeDetection: ChangeDetectionStrategy.OnPush,
55-
imports: [IgxDayItemComponent, TitleCasePipe]
56+
imports: [IgxDayItemComponent, TitleCasePipe, DayDigitPipe]
5657
})
5758
export class IgxDaysViewComponent extends IgxCalendarBaseDirective implements AfterContentChecked {
5859
protected el = inject(ElementRef);
@@ -446,8 +447,7 @@ export class IgxDaysViewComponent extends IgxCalendarBaseDirective implements Af
446447
*/
447448
public formattedDate(value: Date): string {
448449
if (this.formatViews.day) {
449-
const dateParts = this.formatterDay.formatToParts(value);
450-
return dateParts.find(part => part.type === 'day')?.value ?? value.getDate().toString();
450+
return this.formatterDay.format(value);
451451
}
452452

453453
return `${value.getDate()}`;

src/app/calendar/calendar.sample.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
[showWeekNumbers]="properties.showWeekNumbers"
1515
[hasHeader]="!properties.hideHeader"
1616
[formatOptions]="formatOptions"
17+
[formatViews]="formatViews"
1718
[disabledDates]="disabledDates"
1819
[specialDates]="specialDates"
1920
(selected)="onSelection($event)"

0 commit comments

Comments
 (0)