// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import { assertEquals, assertThrows } from "@std/assert"; import { FakeTime } from "@std/testing/time"; import { DateTimeFormatter } from "./_date_time_formatter.ts"; Deno.test("dateTimeFormatter.format()", () => { const cases = [ ["yyyy-MM-dd HH:mm:ss a", new Date(2020, 0, 1), "2020-01-01 00:00:00 AM"], [ "yyyy-MM-dd HH:mm:ss a", new Date(2020, 0, 1, 23, 59, 59), "2020-01-01 23:59:59 PM", ], [ "yyyy-MM-dd hh:mm:ss a", new Date(2020, 0, 1, 23, 59, 59), "2020-01-01 11:59:59 PM", ], ["yyyy-MM-dd a", new Date(2020, 0, 1), "2020-01-01 AM"], ["yyyy-MM-dd HH:mm:ss a", new Date(2020, 0, 1), "2020-01-01 00:00:00 AM"], ["yyyy-MM-dd hh:mm:ss a", new Date(2020, 0, 1), "2020-01-01 12:00:00 AM"], ["yyyy", new Date(2020, 0, 1), "2020"], ["MM", new Date(2020, 0, 1), "01"], ] as const; for (const [format, date, expected] of cases) { const formatter = new DateTimeFormatter(format); assertEquals(formatter.format(date), expected); } }); Deno.test("dateTimeFormatter.format() with empty format string returns empty string", () => { const format = ""; const formatter = new DateTimeFormatter(format); assertEquals( formatter.format(new Date(2020, 0, 1)), "", ); }); Deno.test("dateTimeFormatter.parse()", () => { const format = "yyyy-MM-dd"; const formatter = new DateTimeFormatter(format); assertEquals(formatter.parse("2020-01-01"), new Date(2020, 0, 1)); }); Deno.test("dateTimeFormatter.formatToParts()", () => { const format = "yyyy-MM-dd"; const formatter = new DateTimeFormatter(format); assertEquals(formatter.formatToParts("2020-01-01"), [ { type: "year", value: "2020" }, { type: "literal", value: "-" }, { type: "month", value: "01" }, { type: "literal", value: "-" }, { type: "day", value: "01" }, ]); }); Deno.test("dateTimeFormatter.formatToParts() throws on an empty string", () => { const format = "yyyy-MM-dd"; const formatter = new DateTimeFormatter(format); assertThrows( () => formatter.formatToParts(""), Error, "Cannot format value: The value is not valid for part { year undefined } ", ); }); Deno.test("dateTimeFormatter.formatToParts() throws on a string which exceeds the format", () => { const format = "yyyy-MM-dd"; const formatter = new DateTimeFormatter(format); assertThrows( () => formatter.formatToParts("2020-01-01T00:00:00.000Z"), Error, "datetime string was not fully parsed!", ); }); Deno.test("dateTimeFormatter.partsToDate()", () => { const date = new Date("2020-01-01T00:00:00.000Z"); using _time = new FakeTime(date); const format = "yyyy-MM-dd HH:mm:ss.SSS a"; const formatter = new DateTimeFormatter(format); assertEquals( formatter.partsToDate([ { type: "year", value: "2020" }, { type: "month", value: "01" }, { type: "day", value: "01" }, { type: "hour", value: "00" }, { type: "minute", value: "00" }, { type: "second", value: "00" }, { type: "fractionalSecond", value: "000" }, { type: "dayPeriod", value: "AM" }, { type: "timeZoneName", value: "UTC" }, ]), date, ); }); Deno.test("dateTimeFormatter.partsToDate() works with am dayPeriod", () => { const date = new Date("2020-01-01T00:00:00.000Z"); using _time = new FakeTime(date); const format = "HH a"; const formatter = new DateTimeFormatter(format); assertEquals( formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "AM" }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "AM." }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "A.M." }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "am" }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "am." }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "a.m." }, { type: "timeZoneName", value: "UTC" }, ]), date, ); }); Deno.test("dateTimeFormatter.partsToDate() works with pm dayPeriod", () => { const date = new Date("2020-01-01T13:00:00.000Z"); using _time = new FakeTime(date); const format = "HH a"; const formatter = new DateTimeFormatter(format); assertEquals( formatter.partsToDate([ { type: "hour", value: "01" }, { type: "dayPeriod", value: "PM" }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "01" }, { type: "dayPeriod", value: "PM." }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "01" }, { type: "dayPeriod", value: "P.M." }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "01" }, { type: "dayPeriod", value: "pm" }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "01" }, { type: "dayPeriod", value: "pm." }, { type: "timeZoneName", value: "UTC" }, ]), date, ); assertEquals( formatter.partsToDate([ { type: "hour", value: "01" }, { type: "dayPeriod", value: "p.m." }, { type: "timeZoneName", value: "UTC" }, ]), date, ); }); Deno.test("dateTimeFormatter.partsToDate() throws with invalid dayPeriods", () => { const format = "HH a"; const formatter = new DateTimeFormatter(format); assertThrows( () => formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "A.M" }, { type: "timeZoneName", value: "UTC" }, ]), Error, "dayPeriod 'A.M' is not supported.", ); assertThrows( () => formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "a.m" }, { type: "timeZoneName", value: "UTC" }, ]), Error, "dayPeriod 'a.m' is not supported.", ); assertThrows( () => formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "P.M" }, { type: "timeZoneName", value: "UTC" }, ]), Error, "dayPeriod 'P.M' is not supported.", ); assertThrows( () => formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "p.m" }, { type: "timeZoneName", value: "UTC" }, ]), Error, "dayPeriod 'p.m' is not supported.", ); assertThrows( () => formatter.partsToDate([ { type: "hour", value: "00" }, { type: "dayPeriod", value: "noon" }, { type: "timeZoneName", value: "UTC" }, ]), Error, "dayPeriod 'noon' is not supported.", ); }); Deno.test("dateTimeFormatter.partsToDate() sets utc", () => { const date = new Date("2020-01-01T00:00:00.000Z"); using _time = new FakeTime(date); const cases = [ ["yyyy-MM-dd HH:mm:ss.SSS a", [ { type: "year", value: "2020" }, { type: "month", value: "01" }, { type: "day", value: "01" }, { type: "hour", value: "00" }, { type: "minute", value: "00" }, { type: "second", value: "00" }, { type: "fractionalSecond", value: "000" }, { type: "timeZoneName", value: "UTC" }, { type: "dayPeriod", value: "AM" }, ], date], ["yyyy-MM-dd", [ { type: "year", value: "2020" }, { type: "month", value: "01" }, { type: "day", value: "01" }, { type: "timeZoneName", value: "UTC" }, ], date], ["yyyy-MM", [ { type: "year", value: "2020" }, { type: "month", value: "01" }, { type: "timeZoneName", value: "UTC" }, ], date], ] as const; for (const [format, input, output] of cases) { const formatter = new DateTimeFormatter(format); assertEquals(formatter.partsToDate([...input]), output); } });