forked from baron/baron-sso
offline 스코프 제거, rp_claims 값 표준화
This commit is contained in:
137
devfront/src/features/clients/rpClaimDateTime.ts
Normal file
137
devfront/src/features/clients/rpClaimDateTime.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
export type RPClaimDateTimeValueType = "date" | "datetime";
|
||||
|
||||
export const FALLBACK_TIME_ZONE = "UTC";
|
||||
|
||||
export function getBrowserTimeZone(): string {
|
||||
return Intl.DateTimeFormat().resolvedOptions().timeZone || FALLBACK_TIME_ZONE;
|
||||
}
|
||||
|
||||
export function getSupportedTimeZones(currentTimeZone = getBrowserTimeZone()) {
|
||||
const supported =
|
||||
typeof Intl.supportedValuesOf === "function"
|
||||
? Intl.supportedValuesOf("timeZone")
|
||||
: [];
|
||||
return Array.from(
|
||||
new Set([currentTimeZone, FALLBACK_TIME_ZONE, ...supported]),
|
||||
);
|
||||
}
|
||||
|
||||
function getTimeZoneOffsetMs(date: Date, timeZone: string) {
|
||||
const parts = new Intl.DateTimeFormat("en-US", {
|
||||
timeZone,
|
||||
hour12: false,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}).formatToParts(date);
|
||||
const values = Object.fromEntries(
|
||||
parts
|
||||
.filter((part) => part.type !== "literal")
|
||||
.map((part) => [part.type, part.value]),
|
||||
);
|
||||
const hour = values.hour === "24" ? "00" : values.hour;
|
||||
const asUTC = Date.UTC(
|
||||
Number(values.year),
|
||||
Number(values.month) - 1,
|
||||
Number(values.day),
|
||||
Number(hour),
|
||||
Number(values.minute),
|
||||
Number(values.second),
|
||||
);
|
||||
return asUTC - date.getTime();
|
||||
}
|
||||
|
||||
function zonedDateTimeToUnixSeconds(
|
||||
year: number,
|
||||
month: number,
|
||||
day: number,
|
||||
hour: number,
|
||||
minute: number,
|
||||
timeZone: string,
|
||||
) {
|
||||
const utcGuess = Date.UTC(year, month - 1, day, hour, minute, 0);
|
||||
let instant = utcGuess - getTimeZoneOffsetMs(new Date(utcGuess), timeZone);
|
||||
const corrected = utcGuess - getTimeZoneOffsetMs(new Date(instant), timeZone);
|
||||
if (corrected !== instant) {
|
||||
instant = corrected;
|
||||
}
|
||||
return Math.trunc(instant / 1000);
|
||||
}
|
||||
|
||||
export function dateTimeInputToUnixSeconds(
|
||||
value: string,
|
||||
valueType: RPClaimDateTimeValueType,
|
||||
timeZone: string,
|
||||
): number | null {
|
||||
const trimmed = value.trim();
|
||||
const match =
|
||||
valueType === "date"
|
||||
? /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed)
|
||||
: /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/.exec(trimmed);
|
||||
if (!match) return null;
|
||||
|
||||
const year = Number(match[1]);
|
||||
const month = Number(match[2]);
|
||||
const day = Number(match[3]);
|
||||
const hour = valueType === "datetime" ? Number(match[4]) : 0;
|
||||
const minute = valueType === "datetime" ? Number(match[5]) : 0;
|
||||
const unixSeconds = zonedDateTimeToUnixSeconds(
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hour,
|
||||
minute,
|
||||
timeZone || FALLBACK_TIME_ZONE,
|
||||
);
|
||||
return Number.isFinite(unixSeconds) ? unixSeconds : null;
|
||||
}
|
||||
|
||||
export function unixSecondsToDateTimeInput(
|
||||
value: number,
|
||||
valueType: RPClaimDateTimeValueType,
|
||||
timeZone: string,
|
||||
) {
|
||||
const date = new Date(value * 1000);
|
||||
if (Number.isNaN(date.getTime())) return "";
|
||||
const parts = new Intl.DateTimeFormat("en-CA", {
|
||||
timeZone: timeZone || FALLBACK_TIME_ZONE,
|
||||
hour12: false,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}).formatToParts(date);
|
||||
const values = Object.fromEntries(
|
||||
parts
|
||||
.filter((part) => part.type !== "literal")
|
||||
.map((part) => [part.type, part.value]),
|
||||
);
|
||||
const hour = values.hour === "24" ? "00" : values.hour;
|
||||
const dateText = `${values.year}-${values.month}-${values.day}`;
|
||||
if (valueType === "date") return dateText;
|
||||
return `${dateText}T${hour}:${values.minute}`;
|
||||
}
|
||||
|
||||
export function claimDateTimeValueToInputString(
|
||||
value: unknown,
|
||||
fallback: string,
|
||||
valueType: RPClaimDateTimeValueType,
|
||||
timeZone: string,
|
||||
) {
|
||||
if (typeof value === "number" && Number.isFinite(value)) {
|
||||
return unixSecondsToDateTimeInput(value, valueType, timeZone);
|
||||
}
|
||||
if (typeof value === "string" && /^-?\d+$/.test(value.trim())) {
|
||||
return unixSecondsToDateTimeInput(
|
||||
Number(value.trim()),
|
||||
valueType,
|
||||
timeZone,
|
||||
);
|
||||
}
|
||||
const text = typeof value === "string" ? value : fallback;
|
||||
return valueType === "date" ? text.slice(0, 10) : text.slice(0, 16);
|
||||
}
|
||||
Reference in New Issue
Block a user