Skip to content

Commit b6a08fc

Browse files
committed
GH-4: add isaDateFormat for datetime format checks
1 parent e0198bb commit b6a08fc

3 files changed

Lines changed: 159 additions & 15 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ All type checks return the value if it passes, or throw a `TypeError` if it fail
114114
- `isaString(value)` - Strings
115115
- `isaNumber(value)` - Numbers (including negative and floats)
116116
- `isaBoolean(value)` - Booleans
117-
- `isaDate(value)` - Valid Date objects
117+
- `isaDateObj(value)` - Valid Date objects
118+
- `isaDateFormat(format, value)` - Date strings with specific format ("YYYY-MM-DD", "MM/DD/YYYY", "MM-DD-YYYY", "DD-MM-YYYY", "YYYY-MM-DD HH:mm:ss", "YYYY-MM-DDTHH:mm:ss")
118119
- `isaURL(value)` - Valid HTTP/HTTPS URLs
119120
- `isaArray(value)` - Arrays
120121
- `isaObject(value)` - Plain objects

src/class_types.js

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ class ClassTypes {
2121
"isaString": "string",
2222
"isaNumber": "number",
2323
"isaBoolean": "boolean",
24-
"isaDate": "date",
24+
"isaDateObj": "date object",
25+
"isaDateFormat": "date string",
2526
"isaURL": "URL",
2627
"isaArray": "array",
2728
"isaObject": "object",
@@ -81,14 +82,90 @@ class ClassTypes {
8182
);
8283
}
8384

84-
isaDate (value) {
85+
isaDateObj (value) {
8586
return this.#check(
86-
"isaDate",
87+
"isaDateObj",
8788
(v) => v instanceof Date && !isNaN(v.getTime()),
8889
value,
8990
);
9091
}
9192

93+
isaDateFormat (format, value) {
94+
return this.#check(
95+
"isaDateFormat",
96+
(v) => {
97+
if (typeof v !== "string") return false;
98+
const formats = {
99+
"YYYY-MM-DD": {
100+
regex: /^\d{4}-\d{2}-\d{2}$/,
101+
validate: (str) => {
102+
const [year, month, day] = str.split("-")
103+
.map(Number);
104+
const date = new Date(year, month - 1, day);
105+
return date.getFullYear() === year
106+
&& date.getMonth() === month - 1
107+
&& date.getDate() === day;
108+
},
109+
},
110+
"YYYY-MM-DD HH:mm:ss": {
111+
regex: /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/,
112+
validate: (str) => {
113+
const date = new Date(str.replace(" ", "T"));
114+
return !isNaN(date.getTime());
115+
},
116+
},
117+
"YYYY-MM-DDTHH:mm:ss": {
118+
regex: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/,
119+
validate: (str) => {
120+
const date = new Date(str);
121+
return !isNaN(date.getTime());
122+
},
123+
},
124+
"MM/DD/YYYY": {
125+
regex: /^\d{2}\/\d{2}\/\d{4}$/,
126+
validate: (str) => {
127+
const [month, day, year] = str.split("/")
128+
.map(Number);
129+
const date = new Date(year, month - 1, day);
130+
return date.getFullYear() === year
131+
&& date.getMonth() === month - 1
132+
&& date.getDate() === day;
133+
},
134+
},
135+
"MM-DD-YYYY": {
136+
regex: /^\d{2}-\d{2}-\d{4}$/,
137+
validate: (str) => {
138+
const [month, day, year] = str.split("-")
139+
.map(Number);
140+
const date = new Date(year, month - 1, day);
141+
return date.getFullYear() === year
142+
&& date.getMonth() === month - 1
143+
&& date.getDate() === day;
144+
},
145+
},
146+
"DD-MM-YYYY": {
147+
regex: /^\d{2}-\d{2}-\d{4}$/,
148+
validate: (str) => {
149+
const [day, month, year] = str.split("-")
150+
.map(Number);
151+
const date = new Date(year, month - 1, day);
152+
return date.getFullYear() === year
153+
&& date.getMonth() === month - 1
154+
&& date.getDate() === day;
155+
},
156+
},
157+
};
158+
const formatDef = formats[format];
159+
if (!formatDef) {
160+
throw new TypeError(`Unknown date format: ${format}`);
161+
}
162+
if (!formatDef.regex.test(v)) return false;
163+
return formatDef.validate(v);
164+
},
165+
value,
166+
);
167+
}
168+
92169
isaURL (value) {
93170
return this.#check(
94171
"isaURL",

test/test.js

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ class TestChild extends ClassTypes {
2121
return this.isaBoolean(value);
2222
}
2323

24-
testDate (value) {
25-
return this.isaDate(value);
24+
testDateObj (value) {
25+
return this.isaDateObj(value);
26+
}
27+
28+
testDateFormat (format, value) {
29+
return this.isaDateFormat(format, value);
2630
}
2731

2832
testURL (value) {
@@ -277,39 +281,101 @@ describe("ClassTypes", () => {
277281
});
278282
});
279283

280-
describe("isaDate", () => {
284+
describe("isaDateObj", () => {
281285
const validDate = new Date();
282286
const anotherDate = new Date("2024-01-01");
283287

284288
test("returns value for valid dates", () => {
285-
expect(instance.testDate(validDate))
289+
expect(instance.testDateObj(validDate))
286290
.toBe(validDate);
287-
expect(instance.testDate(anotherDate))
291+
expect(instance.testDateObj(anotherDate))
288292
.toBe(anotherDate);
289293
});
290294

291295
test("throws TypeError for invalid dates", () => {
292-
expect(() => instance.testDate(new Date("invalid")))
296+
expect(() => instance.testDateObj(new Date("invalid")))
293297
.toThrow(TypeError);
294298
});
295299

296300
test("throws TypeError for strings", () => {
297-
expect(() => instance.testDate("2024-01-01"))
301+
expect(() => instance.testDateObj("2024-01-01"))
302+
.toThrow(TypeError);
303+
});
304+
305+
test("throws TypeError for numbers", () => {
306+
expect(() => instance.testDateObj(1234567890))
307+
.toThrow(TypeError);
308+
});
309+
310+
test("throws TypeError for null", () => {
311+
expect(() => instance.testDateObj(null))
312+
.toThrow(TypeError);
313+
});
314+
315+
test("throws TypeError for undefined", () => {
316+
expect(() => instance.testDateObj(undefined))
317+
.toThrow(TypeError);
318+
});
319+
});
320+
321+
describe("isaDateFormat", () => {
322+
test("returns value for valid YYYY-MM-DD format", () => {
323+
expect(instance.testDateFormat("YYYY-MM-DD", "2026-03-05"))
324+
.toBe("2026-03-05");
325+
});
326+
327+
test("returns value for valid MM/DD/YYYY format", () => {
328+
expect(instance.testDateFormat("MM/DD/YYYY", "03/05/2026"))
329+
.toBe("03/05/2026");
330+
});
331+
332+
test("returns value for valid MM-DD-YYYY format", () => {
333+
expect(instance.testDateFormat("MM-DD-YYYY", "03-05-2026"))
334+
.toBe("03-05-2026");
335+
});
336+
337+
test("returns value for valid DD-MM-YYYY format", () => {
338+
expect(instance.testDateFormat("DD-MM-YYYY", "05-03-2026"))
339+
.toBe("05-03-2026");
340+
});
341+
342+
test("returns value for valid YYYY-MM-DD HH:mm:ss format", () => {
343+
expect(instance.testDateFormat("YYYY-MM-DD HH:mm:ss", "2026-03-05 14:30:00"))
344+
.toBe("2026-03-05 14:30:00");
345+
});
346+
347+
test("returns value for valid YYYY-MM-DDTHH:mm:ss format", () => {
348+
expect(instance.testDateFormat("YYYY-MM-DDTHH:mm:ss", "2026-03-05T14:30:00"))
349+
.toBe("2026-03-05T14:30:00");
350+
});
351+
352+
test("throws TypeError for invalid format name", () => {
353+
expect(() => instance.testDateFormat("INVALID", "2026-03-05"))
354+
.toThrow(TypeError);
355+
});
356+
357+
test("throws TypeError for wrong pattern", () => {
358+
expect(() => instance.testDateFormat("YYYY-MM-DD", "03-05-2026"))
359+
.toThrow(TypeError);
360+
});
361+
362+
test("throws TypeError for invalid date", () => {
363+
expect(() => instance.testDateFormat("YYYY-MM-DD", "2024-02-30"))
298364
.toThrow(TypeError);
299365
});
300366

301367
test("throws TypeError for numbers", () => {
302-
expect(() => instance.testDate(1234567890))
368+
expect(() => instance.testDateFormat("YYYY-MM-DD", 20260305))
303369
.toThrow(TypeError);
304370
});
305371

306372
test("throws TypeError for null", () => {
307-
expect(() => instance.testDate(null))
373+
expect(() => instance.testDateFormat("YYYY-MM-DD", null))
308374
.toThrow(TypeError);
309375
});
310376

311377
test("throws TypeError for undefined", () => {
312-
expect(() => instance.testDate(undefined))
378+
expect(() => instance.testDateFormat("YYYY-MM-DD", undefined))
313379
.toThrow(TypeError);
314380
});
315381
});
@@ -684,7 +750,7 @@ describe("ClassTypes", () => {
684750
.toBe(42);
685751
expect(instance.maybe("isaBoolean", true))
686752
.toBe(true);
687-
expect(instance.maybe("isaDate", new Date()))
753+
expect(instance.maybe("isaDateObj", new Date()))
688754
.toBeInstanceOf(Date);
689755
expect(instance.maybe("isaURL", "https://example.com"))
690756
.toBe("https://example.com");

0 commit comments

Comments
 (0)