Trying to refactor/optimise an existing method parsing the date range or a date:
const parseDateRange = (range: string): Date | null => {
if (!range?.length) {
return null;
}
// Handle "MM/YYYY" format
if (/^\d{1,2}\/\d{4}$/.test(range)) {
const [month, year] = range.split('/').map(Number);
if (!isNaN(month) && !isNaN(year)) {
return new Date(year, month - 1);
}
}
// Handle "MM/DD/YYYY-MM/DD/YYYY" format
const startDateString = range.slice(0, range.indexOf('-')).trim();
const startDate = new Date(startDateString);
if (!isNaN(startDate.getTime())) {
return startDate;
}
return null;
};
I can probably use something like this type here:
type Split<S extends string, D extends string> =
string extends S ? string[] :
S extends '' ? [] :
S extends `${infer T}${D}${infer U}` ? [T, U]:
[S];
Would appreciate the recommendation how I would utilize this or similar to parse the delimited date range type here.
I am sure you can use different libraries for this such Moment.js
and date-fns
,
But I am just going to assume you need it for some specific cases(like you dont want an external lib). In that case you can go about this using this code I have provided below.
type Split<S extends string, D extends string> = string extends S
? string[]
: S extends ""
? []
: S extends `${infer T}${D}${infer U}`
? [T, U]
: [S];
const parseDateRange = (range: string): string | null => {
if (!range?.length) {
return null;
}
// Handle "MM/YYYY" format
const mmYYYYMatch = /^(\d{1,2})\/(\d{4})$/.exec(range);
if (mmYYYYMatch) {
const [_, month, year] = mmYYYYMatch.map(Number);
if (month && year) {
let new_date = new Date(year, month - 1);
return new_date.toUTCString();
}
}
// Handle "MM/DD/YYYY-MM/DD/YYYY" format
const [start, end] = range.split("-").map((date) => date.trim());
const startDate = new Date(start);
const endDate = new Date(end);
if (!isNaN(startDate.getTime())) {
return startDate.toUTCString(); // Optionally, handle `endDate` if needed.
}
return null;
};
// Example usage
console.log(parseDateRange("08/2024")); // Expected output: Date object for August 2024
console.log(parseDateRange("08/12/2024-08/13/2024")); // Expected output: Date object for August 12, 2024
console.log(parseDateRange("Invalid input")); // Expected output: null
// convert from Z U T C to local time
const date = new Date("2024-08-12T00:00:00Z");
console.log(date.toLocaleString()); // Expected output: Local time for August 12, 2024
Results
Wed, 31 Jul 2024 23:00:00 GMT
Sun, 11 Aug 2024 23:00:00 GMT
null
8/12/2024, 1:00:00 AM
A request by OP on how to use Spilt type
if I wanted to split a date
Here is an example :
type Split<S extends string, D extends string> = string extends S
? string[]
: S extends ""
? []
: S extends `${infer T}${D}${infer U}`
? [T, U]
: [S];
// a type enforcer which expects a format of the date string
type DateParts = Split<'MM/DD/YYYY', '/'>; // the result would be something like this ['MM', 'DD', 'YYYY']
// func that uses the `Split` type to validate input structures.
function parseDateParts<T extends string>(formatDateToString: T): Split<T, '/'> {
return formatDateToString.split('/') as Split<T, '/'>;
}
// how ts would infer from it
const parts = parseDateParts('08/12/2024'); // TypeScript infers ['08', '12', '2024']
Alternatively
You can use it in this way
// the spilt type provided
type Split<S extends string, D extends string> = string extends S
? string[]
: S extends ""
? []
: S extends `${infer T}${D}${infer U}`
? [T, ...Split<U, D>]
: [S];
// trying to mimic date formats ...
type MMYYYY = `${number}/${number}`;
// MM/DD/YYYY-MM/DD/YYYY format
type DateRange = `${number}/${number}/${number}-${number}/${number}/${number}`;
// refactor func to use utility types
const parseDateRange = (range: string): Date | null => {
if (!range?.length) {
return null;
}
// match the MM/YYYY format
if (/^\d{1,2}\/\d{4}$/.test(range)) {
// Use Split to ensure correct type handling
const [month, year] = range.split("/") as Split<MMYYYY, "/">;
const date = new Date(Number(year), Number(month) - 1);
return isNaN(date.getTime()) ? null : date;
}
return null;
};