TypeScript 中日期与时间的高级操作:从类型化到复杂运算
\n\n处理日期和时间是 Web 开发中最常见也最棘手的任务之一。在高级层面,简单地使用 new Date() 已经不够了。TypeScript 提供了强大的类型系统,而现代库(Luxon、Date-fns)则允许编写安全、可预测且高性能的代码。本文将深入探讨高级技术:严格类型化、时区处理、复杂格式化与解析,以及创建自定义工具函数。
1. 日期的严格类型化:自定义类型与 Branded Types
\n\nTypeScript 中的 Date 类型无法防止传入无效值。例如,一个期望接收出生日期的函数可能接收到当前时间戳。解决方案是使用 Branded Types(品牌类型)或 Nominal Typing(名义类型化)。
// 出生日期的 Branded Type\ntype BirthDate = Date & { __brand: 'BirthDate' };\n\nfunction createBirthDate(date: Date): BirthDate {\n // 验证:日期不能是未来的\n if (date.getTime() > Date.now()) {\n throw new Error('出生日期不能是未来的');\n }\n return date as BirthDate;\n}\n\nfunction calculateAge(birthDate: BirthDate): number {\n const diff = Date.now() - birthDate.getTime();\n return Math.floor(diff / (1000 * 60 * 60 * 24 * 365.25));\n}\n\n// 使用\nconst rawDate = new Date('1990-05-15');\nconst birth = createBirthDate(rawDate);\nconsole.log(calculateAge(birth)); // 正常\n\n// 错误:类型 'Date' 的参数不能赋值给类型 'BirthDate' 的参数\n// console.log(calculateAge(new Date())); \n\n\n这种方法确保只有经过验证的出生日期才能传入 calculateAge 函数。对于更复杂的场景,可以创建时间戳(UnixTimestamp)、时间间隔(TimeInterval)等类型。
2. 使用 Intl.DateTimeFormat 进行高级格式化与解析
\n\n内置的 Intl.DateTimeFormat 对象允许在不使用第三方库的情况下灵活格式化日期。在高级层面,我们可以创建可复用的格式化器并处理复杂的区域设置。
// 创建带选项的格式化器\nconst formatter = new Intl.DateTimeFormat('ru-RU', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n weekday: 'long',\n hour: '2-digit',\n minute: '2-digit',\n timeZone: 'Europe/Moscow',\n});\n\n// 格式化\nconsole.log(formatter.format(new Date())); \n// "пятница, 25 октября 2024 г., 14:30"\n\n// 解析(自定义工具函数)\nfunction parseLocalizedDate(dateStr: string, locale: string): Date | null {\n const parts = dateStr.match(/(\\d{4})-(\\d{2})-(\\d{2})/);\n if (!parts) return null;\n \n const [, year, month, day] = parts;\n return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));\n}\n\n\n提示: 为了性能,请一次性创建格式化器并重复使用。避免每次调用都创建新实例。
\n\n3. 处理时区与 Luxon 库
\n\n内置的 Date 在处理时区方面表现不佳。Luxon 是一个来自 Moment.js 创建者的现代库,与 TypeScript 完美集成。
import { DateTime, Settings } from 'luxon';\n\n// 设置默认时区\nSettings.defaultZone = 'Europe/Moscow';\n\n// 显式指定时区创建日期\nconst meeting = DateTime.fromISO('2024-11-01T15:00:00', { zone: 'America/New_York' });\nconsole.log(meeting.toISO()); // 2024-11-01T15:00:00.000-04:00\n\n// 转换为其他时区\nconst moscowTime = meeting.setZone('Europe/Moscow');\nconsole.log(moscowTime.toFormat('dd.MM.yyyy HH:mm')); // 01.11.2024 22:00\n\n// 处理时间间隔\nconst start = DateTi