import { isToday, isAfter, parse, compareAsc, addDays, isSameDay, startOfDay, } from "date-fns"; import { nb } from "date-fns/locale"; import { toZonedTime, format } from "date-fns-tz"; import { unique } from "./common"; const timeZone = "Europe/Oslo"; export const compactDateTimeFormat = "dd.MM.yyyy 'kl.' HH:mm"; export const compactDateFormat = "dd.MM.yyyy"; export function toLocalTime(date: Date | string | number) { return toZonedTime(date, timeZone); } export function formatDate(date: Date | string | number, formatStr: string) { return format(toLocalTime(date), formatStr, { timeZone, locale: nb }); } export function formatYearMonth(yearMonth: string) { // full name of month if year is current year, otherwise name of month + year const parsed = parse(yearMonth, "yyyy-MM", new Date()); if (parsed.getFullYear === new Date().getFullYear) { return formatDate(parsed, "MMMM"); } return formatDate(parsed, "MMMM yyyy"); } export function formatExtendedDateTime( date: Date | string | number, dateOnly: boolean = false, alwaysIncludeYear: boolean = false ) { // wide date with weekday and month name // year included if not current year const parsed = toLocalTime(date); const timePart = dateOnly ? "" : " 'kl.' HH:mm"; const isCurrentYear = parsed.getFullYear() === new Date().getFullYear(); const yearPart = (!isCurrentYear || alwaysIncludeYear) ? " yyyy" : ""; const formatStr = `EEEE d. MMMM${yearPart}${timePart}`; const formatted = format(parsed, formatStr, { timeZone, locale: nb }); return formatted; } export function isTodayOrFuture( date: Date | string | number, timeZone = "UTC" ) { const now = new Date(); const zonedNow = toZonedTime(now, timeZone); const zonedDate = toLocalTime(date); return isToday(zonedDate) || isAfter(zonedDate, zonedNow); } export function compareDates(a: Date | string, b: Date | string) { return compareAsc(new Date(a), new Date(b)); } export function isConsecutiveDays(a: Date, b: Date): boolean { const nextDay = addDays(a, 1); return isSameDay(nextDay, b); } export function groupConsecutiveDates(dates: string[]): string[][] { const groupedDates: string[][] = []; const uniqueDays = unique( dates.map((date) => format(startOfDay(toLocalTime(date)), "yyyy-MM-dd")) ).sort(); let group: string[] = []; for (let i = 0; i < uniqueDays.length; i++) { if ( i === 0 || isSameDay(uniqueDays[i - 1], uniqueDays[i]) || isConsecutiveDays(uniqueDays[i - 1], uniqueDays[i]) ) { group.push(uniqueDays[i]); } else { groupedDates.push(group); group = [uniqueDays[i]]; } } if (group.length > 0) { groupedDates.push(group); } return groupedDates; } export function formatDateRange(dates: string[]): string { if (dates.length === 1) { return formatDate(dates[0], "d. MMMM"); } const firstDate = toLocalTime(dates[0]); const lastDate = toLocalTime(dates[dates.length - 1]); if (firstDate.getMonth() === lastDate.getMonth()) { // same month return `${firstDate.getDate()}.—${lastDate.getDate()}. ${formatDate( firstDate, "MMMM" )}`; } // continues into next month const formattedFirstDate = formatDate(firstDate, "d. MMMM"); const formattedLastDate = formatDate(lastDate, "d. MMMM"); return `${formattedFirstDate}—${formattedLastDate}`; }