Files
flyer-crawler.projectium.com/src/utils/dateUtils.ts
Torben Sorensen 4e20b1b430
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 54s
even more and more test fixes
2026-01-05 01:26:12 -08:00

101 lines
3.4 KiB
TypeScript

// src/utils/dateUtils.ts
import { parseISO, format, isValid, differenceInDays } from 'date-fns';
/**
* Calculates the current year and a simplified week number.
*
* Note: This is a simple calculation where week 1 starts on January 1st.
* For true ISO 8601 week numbers (where week 1 is the first week with a Thursday),
* a dedicated library like `date-fns` (`getISOWeek` and `getISOWeekYear`) is recommended.
*
* @param date The date to calculate the simple week for. Defaults to the current date.
* @returns An object containing the year and week number.
*/
export function calculateSimpleWeekAndYear(date: Date = new Date()): { year: number; week: number } {
const year = date.getFullYear();
// Use UTC dates to calculate the difference in days.
// This avoids issues with Daylight Saving Time (DST) where a day might have 23 or 25 hours,
// which can cause the millisecond-based calculation to be slightly off (e.g., 149.96 days).
const startOfYear = Date.UTC(year, 0, 1);
const current = Date.UTC(year, date.getMonth(), date.getDate());
const msPerDay = 1000 * 60 * 60 * 24;
const dayOfYear = (current - startOfYear) / msPerDay;
// Divide by 7, take the floor to get the zero-based week, and add 1 for a one-based week number.
const week = Math.floor(dayOfYear / 7) + 1;
return { year, week };
}
export const formatShortDate = (dateString: string | null | undefined): string | null => {
if (!dateString) return null;
// Using `parseISO` from date-fns is more reliable than `new Date()` for YYYY-MM-DD strings.
// It correctly interprets the string as a local date, avoiding timezone-related "off-by-one" errors.
const date = parseISO(dateString);
if (isValid(date)) {
return format(date, 'MMM d');
}
return null;
};
export const calculateDaysBetween = (
startDate: string | Date | null | undefined,
endDate: string | Date | null | undefined,
): number | null => {
if (!startDate || !endDate) return null;
const start = typeof startDate === 'string' ? parseISO(startDate) : startDate;
const end = typeof endDate === 'string' ? parseISO(endDate) : endDate;
if (!isValid(start) || !isValid(end)) return null;
return differenceInDays(end, start);
};
interface DateRangeOptions {
verbose?: boolean;
}
export const formatDateRange = (
startDate: string | null | undefined,
endDate: string | null | undefined,
options?: DateRangeOptions,
): string | null => {
if (!options?.verbose) {
const start = formatShortDate(startDate);
const end = formatShortDate(endDate);
if (start && end) {
return start === end ? start : `${start} - ${end}`;
}
return start || end || null;
}
// Verbose format logic
const dateFormat = 'MMMM d, yyyy';
const formatFn = (dateStr: string | null | undefined) => {
if (!dateStr) return null;
const date = parseISO(dateStr);
return isValid(date) ? format(date, dateFormat) : null;
};
const start = formatFn(startDate);
const end = formatFn(endDate);
if (start && end) {
return start === end ? `Valid on ${start}` : `Deals valid from ${start} to ${end}`;
}
if (start) return `Deals start ${start}`;
if (end) return `Deals end ${end}`;
return null;
};
/**
* Returns the current date as an ISO 8601 string (YYYY-MM-DD).
* Useful for getting "today" without the time component.
*/
export const getCurrentDateISOString = (): string => {
return format(new Date(), 'yyyy-MM-dd');
};
export { calculateSimpleWeekAndYear as getSimpleWeekAndYear };