feat: infer default time format from navigator.language
This commit is contained in:
parent
f89b8c3b52
commit
e55ffccd21
3 changed files with 53 additions and 3 deletions
|
|
@ -8,6 +8,7 @@ import {
|
||||||
formatShortDateTime,
|
formatShortDateTime,
|
||||||
formatTime,
|
formatTime,
|
||||||
formatTimeAgo,
|
formatTimeAgo,
|
||||||
|
inferDateTimePreferences,
|
||||||
} from "../datetime";
|
} from "../datetime";
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
@ -89,6 +90,14 @@ describe("datetime formatters", () => {
|
||||||
expect(formatTime(sampleDate, { locale: "en-US", timeZone: "UTC", timeFormat })).toBe(expected);
|
expect(formatTime(sampleDate, { locale: "en-US", timeZone: "UTC", timeFormat })).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
["en-US", { dateFormat: "MM/DD/YYYY", timeFormat: "12h" }],
|
||||||
|
["en-GB", { dateFormat: "DD/MM/YYYY", timeFormat: "24h" }],
|
||||||
|
["ja-JP", { dateFormat: "YYYY/MM/DD", timeFormat: "24h" }],
|
||||||
|
] as const)("infers date and time preferences for %s", (locale, expected) => {
|
||||||
|
expect(inferDateTimePreferences(locale)).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
test("formats combined values with custom date and time preferences", () => {
|
test("formats combined values with custom date and time preferences", () => {
|
||||||
expect(
|
expect(
|
||||||
formatDateTime(sampleDate, {
|
formatDateTime(sampleDate, {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ export const TIME_FORMATS = ["12h", "24h"] as const;
|
||||||
export type TimeFormatPreference = (typeof TIME_FORMATS)[number];
|
export type TimeFormatPreference = (typeof TIME_FORMATS)[number];
|
||||||
export const DEFAULT_TIME_FORMAT: TimeFormatPreference = "12h";
|
export const DEFAULT_TIME_FORMAT: TimeFormatPreference = "12h";
|
||||||
|
|
||||||
|
const BROWSER_PREFERENCE_SAMPLE_DATE = new Date(Date.UTC(2006, 0, 2, 15, 4, 5));
|
||||||
|
|
||||||
const DATE_PART_ORDERS = {
|
const DATE_PART_ORDERS = {
|
||||||
"MM/DD/YYYY": ["month", "day", "year"],
|
"MM/DD/YYYY": ["month", "day", "year"],
|
||||||
"DD/MM/YYYY": ["day", "month", "year"],
|
"DD/MM/YYYY": ["day", "month", "year"],
|
||||||
|
|
@ -109,6 +111,43 @@ function formatConfiguredTime(date: Date, options: DateFormatOptions) {
|
||||||
}).format(date);
|
}).format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function inferDateTimePreferences(locale?: string) {
|
||||||
|
const dateOrder = getDateTimeFormat(locale, undefined, {
|
||||||
|
month: "numeric",
|
||||||
|
day: "numeric",
|
||||||
|
year: "numeric",
|
||||||
|
})
|
||||||
|
.formatToParts(BROWSER_PREFERENCE_SAMPLE_DATE)
|
||||||
|
.flatMap((part) => {
|
||||||
|
if (part.type === "year" || part.type === "month" || part.type === "day") {
|
||||||
|
return [part.type];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
})
|
||||||
|
.join("/");
|
||||||
|
|
||||||
|
let dateFormat = DEFAULT_DATE_FORMAT;
|
||||||
|
|
||||||
|
if (dateOrder === "day/month/year") {
|
||||||
|
dateFormat = "DD/MM/YYYY";
|
||||||
|
} else if (dateOrder === "year/month/day") {
|
||||||
|
dateFormat = "YYYY/MM/DD";
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeFormat: TimeFormatPreference = "12h";
|
||||||
|
const hour12 = getDateTimeFormat(locale, undefined, { hour: "numeric" }).resolvedOptions().hour12;
|
||||||
|
|
||||||
|
if (hour12 === false) {
|
||||||
|
timeFormat = "24h";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dateFormat,
|
||||||
|
timeFormat,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 1/10/2026, 2:30 PM
|
// 1/10/2026, 2:30 PM
|
||||||
export function formatDateTime(date: DateInput, options: DateFormatOptions = {}): string {
|
export function formatDateTime(date: DateInput, options: DateFormatOptions = {}): string {
|
||||||
return formatValidDate(
|
return formatValidDate(
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import { useState } from "react";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
import { normalizeUsername } from "~/lib/username";
|
import { normalizeUsername } from "~/lib/username";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { DEFAULT_DATE_FORMAT, DEFAULT_TIME_FORMAT } from "~/client/lib/datetime";
|
import { inferDateTimePreferences } from "~/client/lib/datetime";
|
||||||
|
|
||||||
export const clientMiddleware = [authMiddleware];
|
export const clientMiddleware = [authMiddleware];
|
||||||
|
|
||||||
|
|
@ -59,10 +59,12 @@ export function OnboardingPage() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { dateFormat, timeFormat } = inferDateTimePreferences(navigator.language);
|
||||||
|
|
||||||
const { data, error } = await authClient.signUp.email({
|
const { data, error } = await authClient.signUp.email({
|
||||||
username: normalizeUsername(values.username),
|
username: normalizeUsername(values.username),
|
||||||
dateFormat: DEFAULT_DATE_FORMAT,
|
dateFormat,
|
||||||
timeFormat: DEFAULT_TIME_FORMAT,
|
timeFormat,
|
||||||
password: values.password,
|
password: values.password,
|
||||||
email: values.email.toLowerCase().trim(),
|
email: values.email.toLowerCase().trim(),
|
||||||
name: values.username,
|
name: values.username,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue