Skip to content

Instantly share code, notes, and snippets.

@evelant
Created September 6, 2023 15:02
Show Gist options
  • Save evelant/73203ff75b3e7d3e7a524c47c8d28694 to your computer and use it in GitHub Desktop.
Save evelant/73203ff75b3e7d3e7a524c47c8d28694 to your computer and use it in GitHub Desktop.
DateUtils
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