Skip to content

Commit 53ccd84

Browse files
committed
TimezonelessDate now throws exception for invalid dates
1 parent fe6055f commit 53ccd84

3 files changed

Lines changed: 107 additions & 2 deletions

File tree

src/helpers/timezoneless-date.test.ts

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,90 @@ import {TimezonelessDate} from "./timezoneless-date";
1616
function d(s: string): TimezonelessDate {
1717
return TimezonelessDate.parseISODate(s);
1818
}
19+
function d2(y: number, m: number, d: number): string {
20+
return (new TimezonelessDate(y, m, d)).toISOString();
21+
}
22+
23+
describe("Instantiation", () => {
24+
test("Can instantiate first and last day of each month", () => {
25+
function testISO(s: string) {
26+
expect(d(s).toISOString()).toBe(s);
27+
}
28+
expect(d2(1990, 0, 1)).toBe("1990-01-01");
29+
expect(d2(1991, 0, 31)).toBe("1991-01-31");
30+
expect(d2(1992, 1, 1)).toBe("1992-02-01");
31+
expect(d2(1993, 1, 28)).toBe("1993-02-28");
32+
expect(d2(1994, 2, 1)).toBe("1994-03-01");
33+
expect(d2(1995, 2, 31)).toBe("1995-03-31");
34+
expect(d2(1996, 3, 1)).toBe("1996-04-01");
35+
expect(d2(1997, 3, 30)).toBe("1997-04-30");
36+
expect(d2(1998, 4, 1)).toBe("1998-05-01");
37+
expect(d2(1999, 4, 31)).toBe("1999-05-31");
38+
expect(d2(2000, 5, 1)).toBe("2000-06-01");
39+
expect(d2(2001, 5, 30)).toBe("2001-06-30");
40+
expect(d2(2002, 6, 1)).toBe("2002-07-01");
41+
expect(d2(2003, 6, 31)).toBe("2003-07-31");
42+
expect(d2(2004, 7, 1)).toBe("2004-08-01");
43+
expect(d2(2005, 7, 31)).toBe("2005-08-31");
44+
expect(d2(2006, 8, 1)).toBe("2006-09-01");
45+
expect(d2(2007, 8, 30)).toBe("2007-09-30");
46+
expect(d2(2008, 9, 1)).toBe("2008-10-01");
47+
expect(d2(2009, 9, 31)).toBe("2009-10-31");
48+
expect(d2(2010, 10, 1)).toBe("2010-11-01");
49+
expect(d2(2011, 10, 30)).toBe("2011-11-30");
50+
expect(d2(2012, 11, 1)).toBe("2012-12-01");
51+
expect(d2(2013, 11, 31)).toBe("2013-12-31");
52+
testISO("2014-01-01");
53+
testISO("2015-01-31");
54+
testISO("2016-02-01");
55+
testISO("2017-02-28");
56+
testISO("2018-03-01");
57+
testISO("2019-03-31");
58+
testISO("2020-04-01");
59+
testISO("2021-04-30");
60+
testISO("2022-05-01");
61+
testISO("2023-05-31");
62+
testISO("2024-06-01");
63+
testISO("2025-06-30");
64+
testISO("2026-07-01");
65+
testISO("2027-07-31");
66+
testISO("2028-08-01");
67+
testISO("2029-08-31");
68+
testISO("2030-09-01");
69+
testISO("2031-09-30");
70+
testISO("2032-10-01");
71+
testISO("2033-10-31");
72+
testISO("2034-11-01");
73+
testISO("2035-11-30");
74+
testISO("2036-12-01");
75+
testISO("2037-12-31");
76+
77+
// TODO: account for leap year
78+
});
79+
test("Throws exception for invalid months", () => {
80+
expect(() => d("1990-00-15")).toThrow();
81+
expect(() => d2(1990, -1, 15)).toThrow();
82+
expect(() => d("1990-13-15")).toThrow();
83+
expect(() => d2(1990, 12, 15)).toThrow();
84+
});
85+
test("Throws exception for invalid days", () => {
86+
expect(() => d("1990-01-32")).toThrow();
87+
//expect(() => d("1990-02-32")).toThrow();
88+
expect(() => d("1990-03-32")).toThrow();
89+
expect(() => d("1990-04-31")).toThrow();
90+
expect(() => d("1990-05-32")).toThrow();
91+
expect(() => d("1990-06-31")).toThrow();
92+
expect(() => d("1990-07-32")).toThrow();
93+
expect(() => d("1990-08-32")).toThrow();
94+
expect(() => d("1990-09-31")).toThrow();
95+
expect(() => d("1990-10-32")).toThrow();
96+
expect(() => d("1990-11-31")).toThrow();
97+
expect(() => d("1990-12-32")).toThrow();
98+
99+
// TODO: test constructor from ISO string
100+
// TODO: account for leap year
101+
});
102+
});
19103

20104
describe("toString()-like methods", () => {
21105
test("Can print a string", () => {
@@ -35,8 +119,12 @@ describe("toString()-like methods", () => {
35119
expect(a.toISOString()).toBe("1984-10-04");
36120
});
37121
test("Can pad the month", () => {
38-
const a = new TimezonelessDate(1984, 2, 27);
39-
expect(a.toISOString()).toBe("1984-03-27");
122+
const a = new TimezonelessDate(1995, 2, 27);
123+
expect(a.toISOString()).toBe("1995-03-27");
124+
});
125+
test("Can pad both", () => {
126+
const a = new TimezonelessDate(2000, 3, 1);
127+
expect(a.toISOString()).toBe("2000-04-01");
40128
});
41129
});
42130
});

src/helpers/timezoneless-date.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ export class TimezonelessDate {
2424
*/
2525
constructor(y: number, m: number, d: number) {
2626
this.__d = dayjs(0).year(y).month(m).date(d);
27+
28+
// All of this for a crude slow way of validating the date.
29+
// We accept it here because we're not particularly concerned
30+
// about speed, but maybe we should improve it some day.
31+
const yStr = String(y).padStart(4, "0");
32+
const mStr = String(m + 1).padStart(2, "0");
33+
const dStr = String(d).padStart(2, "0");
34+
const s = `${yStr}-${mStr}-${dStr}`;
35+
if (this.toISOString() !== s) {
36+
throw new Error(`Invalid date: ${s}`);
37+
}
2738
}
2839

2940
toString(): string {

src/pages/food/diary.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ I sometimes try to put in diacritics/accents or native language characters, but
108108
- 2021-11-10 - Ultimate Bacon Burger
109109
- **Da Tang Noodles** (Chinese, Noodles) - Chatswood
110110
- 2024-04-20 - Braised Beef Noodle Soup
111+
- <Ve n="Dean's Diner" c="Burgers" l="Newtown"/>
112+
- <It c="burger" d="2025-08-17" n="Australian Crawl" cost="AUD 24.00 with chips"/>
113+
- *"Beef pattie, beetroot, bacon, egg, cheese, lettuce, tomato, onion & sauce"*
114+
- I had mine with tomato sauce + mustard sauce.
115+
- <It c="sides" d="2025-08-17" n="Chips"/>
116+
- <It c="drink" d="2025-08-17" n="Hippy Hippy Shake (Thickshake), Chocolate Flavoured" cost="AUD 10.00"/>
111117
- **Destination Roll** (Vietnamese, Banh Mi) - North Sydney
112118
- 2024-10-17 - Chicken Roll
113119
- **Don Don City** (Korean) - CBD, George St.

0 commit comments

Comments
 (0)