Skip to content

Commit 89e9e5b

Browse files
fix: iso8601 week start and use intl.locale when possible for week start (#8815)
* fix: iso8601 week start and use intl.locale when possible for week start * add fw override * fix lint * fix merge --------- Co-authored-by: Yihui Liao <[email protected]>
1 parent e5e2a72 commit 89e9e5b

File tree

3 files changed

+98
-3
lines changed

3 files changed

+98
-3
lines changed

packages/@internationalized/date/src/queries.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ export function endOfWeek(date: DateValue, locale: string, firstDayOfWeek?: DayO
223223
}
224224

225225
const cachedRegions = new Map<string, string>();
226+
const cachedWeekInfo = new Map<string, {firstDay: number}>();
226227

227228
function getRegion(locale: string): string | undefined {
228229
// If the Intl.Locale API is available, use it to get the region for the locale.
@@ -251,8 +252,47 @@ function getRegion(locale: string): string | undefined {
251252
function getWeekStart(locale: string): number {
252253
// TODO: use Intl.Locale for this once browsers support the weekInfo property
253254
// https://github.com/tc39/proposal-intl-locale-info
254-
let region = getRegion(locale);
255-
return region ? weekStartData[region] || 0 : 0;
255+
let weekInfo = cachedWeekInfo.get(locale);
256+
if (!weekInfo) {
257+
if (Intl.Locale) {
258+
// @ts-ignore
259+
let localeInst = new Intl.Locale(locale);
260+
if ('getWeekInfo' in localeInst) {
261+
// @ts-expect-error
262+
weekInfo = localeInst.getWeekInfo();
263+
if (weekInfo) {
264+
cachedWeekInfo.set(locale, weekInfo);
265+
return weekInfo.firstDay;
266+
}
267+
}
268+
}
269+
let region = getRegion(locale);
270+
if (locale.includes('-fw-')) {
271+
let day = locale.split('-fw-')[1];
272+
if (day === 'mon') {
273+
weekInfo = {firstDay: 1};
274+
} else if (day === 'tue') {
275+
weekInfo = {firstDay: 2};
276+
} else if (day === 'wed') {
277+
weekInfo = {firstDay: 3};
278+
} else if (day === 'thu') {
279+
weekInfo = {firstDay: 4};
280+
} else if (day === 'fri') {
281+
weekInfo = {firstDay: 5};
282+
} else if (day === 'sat') {
283+
weekInfo = {firstDay: 6};
284+
} else {
285+
weekInfo = {firstDay: 0};
286+
}
287+
} else if (locale.includes('u-ca-iso8601')) {
288+
weekInfo = {firstDay: 1};
289+
} else {
290+
weekInfo = {firstDay: region ? weekStartData[region] || 0 : 0};
291+
}
292+
cachedWeekInfo.set(locale, weekInfo);
293+
}
294+
295+
return weekInfo.firstDay;
256296
}
257297

258298
/** Returns the number of weeks in the given month and locale. */

packages/@internationalized/date/tests/queries.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ describe('queries', function () {
273273
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'fr-FR', 'sun')).toEqual(new CalendarDate(2021, 8, 1));
274274
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US', 'thu')).toEqual(new CalendarDate(2021, 7, 29));
275275
});
276+
277+
it('should return the start of the week in en-US-u-ca-iso8601', function () {
278+
// start of week is monday
279+
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US-u-ca-iso8601')).toEqual(new CalendarDate(2021, 8, 2));
280+
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'fr-FR-u-ca-iso8601')).toEqual(new CalendarDate(2021, 8, 2));
281+
282+
// override first day of week
283+
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US-u-ca-iso8601-fw-tue')).toEqual(new CalendarDate(2021, 8, 3));
284+
});
276285
});
277286

278287
describe('endOfWeek', function () {

packages/react-aria-components/stories/Calendar.stories.tsx

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,18 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {Button, Calendar, CalendarCell, CalendarGrid, CalendarStateContext, Heading, RangeCalendar} from 'react-aria-components';
13+
import {
14+
Button,
15+
Calendar,
16+
CalendarCell,
17+
CalendarGrid,
18+
CalendarProps,
19+
CalendarStateContext,
20+
DateValue,
21+
Heading,
22+
I18nProvider,
23+
RangeCalendar
24+
} from 'react-aria-components';
1425
import {CalendarDate, parseDate} from '@internationalized/date';
1526
import {Meta, StoryObj} from '@storybook/react';
1627
import React, {useContext} from 'react';
@@ -120,6 +131,41 @@ export const CalendarMultiMonth: CalendarStory = {
120131
}
121132
};
122133

134+
135+
interface CalendarFirstDayOfWeekExampleProps extends CalendarProps<DateValue> {
136+
locale: string
137+
}
138+
139+
export const CalendarFirstDayOfWeekExample: StoryObj<CalendarFirstDayOfWeekExampleProps> = {
140+
render: function Example(args) {
141+
return (
142+
<div>
143+
<I18nProvider locale={args.locale}>
144+
<Calendar style={{width: 220}}>
145+
<div style={{display: 'flex', alignItems: 'center'}}>
146+
<Button slot="previous">&lt;</Button>
147+
<Heading style={{flex: 1, textAlign: 'center'}} />
148+
<Button slot="next">&gt;</Button>
149+
</div>
150+
<CalendarGrid style={{width: '100%'}}>
151+
{date => <CalendarCell date={date} style={({isSelected, isOutsideMonth}) => ({display: isOutsideMonth ? 'none' : '', textAlign: 'center', cursor: 'default', background: isSelected ? 'blue' : ''})} />}
152+
</CalendarGrid>
153+
</Calendar>
154+
</I18nProvider>
155+
</div>
156+
);
157+
},
158+
args: {
159+
locale: 'en-US-u-ca-iso8601-fw-tue'
160+
},
161+
argTypes: {
162+
locale: {
163+
control: 'select',
164+
options: ['en-US-u-ca-iso8601-fw-tue', 'en-US-u-ca-iso8601', 'en-US', 'fr-FR-u-ca-iso8601-fw-tue', 'fr-FR-u-ca-iso8601', 'fr-FR']
165+
}
166+
}
167+
};
168+
123169
export const RangeCalendarExample: RangeCalendarStory = {
124170
render: () => (
125171
<RangeCalendar style={{width: 220}}>

0 commit comments

Comments
 (0)