130 lines
4.6 KiB
TypeScript
130 lines
4.6 KiB
TypeScript
export type ChinaWorkdayType = 'workday' | 'weekend' | 'holiday' | 'makeup_workday';
|
|
|
|
export interface ChinaWorkdayInfo {
|
|
date: string;
|
|
type: ChinaWorkdayType;
|
|
label: string;
|
|
isWorkday: boolean;
|
|
source: 'official_2026' | 'weekend_fallback';
|
|
}
|
|
|
|
type OverrideInfo = {
|
|
type: Extract<ChinaWorkdayType, 'holiday' | 'makeup_workday'>;
|
|
label: string;
|
|
};
|
|
|
|
export const CHINA_HOLIDAY_SOURCE_2026 = {
|
|
name: '国务院办公厅关于2026年部分节假日安排的通知',
|
|
url: 'https://www.gov.cn/zhengce/zhengceku/202511/content_7047091.htm',
|
|
} as const;
|
|
|
|
const OFFICIAL_2026_OVERRIDES: Record<string, OverrideInfo> = {
|
|
'2026-01-01': { type: 'holiday', label: '元旦' },
|
|
'2026-01-02': { type: 'holiday', label: '元旦' },
|
|
'2026-01-03': { type: 'holiday', label: '元旦' },
|
|
'2026-01-04': { type: 'makeup_workday', label: '元旦调休上班' },
|
|
|
|
'2026-02-14': { type: 'makeup_workday', label: '春节调休上班' },
|
|
'2026-02-15': { type: 'holiday', label: '春节' },
|
|
'2026-02-16': { type: 'holiday', label: '春节' },
|
|
'2026-02-17': { type: 'holiday', label: '春节' },
|
|
'2026-02-18': { type: 'holiday', label: '春节' },
|
|
'2026-02-19': { type: 'holiday', label: '春节' },
|
|
'2026-02-20': { type: 'holiday', label: '春节' },
|
|
'2026-02-21': { type: 'holiday', label: '春节' },
|
|
'2026-02-22': { type: 'holiday', label: '春节' },
|
|
'2026-02-23': { type: 'holiday', label: '春节' },
|
|
'2026-02-28': { type: 'makeup_workday', label: '春节调休上班' },
|
|
|
|
'2026-04-04': { type: 'holiday', label: '清明节' },
|
|
'2026-04-05': { type: 'holiday', label: '清明节' },
|
|
'2026-04-06': { type: 'holiday', label: '清明节' },
|
|
|
|
'2026-05-01': { type: 'holiday', label: '劳动节' },
|
|
'2026-05-02': { type: 'holiday', label: '劳动节' },
|
|
'2026-05-03': { type: 'holiday', label: '劳动节' },
|
|
'2026-05-04': { type: 'holiday', label: '劳动节' },
|
|
'2026-05-05': { type: 'holiday', label: '劳动节' },
|
|
'2026-05-09': { type: 'makeup_workday', label: '劳动节调休上班' },
|
|
|
|
'2026-06-19': { type: 'holiday', label: '端午节' },
|
|
'2026-06-20': { type: 'holiday', label: '端午节' },
|
|
'2026-06-21': { type: 'holiday', label: '端午节' },
|
|
|
|
'2026-09-20': { type: 'makeup_workday', label: '国庆节调休上班' },
|
|
'2026-09-25': { type: 'holiday', label: '中秋节' },
|
|
'2026-09-26': { type: 'holiday', label: '中秋节' },
|
|
'2026-09-27': { type: 'holiday', label: '中秋节' },
|
|
|
|
'2026-10-01': { type: 'holiday', label: '国庆节' },
|
|
'2026-10-02': { type: 'holiday', label: '国庆节' },
|
|
'2026-10-03': { type: 'holiday', label: '国庆节' },
|
|
'2026-10-04': { type: 'holiday', label: '国庆节' },
|
|
'2026-10-05': { type: 'holiday', label: '国庆节' },
|
|
'2026-10-06': { type: 'holiday', label: '国庆节' },
|
|
'2026-10-07': { type: 'holiday', label: '国庆节' },
|
|
'2026-10-10': { type: 'makeup_workday', label: '国庆节调休上班' },
|
|
};
|
|
|
|
const DATE_KEY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})/;
|
|
|
|
export function getDateKey(value: string | Date): string {
|
|
if (value instanceof Date) {
|
|
return formatDateKey(value);
|
|
}
|
|
const matched = value.match(DATE_KEY_PATTERN);
|
|
if (matched) return `${matched[1]}-${matched[2]}-${matched[3]}`;
|
|
const parsed = new Date(value);
|
|
return Number.isNaN(parsed.getTime()) ? '' : formatDateKey(parsed);
|
|
}
|
|
|
|
export function getChinaWorkdayInfo(value: string | Date): ChinaWorkdayInfo {
|
|
const date = getDateKey(value);
|
|
const override = OFFICIAL_2026_OVERRIDES[date];
|
|
if (override) {
|
|
return {
|
|
date,
|
|
type: override.type,
|
|
label: override.label,
|
|
isWorkday: override.type === 'makeup_workday',
|
|
source: 'official_2026',
|
|
};
|
|
}
|
|
|
|
const parsed = parseDateKey(date);
|
|
const weekend = parsed ? isWeekend(parsed) : false;
|
|
return {
|
|
date,
|
|
type: weekend ? 'weekend' : 'workday',
|
|
label: weekend ? '周末' : '工作日',
|
|
isWorkday: !weekend,
|
|
source: 'weekend_fallback',
|
|
};
|
|
}
|
|
|
|
export function isChinaWorkday(value: string | Date): boolean {
|
|
return getChinaWorkdayInfo(value).isWorkday;
|
|
}
|
|
|
|
function formatDateKey(date: Date): string {
|
|
const yyyy = date.getFullYear();
|
|
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
|
const dd = String(date.getDate()).padStart(2, '0');
|
|
return `${yyyy}-${mm}-${dd}`;
|
|
}
|
|
|
|
function parseDateKey(dateKey: string): Date | null {
|
|
const matched = dateKey.match(DATE_KEY_PATTERN);
|
|
if (!matched) return null;
|
|
const year = Number(matched[1]);
|
|
const month = Number(matched[2]);
|
|
const day = Number(matched[3]);
|
|
if (!year || !month || !day) return null;
|
|
return new Date(year, month - 1, day);
|
|
}
|
|
|
|
function isWeekend(date: Date): boolean {
|
|
const day = date.getDay();
|
|
return day === 0 || day === 6;
|
|
}
|