// Cache of start of years and number of days in the 1st week interface MondayCache { year: number // full year, e.g. 2015 mondayDateOf1stWeekUS: number // U.S. standard, the 1st of Jan determines the first week, monday can be in Dec of previous year sundayDateOf1stWeekUS: number mondayDateOf1stWeekISO: number // ISO standard, when the first Thursday of the year determines week numbering sundayDateOf1stWeekISO: number } type YEAR = number const DAY_OF_MILIS = 60*60*24*1000 const DAYS_IN_WEEK = 7 const MondaysCache: { [key: YEAR]: MondayCache } = {} const calculateMondayDateIn1stWeekOfYear = (year: number): MondayCache => { const firstSecondOfYear = new Date(`${year}-01-01T00:00:00.000Z`) const SUNDAY = 0 const MONDAY = 1 const THURSDAY = 4 const FRIDAY = 5 const SATURDAY = 6 const dayOfWeek = firstSecondOfYear.getDay() let daysToPrevMonday: number = 0 // For the Monday itself if (dayOfWeek === SUNDAY) { // Sunday daysToPrevMonday = DAYS_IN_WEEK - 1 } else if (dayOfWeek > MONDAY) { // Tue - Sat daysToPrevMonday = dayOfWeek - MONDAY } // for U.S. the first week is the one with Jan 1st, // for ISO standard, the first week is the one which contains the 1st Thursday of the year const useISOoffset = [FRIDAY, SATURDAY, SUNDAY].includes(dayOfWeek) ? DAYS_IN_WEEK : 0 return { year: year, mondayDateOf1stWeekUS: new Date(firstSecondOfYear).setDate(firstSecondOfYear.getDate() - daysToPrevMonday), sundayDateOf1stWeekUS: new Date(firstSecondOfYear).setDate(firstSecondOfYear.getDate() - daysToPrevMonday + DAYS_IN_WEEK - 1), mondayDateOf1stWeekISO: new Date(firstSecondOfYear).setDate(firstSecondOfYear.getDate() - daysToPrevMonday + useISOoffset), sundayDateOf1stWeekISO: new Date(firstSecondOfYear).setDate(firstSecondOfYear.getDate() - daysToPrevMonday + useISOoffset + DAYS_IN_WEEK - 1), } } // Week number = 1 to 54, U.S. standard by default, can also work in ISO (parameter driven) export const getDateForWeekOfYear = (year: number, weekNumber: number, useISO?: boolean, sunday?: boolean): Date => { const WEEK_OF_MILIS = DAYS_IN_WEEK * DAY_OF_MILIS const dataOfMondayIn1stWeekOfYear = (MondaysCache[year] ??= calculateMondayDateIn1stWeekOfYear(year)) const mondayOfTheRequestedWeek = (useISO ? dataOfMondayIn1stWeekOfYear.mondayDateOf1stWeekISO : dataOfMondayIn1stWeekOfYear.mondayDateOf1stWeekUS) + (weekNumber-1)*WEEK_OF_MILIS const sundayOfTheRequestedWeek = (useISO ? dataOfMondayIn1stWeekOfYear.sundayDateOf1stWeekISO : dataOfMondayIn1stWeekOfYear.sundayDateOf1stWeekUS) + (weekNumber-1)*WEEK_OF_MILIS return new Date(sunday ? sundayOfTheRequestedWeek : mondayOfTheRequestedWeek) } export const _unitTests = { calculateMondayDateIn2stWeekOfYear: calculateMondayDateIn1stWeekOfYear }