| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- import { describe, expect, it } from "vitest";
- import { parseDateInputAsTimezone } from "@/lib/utils/date-input";
- describe("parseDateInputAsTimezone", () => {
- describe("date-only input (YYYY-MM-DD)", () => {
- it("should interpret date-only as end-of-day (23:59:59) in given timezone", () => {
- // Input: "2024-12-31" in Asia/Shanghai (UTC+8)
- // Expected: 2024-12-31 23:59:59 in Shanghai = 2024-12-31 15:59:59 UTC
- const result = parseDateInputAsTimezone("2024-12-31", "Asia/Shanghai");
- expect(result.getUTCFullYear()).toBe(2024);
- expect(result.getUTCMonth()).toBe(11); // December = 11
- expect(result.getUTCDate()).toBe(31);
- expect(result.getUTCHours()).toBe(15); // 23:59:59 Shanghai = 15:59:59 UTC
- expect(result.getUTCMinutes()).toBe(59);
- expect(result.getUTCSeconds()).toBe(59);
- });
- it("should handle UTC timezone correctly", () => {
- // Input: "2024-06-15" in UTC
- // Expected: 2024-06-15 23:59:59 UTC
- const result = parseDateInputAsTimezone("2024-06-15", "UTC");
- expect(result.getUTCFullYear()).toBe(2024);
- expect(result.getUTCMonth()).toBe(5); // June = 5
- expect(result.getUTCDate()).toBe(15);
- expect(result.getUTCHours()).toBe(23);
- expect(result.getUTCMinutes()).toBe(59);
- expect(result.getUTCSeconds()).toBe(59);
- });
- it("should handle negative offset timezone (America/New_York)", () => {
- // Input: "2024-07-04" in America/New_York (UTC-4 during DST)
- // Expected: 2024-07-04 23:59:59 in NY = 2024-07-05 03:59:59 UTC
- const result = parseDateInputAsTimezone("2024-07-04", "America/New_York");
- expect(result.getUTCFullYear()).toBe(2024);
- expect(result.getUTCMonth()).toBe(6); // July = 6
- expect(result.getUTCDate()).toBe(5); // Next day in UTC
- expect(result.getUTCHours()).toBe(3); // 23:59:59 NY (UTC-4) = 03:59:59 UTC next day
- expect(result.getUTCMinutes()).toBe(59);
- expect(result.getUTCSeconds()).toBe(59);
- });
- it("should handle date at year boundary", () => {
- // Input: "2024-01-01" in Asia/Tokyo (UTC+9)
- // Expected: 2024-01-01 23:59:59 in Tokyo = 2024-01-01 14:59:59 UTC
- const result = parseDateInputAsTimezone("2024-01-01", "Asia/Tokyo");
- expect(result.getUTCFullYear()).toBe(2024);
- expect(result.getUTCMonth()).toBe(0); // January = 0
- expect(result.getUTCDate()).toBe(1);
- expect(result.getUTCHours()).toBe(14); // 23:59:59 Tokyo (UTC+9) = 14:59:59 UTC
- });
- });
- describe("ISO datetime input", () => {
- it("should handle ISO datetime string", () => {
- // Input: "2024-12-31T10:30:00" in Asia/Shanghai
- // Expected: 2024-12-31 10:30:00 in Shanghai = 2024-12-31 02:30:00 UTC
- const result = parseDateInputAsTimezone("2024-12-31T10:30:00", "Asia/Shanghai");
- expect(result.getUTCFullYear()).toBe(2024);
- expect(result.getUTCMonth()).toBe(11);
- expect(result.getUTCDate()).toBe(31);
- expect(result.getUTCHours()).toBe(2); // 10:30 Shanghai = 02:30 UTC
- expect(result.getUTCMinutes()).toBe(30);
- });
- it("should handle ISO datetime with Z suffix - note: behavior depends on runtime TZ", () => {
- // NOTE: Z-suffixed input is not a typical use case for this function.
- // User input from date pickers typically doesn't include Z suffix.
- // When Z suffix is present, new Date() parses it as UTC, but fromZonedTime
- // reads the LOCAL time components (which depend on runtime timezone).
- //
- // For this reason, we recommend NOT using Z-suffixed input with this function.
- // This test documents the behavior for awareness, not for correctness assertion.
- const result = parseDateInputAsTimezone("2024-12-31T10:30:00Z", "Asia/Shanghai");
- // Just verify it doesn't throw and returns a valid date
- expect(result).toBeInstanceOf(Date);
- expect(Number.isNaN(result.getTime())).toBe(false);
- });
- });
- describe("error handling", () => {
- it("should throw for invalid date string", () => {
- expect(() => parseDateInputAsTimezone("invalid-date", "UTC")).toThrow(
- "Invalid date input: invalid-date"
- );
- });
- it("should throw for empty string", () => {
- expect(() => parseDateInputAsTimezone("", "UTC")).toThrow();
- });
- });
- describe("DST edge cases", () => {
- it("should handle DST transition date in spring (America/New_York)", () => {
- // March 10, 2024 is when DST starts in US (clocks spring forward at 2am)
- // Input: "2024-03-10" in America/New_York
- // Expected: 2024-03-10 23:59:59 in NY (UTC-4 after DST) = 2024-03-11 03:59:59 UTC
- const result = parseDateInputAsTimezone("2024-03-10", "America/New_York");
- expect(result.getUTCFullYear()).toBe(2024);
- expect(result.getUTCMonth()).toBe(2); // March = 2
- expect(result.getUTCDate()).toBe(11); // Next day in UTC
- expect(result.getUTCHours()).toBe(3); // UTC-4 offset after DST
- });
- it("should handle DST transition date in fall (America/New_York)", () => {
- // November 3, 2024 is when DST ends in US (clocks fall back at 2am)
- // Input: "2024-11-03" in America/New_York
- // Expected: 2024-11-03 23:59:59 in NY (UTC-5 after DST ends) = 2024-11-04 04:59:59 UTC
- const result = parseDateInputAsTimezone("2024-11-03", "America/New_York");
- expect(result.getUTCFullYear()).toBe(2024);
- expect(result.getUTCMonth()).toBe(10); // November = 10
- expect(result.getUTCDate()).toBe(4); // Next day in UTC
- expect(result.getUTCHours()).toBe(4); // UTC-5 offset after DST ends
- });
- });
- });
|