Created
September 6, 2023 15:02
-
-
Save evelant/73203ff75b3e7d3e7a524c47c8d28694 to your computer and use it in GitHub Desktop.
DateUtils
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { closestTo } from "date-fns" | |
import _ from "lodash" | |
const SECONDS = 1000 | |
const MINUTES = SECONDS * 60 | |
const HOURS = MINUTES * 60 | |
const DAYS = HOURS * 24 | |
const YEARS = DAYS * 365 | |
/** | |
* given a date and a UTC hour number, get the UTC date at that UTC hour which comes after date | |
*/ | |
const getNextRolloverDate = (UTCHour: number, date: Date = new Date()) => { | |
const ret = new Date(date.getTime()) | |
const dateHour = date.getUTCHours() | |
if (dateHour >= UTCHour) { | |
//we need the date from a the next day, add 24 hours | |
ret.setTime(ret.getTime() + 1000 * 60 * 60 * 24) | |
} | |
//set time to the UTC hour and clear minutes, seconds, milliseconds | |
ret.setUTCHours(UTCHour, 0, 0, 0) | |
return ret | |
} | |
/** | |
* given a date and a UTC hour number, get the UTC date at that UTC hour which came before date | |
*/ | |
export const getPreviousRolloverDate = (UTCHour: number, date: Date = new Date()) => { | |
const ret = new Date(date.getTime()) | |
const dateHour = date.getUTCHours() | |
if (dateHour < UTCHour) { | |
//we need the date from a previous day, subtract 24 hours | |
ret.setTime(ret.getTime() - 1000 * 60 * 60 * 24) | |
} | |
//set time to the UTC hour and clear minutes, seconds, milliseconds | |
ret.setUTCHours(UTCHour, 0, 0, 0) | |
return ret | |
} | |
/** | |
* Given (maybe) current offset and rollover hour check if the timezone offset has changed | |
* and return new offset and rollover hour or calculate offset and rollover hour if they don't exist | |
*/ | |
export function getUpdatedTimezoneInfo( | |
currentUTCOffsetMinutes?: undefined | number, | |
currentUTCRolloverHour?: undefined | number, | |
): { utcOffsetMinutes: number; utcRolloverHour: number } { | |
//Multiply getTimezoneOffset by -1 since it returns the current diff in minutes between local time and UTC time | |
//so for example EST (utc -4) returns 60 * 4 = 240 but we want the time offset from utc which is -240 | |
const newOffset = -1.0 * new Date().getTimezoneOffset() | |
let newRollover = currentUTCRolloverHour | |
//if offset changed or missing rollover then calculate it | |
if (newOffset != currentUTCOffsetMinutes || typeof newRollover != "number") { | |
if (typeof newRollover !== "number" || typeof currentUTCOffsetMinutes != "number") { | |
//if utcRolloverHour or offset doesn't exist yet set utcrolloverhour to current timezone offset | |
newRollover = -1.0 * (newOffset / 60) | |
} else { | |
//if there's already a utcRolloverHour and currentOffset changed (such as daylight savings time or system timezone change) | |
//then update utcRolloverHour by the amount it changed | |
//ex: go from -4 hours to -5 hours | |
// -240 - -300 = 60 / 60 = 1 so utcRolloverHour 4 + 1 = 5 | |
//ex: go from -5 hours to -4 hours | |
// -300 - -240 = -60 / 60 = -1 so utcRolloverHour 5 + -1 = 4 | |
newRollover += (currentUTCOffsetMinutes - newOffset) / 60 | |
} | |
} | |
return { utcOffsetMinutes: newOffset, utcRolloverHour: newRollover } | |
} | |
const DateUtils = { | |
epoch() { | |
return new Date(0) | |
}, | |
/** | |
* return a new date with the day component of toDate but the UTC time component of fromDate | |
*/ | |
copyUTCTime: (fromDate: Date, toDate: Date) => { | |
const d = new Date(toDate.getTime()) | |
d.setUTCHours( | |
fromDate.getUTCHours(), | |
fromDate.getUTCMinutes(), | |
fromDate.getUTCSeconds(), | |
fromDate.getUTCMilliseconds(), | |
) | |
return d | |
}, | |
/** | |
* Given a UTCOffset in minutes, what Day number does a Date correspond to | |
*/ | |
getDayWithUTCOffset(UTCOffset: number, date = Date.now()): number { | |
// log.log("day with utc offset", UTCOffset, date, this.addMinutes(UTCOffset, date)); | |
return this.addMinutesDate(UTCOffset, date).getDay() | |
}, | |
/** | |
* true if firstDate and secondDate fall within the same rollover period | |
*/ | |
isSameDay: (firstDate: Date, secondDate: Date, UTCRolloverHour: number) => { | |
return ( | |
getNextRolloverDate(UTCRolloverHour, firstDate).getTime() == | |
getNextRolloverDate(UTCRolloverHour, secondDate).getTime() | |
) | |
}, | |
addSeconds: (seconds: number, d?: undefined | Date | undefined | number): number => { | |
d = typeof d === "number" ? d : typeof d === "undefined" ? Date.now() : d.getTime() | |
return d + seconds * SECONDS | |
}, | |
addSecondsDate: (seconds: number, d?: undefined | Date | undefined | number): Date => { | |
return new Date(DateUtils.addSeconds(seconds, d)) | |
}, | |
addMinutes: (minutes: number, d?: undefined | Date | undefined | number): number => { | |
d = typeof d === "number" ? d : typeof d === "undefined" ? Date.now() : d.getTime() | |
return d + minutes * MINUTES | |
}, | |
addMinutesDate: (minutes: number, d?: undefined | Date | undefined | number): Date => { | |
return new Date(DateUtils.addMinutes(minutes, d)) | |
}, | |
addHours: (hours: number, d?: undefined | Date | undefined | number): number => { | |
d = typeof d === "number" ? d : typeof d === "undefined" ? Date.now() : d.getTime() | |
return d + hours * HOURS | |
}, | |
addHoursDate: (hours: number, d?: undefined | Date | undefined | number): Date => { | |
return new Date(DateUtils.addHours(hours, d)) | |
}, | |
addDays: (days: number, d?: undefined | Date | undefined | number): number => { | |
d = typeof d === "number" ? d : typeof d === "undefined" ? Date.now() : d.getTime() | |
return d + days * DAYS | |
}, | |
addDaysDate: (days: number, d?: undefined | Date | undefined | number): Date => { | |
return new Date(DateUtils.addDays(days, d)) | |
}, | |
addYears: (years: number, d?: undefined | Date | undefined | number): number => { | |
d = typeof d === "number" ? d : typeof d === "undefined" ? Date.now() : d.getTime() | |
return d + years * YEARS | |
}, | |
addYearsDate: (years: number, d?: undefined | Date | undefined | number): Date => { | |
return new Date(DateUtils.addYears(years, d)) | |
}, | |
closest: (dates: Date[], fromDate: Date, allowPast = false): Date | undefined => { | |
if (!allowPast) { | |
dates = _.reject(dates, d => d.getTime() < fromDate.getTime()) | |
} | |
return closestTo(fromDate, dates) | |
}, | |
getNumberOfDaysPerMonth: _.memoize((): number[] => { | |
const ret = [] | |
for (let i = 0; i < 12; i++) { | |
const month = new Date(new Date().setUTCMonth(i + 1, 1)) | |
//when using setDate(0) you get the last day of the previous month | |
ret.push(new Date(month.setUTCDate(0)).getDate()) | |
} | |
return ret | |
}), | |
getStartOfDay(d?: undefined | Date | number): Date { | |
d = typeof d === "number" ? d : typeof d === "undefined" ? Date.now() : d.getTime() | |
const date = new Date(d) | |
date.setHours(0, 0, 0, 0) | |
return date | |
}, | |
getLastSunday(d: Date = new Date()) { | |
//https://stackoverflow.com/questions/12791378/get-the-most-recently-occurring-sunday | |
const t = new Date(d) | |
t.setDate(t.getDate() - t.getDay()) | |
t.setHours(0, 0, 0, 0) | |
return t | |
}, | |
} as const | |
export default DateUtils |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment