64 lines
2.8 KiB
TypeScript
64 lines
2.8 KiB
TypeScript
|
|
// 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
|
|
}
|