Skip to content

Commit 7baa0af

Browse files
chore: properly format timestamptz
1 parent a4f87dd commit 7baa0af

File tree

2 files changed

+434
-19
lines changed

2 files changed

+434
-19
lines changed

src/validators/timestamptz.ts

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,100 @@
11
import type { ValidationNames } from '../types'
2-
import { TimestampValidator } from './timestamps'
2+
import { BaseValidator } from './base'
33

4-
export class TimestampTzValidator extends TimestampValidator {
4+
export class TimestampTzValidator extends BaseValidator<number | string> {
55
public name: ValidationNames = 'timestampTz'
66

77
constructor() {
88
super()
99
this.addRule({
1010
name: 'timestampTz',
11-
test: (value: number | string) => {
12-
// Check if it's a valid number
13-
const num = Number(value)
14-
if (Number.isNaN(num)) {
11+
test: (value: number | string | null | undefined) => {
12+
if (value === null || value === undefined) {
1513
return false
1614
}
1715

18-
// MySQL TIMESTAMP range: 1970-01-01 to 2038-01-19
19-
const minTimestamp = 0 // 1970-01-01 00:00:00 UTC
20-
const maxTimestamp = 2147483647 // 2038-01-19 03:14:07 UTC
16+
// For numeric values, validate as Unix timestamp
17+
if (typeof value === 'number') {
18+
const num = Number(value)
19+
if (Number.isNaN(num)) {
20+
return false
21+
}
2122

22-
// First check if it's within the valid range
23-
if (num < minTimestamp || num > maxTimestamp) {
24-
return false
23+
// MySQL TIMESTAMP range: 1970-01-01 to 2038-01-19
24+
const minTimestamp = 0 // 1970-01-01 00:00:00 UTC
25+
const maxTimestamp = 2147483647 // 2038-01-19 03:14:07 UTC
26+
27+
return num >= minTimestamp && num <= maxTimestamp
2528
}
2629

27-
// For string inputs, check if the length is valid (10-13 digits)
30+
// For string values, check for timezone information
2831
if (typeof value === 'string') {
29-
const timestampStr = value.toString()
30-
const length = timestampStr.length
31-
if (length < 10 || length > 13) {
32-
return false
32+
const str = value.trim()
33+
34+
// Check if it's a numeric string (Unix timestamp)
35+
const num = Number(str)
36+
if (!Number.isNaN(num)) {
37+
// If it's a numeric string, validate as Unix timestamp
38+
const minTimestamp = 0
39+
const maxTimestamp = 2147483647
40+
41+
// Check length (10-13 digits) for Unix timestamps
42+
if (str.length < 10 || str.length > 13) {
43+
return false
44+
}
45+
46+
return num >= minTimestamp && num <= maxTimestamp
47+
}
48+
49+
// Check for ISO 8601 format with timezone
50+
// Examples: 2023-12-25T10:30:00Z, 2023-12-25T10:30:00+05:00, 2023-12-25T10:30:00-08:00
51+
const isoWithTzRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?(Z|[+-]\d{2}:\d{2})$/
52+
if (isoWithTzRegex.test(str)) {
53+
const date = new Date(str)
54+
return !Number.isNaN(date.getTime())
3355
}
56+
57+
// Check for RFC 3339 format with timezone
58+
// Examples: 2023-12-25 10:30:00Z, 2023-12-25 10:30:00+05:00
59+
const rfc3339WithTzRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d{3})?(Z|[+-]\d{2}:\d{2})$/
60+
if (rfc3339WithTzRegex.test(str)) {
61+
const date = new Date(str)
62+
return !Number.isNaN(date.getTime())
63+
}
64+
65+
// Check for other common timezone formats
66+
// Examples: 2023-12-25T10:30:00.000Z, 2023-12-25 10:30:00 UTC
67+
const otherTzFormats = [
68+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/, // ISO with milliseconds
69+
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$/, // UTC suffix
70+
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} GMT$/, // GMT suffix
71+
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [A-Z]{3,4}$/, // Timezone abbreviations
72+
]
73+
74+
for (const regex of otherTzFormats) {
75+
if (regex.test(str)) {
76+
const date = new Date(str)
77+
return !Number.isNaN(date.getTime())
78+
}
79+
}
80+
81+
return false
3482
}
3583

36-
return true
84+
return false
3785
},
38-
message: 'Must be a valid timestamp between 1970-01-01 and 2038-01-19',
86+
message: 'Must be a valid timestamp with timezone information (ISO 8601, RFC 3339, or Unix timestamp)',
3987
})
4088
}
89+
90+
test(value: any): boolean {
91+
// Override the base test method to handle null/undefined properlyc
92+
if (value === null || value === undefined) {
93+
return !this.isRequired
94+
}
95+
96+
return this.validate(value).valid
97+
}
4198
}
4299

43100
export function timestampTz(): TimestampTzValidator {

0 commit comments

Comments
 (0)