Skip to content

Instantly share code, notes, and snippets.

@phenomnomnominal
Created October 3, 2020 23:22
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save phenomnomnominal/d0c52eb4c84f8adaa83a87f78beb9c46 to your computer and use it in GitHub Desktop.
Save phenomnomnominal/d0c52eb4c84f8adaa83a87f78beb9c46 to your computer and use it in GitHub Desktop.
TypeScript Template Types Date Validator
// Check it out here:
https://www.typescriptlang.org/play?ts=4.1.0-dev.20201001#code/C4TwDgpgBAcgrgWwEYQE4GcoF4oHIAMuUAPngIxGm4BMleAzHbgCxMCsTAbEwOxMAcTAJy4A3AChQkWAHsAdgC00M7FACiADwDGAGzgATCAB54yNOgA0eQgD4Jk8NACaEAIapVAAwAkAb1MoGAC+fgHmIf6IgegRYcGeElLQALLywAAWmDie+KHySqgyQZ4k5ISluGQUFWS0iY5QACKuIFlQPr4ETNVUtLH5ysUV9OVU9BT2SVAAEjJwGF65kWbxpZ5koVHhJb2jeNQ9+7QV1IwnrPXSCvIQs-NtMAOFa0uPioM7Zd2HlbQO0skAJZyODACBtDpdGpMY5jJisCocWJbeKXaAAZQgWnk+jaQJBYPQaKgQJ0OkB6CxOIhmxWMVp0WRdOKkwa1zkEAACqgIAAzQEaVS4ADUTAAtGJxP9oM0wZz3JS1KhCqgjMlwehXABzaAQDRguS4qDoYCoYFamyqXxQNAqgBcJI12ugQVZ0llEAAKoCEMZxFAA1BPTb9RBDZgTWa5FrxJacP7A8G9QajR1gby0E1XGDvb6ggo-OnM+yIEMAPxQBOBwPyjAQD25iAlowNn0QKwly12qtBkMpzBpuQZjwe8s96u1ymt32PZseruV6seycQJUqoyeacQB0ASTkADdXOT9Fmc22HX5PcU7FKpiut82e1v0aa+2GjZHzRYeyWXx5k++Eaml+PYrvoW6qPe2Zem2s43C20GNn+NjfhOCoQPoJaQehza-qaNixthdbgYhbZvuGp4wb6f5QGW441uhmE3ORRp4R4dHVpxgYdGBW75n4YElkM3ZcQGgnMSJDHEVubrQFBZ4zjIj5LqRilsSxQFRjGcaLoGW5wRyNEARRg7DpRQSeoWQ6Zo25a6Vx8lUcYHpWI2XZPqpEAGRARmhiZVlmaOJQcaJjkIWCC4eXK6FrjIqqeI5jbebuB5HoCJ4ehevj6UpNx-teslQIlbZGFFPmvsZH7AdGqGJm2vn9sa1VarVYmMR6RFTtB4XlagKGgYxjadU5RhIfhhE4Lx0EaZRNEVvRbXEUNlWYGN7H2aJAY8e10EWQJg1tsJC1FQdvpQJJi2UiRYKFWFZUNYBTVaRNG0eg9-m+EWHguO4+VigFmapHIGR-QDI4tOg+XBcdK4-agQMZM0IBGHDf5WAj6Ro1mrTIed90VX5qZg1AqOmkE-2fdZHgY1DtEw+hcMYyjbioFjNP4XjKlgu9ROU2ZpOoGOm2wyzzO-RzF2USusXxY5KWHselFZW9ZOeDe0onXWjNpOkSOldWAszZ+NU9uz-6E5pIEqSAPOWybaHEXDw1w2LrP4a1mtXRjw1M2b-UO1dSPDXrSPId+OlgU7K0kyzc0bQHGHe9HZu0fHoU7SAM2h6+82bZtHQC+Tfhm0X2UtLTkvCxnnPp8R3uS5HLP2BrIvuEzPaG9HxstabOu209VtSVdTuTQzosC-7Q+JzrPs62qffuy9jfuDNhu57XXsz8nC-scd1YF7HZMUyXJSV1P+j1wNjtN7eDSt6qHeH+bjXd+HqiP+Lz+PU769cYbDplWlsqOKG577yzSieOGWVC5q1uuhdu1YU5d2am-eMiCd4zQxpgX+nEU4AK5lyGKwDZbwJ1uAxWGMson3VnedCesPI2wJi-FBL0GH9yRtgtOAZs4eHwXpaCQD1wJToS0ch6UlZQD8Dwgqt9pArmUoGdSyDno6R-HlJhj0Sx3AWCFLi6k+EBiURbdofgSzcj5AKCIX0oAAHleS8kpMAOy9M6wljsQ4iAwAjDuMcZaaOPjPF0zzoo9RvCXGUjcfYxx3iomePctWIxjVTKZgCU44KXDPZNhuKkmJHjgB+OMakoJwSoD6PCVkjkOTUnxP4dFOsMtQE4RuGIk8JYspsWvLIuSTTKmxK8T2VJ-dX6sOrIMjRH1rHaJiHaYm+JQTgiFhvW4cwMBzMJEYKZ6NgTzPQDUgMYyv4TKpjMFZMRoZVzrFMjZpy9lSyIUI+R2S+ktNsc8yRvgDkyJbuhRs+s6rUXGVVFR79qxrRmskjwUz8ozL5oDbZhJoXE0xNicM+UAB0szARkgpLTXRnEVxTLWeCZF1JSTknQNc+4bN4XEtNFYElqK6UkixeS3GkswXRwhScqlZMYXWKJZDXlSKqSMsFucpZhKaWQxFbiSlGBqUElpagelMrBV9RrgGDlxiuVQqFbC6mUrcXlMlYqilurlXMtNWywB9yQHCLrI2F5jr3lrQKnAy5pyBUMtxGSikfyAzmqNiwxBhrAV2x7tWb1aqg1aQ9r66NyjB6XQwlM4aVzzWT2TRfKVPspVqlDX1D2YEo3DSjUYKNYcr5exZRSXN2KKXxuQiM8+qbo6BrxQnbNprMEFuKcE4tqqZoVpznvJZ2b609vrXNUdwSOjmqCDCr1erh2Cwxb4RtqsNUlKzfGrdFyrolrPjunNDdGJTPdZSE1Oyy09kDYm+2gYl2HKBUmqAK6Y2vrAqm0eHr7hyrVZmzJXadm5tNfmq1i9O0lp-ZSMtK6CKqM7a24x7bynAcJD2iDu9t0DpRUaaO76O0lLnacxF66C0Lr8Cuo6OHGKHrQwKrdX7TkXuWfcAV-ruXyrDQPB9AYn0fr40B79mT02kcg+fRjMGIAcafQhkFLbTkzVQ-uxOObk69qI1xEjPLBaLoo6fBjJ6q0ppY100TpzOPaKGSwxDgZrM8dTTg6sDneGvQEbakhv6MAvO0VlVzXzaF1g46bXt96e52f42F4xjHnOPt7QYu59TiGNOC1Kl5AqqEGZoXfdCN7I2qps7G5tAZ30EcHXF0rhXXyJeXJ51LsHVUvKjVlajsCpTfLS-WzjG7n3htQRtXrk7yUVYyUN2rHnksPPgTW9AGXZtNfeb1wLDR8Qj0or8gg+Btu4BvFMZIrgNDra3EYXAQhzsiD2w0OGMtVAncqBdy7xI4ZYRwPdsgF2FAUCu9IF7NxtF3c8qdj752FD4G+89lmWjPU5re0Dh7oPwd2jIBwH7zgoc3BgBALU2ZAT7mgHDhSxgEdCAUGKcHu3IfuGh+xqVWOcfADxwTjbJUSdk6RyjynGs-sck5DIdAgJGf48B0T4Hn3hTUFYGjmO1P-sw9NXzgXQvmfvfFxz1HVP4Yz0JyNEn5PGDS+1sDdIt2debZB0IYU+ADea73ArdKGMZbogQEeHQIvdcW-J7YW3qVjyO+IQAIUBFqd35uLtijIDb7nLMMavZZ76MX539dfa51MI3GQaceDN6zz31uwcQ+j23HWJZ6e4+F9nhPeu8-k4L2nmPxebiK8F0z0POfw954l1LzXGMkam-j8T3P9ArdR7r0X43QcK8D-b-QfXqfrv1+N3biBvfiHO9d63yvg+vdz9+wvjIS-jwr5VEHkPk-E9CH12KegtBDd791i0OPquk-W+T7X+fY-EYP7l-MDfU-n8z7zwp1vw-3vxABL2xzLxV3hy32ryAO7x1iRjMX52b3L373P0vw70lx33RxAKRm0T7yfwvxf2t09DgMLy13HxaG0SJV-3QOIPoFILIDtB22AIoM-xAGoJpSdxdzJFoKrwAIYI52wJlzYNAM4MVRlhPz4JgMEKYM4G93IJ7yoJWSJUf2gOn310YLtDYFJzf131wOUPmFUO-yzzQP4M0KRx0LBxH3f1ELwJUJpXAIZxbzP3MJIMsNJ3Jxv3gMoI4IcMVSQKVxcLMJkK0KsIlxsP0LsMMPhhpW9WkI0PcKYJ0O0Ke0UIQJiKJW9W4PX1cNCI8OYOEPTzEP8PmWyMD2DwSP-wsOSKEDtHkKKLv3sKMLiJlTUNFzcNkNSLtGYHwAUG4FYKUL8JaMVW9UzyqKIIELCLqN6LBz4EGIyOGNiNGLaJMNL2VwmIwK6JSNmPJy73SN8PELKNWI5G0Sbw2LyMSO2JmL6Ktw1wOPYKOO5hlV9U2PoOmNSLRW2wUNH2iKWKyJeJrRyN4MuOqKSO6J0K+MaIMP+NaLw19UkMqNBMmJqIhKEDRTIG+OhL+KeJ8kBOxXaI9yuI+MhO+IUHmJ8MeNKOePhJrXGORK2JJPRLJPwEEAWMOOpLxNpIJMxwgIuJCOJIKNJO23ZzSN+KGNxO9V9UCJQKgI6PyNqM+LJOFExK5yAA
type Numbers = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
type NonZero = Exclude<Numbers, '0'>;
type Year = `${Numbers}${Numbers}${Numbers}${Numbers}`;
type Months = `0${NonZero}` | '10' | '11' | '12';
type Days = `${'0' | '1' | '2'}${NonZero}` | '30' | '31';
type Hours = `0${Numbers}` | `1${Numbers}` | '20' | '21' | '22' | '23' | '24';
type ZoneHours = NonZero | `0${NonZero}` | '10' | '11' | '12'
type Minutes = `${'0' | '1' | '2' | '3' | '4' | '5'}${Numbers}`;
type Seconds = Minutes;
type Milliseconds = `${Numbers}${Numbers}${Numbers}`;
type ZonePrefix = '+' | '-';
type DateParseError<Message extends string> = { error: Message };
type DateTime<
T extends string
> =
T extends `${infer DateTime}Z${infer Zone}` ?
ParseDateTimeZone<DateTime, Zone> :
T extends `${infer Date}` ?
ParseDateTimeNoZone<Date> :
DateParseError<`DateTime: Invalid DateTime: ${T}`>;
type ParseDateTimeZone<
DateTimeStr extends string,
ZoneStr extends string,
ParsedDateTime = ParseDateTimeNoZone<DateTimeStr>,
ParsedZone = ParseZone<ZoneStr>
> = ParsedDateTime extends DateTimeStr ?
ParsedZone extends ZoneStr ?
`${ParsedDateTime}Z${ParsedZone}` :
ParsedZone :
ParsedDateTime;
type ParseDateTimeNoZone<
DateTimeNoZoneStr extends string
> =
DateTimeNoZoneStr extends `${infer Date}T${infer Time}` ?
ParseDateTime<Date, Time> :
DateTimeNoZoneStr extends `${infer Date}` ?
ParseDate<Date> :
DateParseError<`ParseDateTimeNoZone: Invalid Date: ${DateTimeNoZoneStr}`>;
type ParseDateTime<
DateStr extends string,
TimeStr extends string,
ParsedDate = ParseDate<DateStr>,
ParsedTime = ParseTime<TimeStr>
> = ParsedDate extends DateStr ?
ParsedTime extends TimeStr ?
`${ParsedDate}T${ParsedTime}` :
ParsedTime :
ParsedDate;
type ParseDate<
DateStr extends string
> =
DateStr extends `${infer YearStr}-${infer MonthStr}-${infer DaysStr}` ?
ParseYearMonthDay<YearStr, MonthStr, DaysStr> :
DateStr extends `${infer YearStr}-${infer MonthStr}` ?
ParseYearMonth<YearStr, MonthStr> :
DateStr extends `${infer YearStr}` ?
ParseYear<YearStr> :
DateParseError<`ParseDate: Invalid Date: ${DateStr}`>;
type ParseYearMonthDay<
YearStr extends string,
MonthStr extends string,
DayStr extends string,
ParsedYear = ParseYear<YearStr>,
ParsedMonth = ParseMonth<MonthStr>,
ParsedDay = ParseDay<DayStr>,
> = ParsedYear extends YearStr ?
ParsedMonth extends MonthStr ?
ParsedDay extends DayStr ?
`${YearStr}-${MonthStr}-${DayStr}` :
ParsedDay :
ParsedMonth :
ParsedYear;
type ParseYearMonth<
YearStr extends string,
MonthStr extends string,
ParsedYear = ParseYear<YearStr>,
ParsedMonth = ParseMonth<MonthStr>,
> = ParsedYear extends YearStr ?
ParsedMonth extends MonthStr ?
`${YearStr}-${MonthStr}` :
ParsedMonth :
ParsedYear;
type ParseYear<
YearStr extends string,
> =
YearStr extends Year ?
YearStr :
DateParseError<`ParseYear: Invalid Year: ${YearStr}`>;
type ParseMonth<
MonthStr extends string,
> =
MonthStr extends Months ?
MonthStr :
DateParseError<`ParseMonth: Invalid Month: ${MonthStr}`>;
type ParseDay<
DayStr extends string,
> =
DayStr extends Days ?
DayStr :
DateParseError<`ParseDay: Invalid Date: ${DayStr}`>;
type ParseZone<
ZoneStr extends string
> =
ZoneStr extends ZoneHours ?
ZoneStr :
ZoneStr extends `${ZonePrefix}${infer Offset}` ?
ParseZoneOffset<Offset> extends Offset ?
ZoneStr :
ParseZoneOffset<Offset> :
ZoneStr extends `${infer Offset}` ?
ParseZoneOffset<Offset> extends Offset ?
ZoneStr :
ParseZoneOffset<Offset> :
DateParseError<`ParseZone: Invalid Zone: ${ZoneStr}`>
type ParseZoneOffset<
OffsetStr extends string,
> =
OffsetStr extends `${infer Hours}:${infer Minutes}` ?
ParseHoursMinutes<Hours, Minutes> :
OffsetStr extends `${infer Hours}` ?
ParseHours<Hours> :
DateParseError<`ParseZoneOffset: Invalid Offset: ${OffsetStr}`>;
type ParseTime<
TimeStr extends string
> =
TimeStr extends `${infer HoursStr}:${infer MinutesStr}:${infer SecondsStr}.${infer MillisStr}` ?
ParseHoursMinutesSecondsMillis<HoursStr, MinutesStr, SecondsStr, MillisStr> :
TimeStr extends `${infer HoursStr}:${infer MinutesStr}:${infer SecondsStr}` ?
ParseHoursMinutesSeconds<HoursStr, MinutesStr, SecondsStr> :
TimeStr extends `${infer HoursStr}:${infer MinutesStr}` ?
ParseHoursMinutes<HoursStr, MinutesStr> :
DateParseError<`ParseTime: Invalid Time: ${TimeStr}`>;;
type ParseHoursMinutesSecondsMillis<
HoursStr extends string,
MinutesStr extends string,
SecondsStr extends string,
MillisStr extends string,
ParsedHours = ParseHours<HoursStr>,
ParsedMinutes = ParseMinutes<MinutesStr>,
ParsedSeconds = ParseSeconds<SecondsStr>,
ParsedMillis = ParseMillis<MillisStr>
> =
ParsedHours extends HoursStr ?
ParsedMinutes extends MinutesStr ?
ParsedSeconds extends SecondsStr ?
ParsedMillis extends MillisStr ?
`${HoursStr}:${MinutesStr}:${SecondsStr}.${MillisStr}` :
ParsedMillis :
ParsedSeconds :
ParsedMinutes :
ParsedHours;
type ParseHoursMinutesSeconds<
HoursStr extends string,
MinutesStr extends string,
SecondsStr extends string,
ParsedHours = ParseHours<HoursStr>,
ParsedMinutes = ParseMinutes<MinutesStr>,
ParsedSeconds = ParseSeconds<SecondsStr>
> =
ParsedHours extends HoursStr ?
ParsedMinutes extends MinutesStr ?
ParsedSeconds extends SecondsStr ?
`${HoursStr}:${MinutesStr}:${SecondsStr}` :
ParsedSeconds :
ParsedMinutes :
ParsedHours;
type ParseHoursMinutes<
HoursStr extends string,
MinutesStr extends string,
ParsedHours = ParseHours<HoursStr>,
ParsedMinutes = ParseMinutes<MinutesStr>
> =
ParsedHours extends HoursStr ?
ParsedMinutes extends MinutesStr ?
`${HoursStr}:${MinutesStr}` :
ParsedMinutes :
ParsedHours;
type ParseHours<
HourStr extends string,
> =
HourStr extends Hours ?
HourStr :
DateParseError<`ParseHours: Invalid Hour: ${HourStr}`>;
type ParseMinutes<
MinutesStr extends string,
> =
MinutesStr extends Minutes ?
MinutesStr :
DateParseError<`ParseMinutes: Invalid Minutes: ${MinutesStr}`>;
type ParseSeconds<
SecondsStr extends string,
> =
SecondsStr extends Seconds ?
SecondsStr :
DateParseError<`ParseSeconds: Invalid Seconds: ${SecondsStr}`>;
type ParseMillis<
MillisStr extends string,
> =
MillisStr extends Milliseconds ?
MillisStr :
DateParseError<`ParseMillis: Invalid Milliseconds: ${MillisStr}`>;
type MinYear = DateTime<'0000'>;
type MaxYear = DateTime<'9999'>;
type YearError = DateTime<'19999'>;
type YearZone = DateTime<'1999Z1'>;
type YearZoneHour = DateTime<'1999Z01'>;
type YearZoneHoursMinutes = DateTime<'1999Z01:15'>;
type YearZoneNegative = DateTime<'1999Z-01'>;
type YearZoneHoursMinutesNegative = DateTime<'1999Z-01:15'>;
type YearZonePositive = DateTime<'1999Z+24'>;
type YearZoneHoursMinutesPositive = DateTime<'1999Z+01:15'>;
type YearMonth = DateTime<'1999-03'>;
type YearMonthError = DateTime<'1999+03'>;
type YearInvalidMonthErrorSmall = DateTime<'1999-00'>;
type YearInvalidMonthErrorBig = DateTime<'1999-13'>;
type YearMonthZone = DateTime<'1999-03Z1'>;
type YearMonthZoneHour = DateTime<'1999-03Z01'>;
type YearMonthZoneNegative = DateTime<'1999-03Z-01'>;
type YearMonthZonePositive = DateTime<'1999-03Z+24'>;
type YearMonthDayError = DateTime<'1999-03+03'>;
type YearMonthDay = DateTime<'1999-03-03'>;
type YearMonthInvalidDayErrorSmall = DateTime<'1999-03-00'>;
type YearMonthInvalidDayErrorBig = DateTime<'1999-03-32'>;
type YearMonthDayZone = DateTime<'1999-03-03Z1'>;
type YearMonthDayZoneHour = DateTime<'1999-03-03Z01'>;
type YearMonthDayZoneNegative = DateTime<'1999-03-03Z-01'>;
type YearMonthDayZonePositive = DateTime<'1999-03-03Z+24'>;
type YearMonthDayHourError = DateTime<'1999-03-03T01'>;
type YearMonthDayHourMinute = DateTime<'1999-03-03T01:00'>;
type YearMonthDayHourMinuteErrorSmall = DateTime<'1999-03-03T01:1'>;
type YearMonthDayHourMinuteErrorBig = DateTime<'1999-03-03T01:60'>;
type YearMonthDayHourMinuteZone = DateTime<'1999-03-03T01:59Z1'>;
type YearMonthDayHourMinuteZoneHour = DateTime<'1999-03-03T01:59Z03'>;
type YearMonthDayHourMinuteZoneNegative = DateTime<'1999-03-03T01:59Z-02'>;
type YearMonthDayHourMinuteZonePositive = DateTime<'1999-03-03T01:59Z+23'>;
type YearMonthDayHourMinuteSecond = DateTime<'1999-03-03T01:59:59'>;
type YearMonthDayHourMinuteSecondErrorSmall = DateTime<'1999-03-03T01:59:0'>;
type YearMonthDayHourMinuteSecondErrorBig = DateTime<'1999-03-03T01:59:60'>;
type YearMonthDayHourMinuteSecondZone = DateTime<'1999-03-03T01:59:40Z6'>;
type YearMonthDayHourMinuteSecondZoneHour = DateTime<'1999-03-03T01:59:40Z07'>;
type YearMonthDayHourMinuteSecondZoneHourNegative = DateTime<'1999-03-03T01:59:40Z-04'>;
type YearMonthDayHourMinuteSecondZoneHourPositive = DateTime<'1999-03-03T01:59:40Z+05'>;
type YearMonthDayHourMinuteSecondMilli = DateTime<'1999-03-03T01:59:59.000'>;
type YearMonthDayHourMinuteSecondMilliErrorSmall = DateTime<'1999-03-03T01:59:59.0'>;
type YearMonthDayHourMinuteSecondMilliErrorBig = DateTime<'1999-03-03T01:59:59.1000'>;
type YearMonthDayHourMinuteSecondMilliZone = DateTime<'1999-03-03T01:59:59.000Z7'>;
type YearMonthDayHourMinuteSecondMilliZoneHour = DateTime<'1999-03-03T01:59:59.000Z08'>;
type YearMonthDayHourMinuteSecondMilliZoneNegative = DateTime<'1999-03-03T01:59:59.000Z-09'>;
type YearMonthDayHourMinuteSecondMilliZonePositive = DateTime<'1999-03-03T01:59:59.000Z+10'>;
@Realetive
Copy link

Wow! Cool! May be publish it as npm-package to @types/ParseDate?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment