Skip to content

Instantly share code, notes, and snippets.

@turlockmike
Created November 3, 2020 06:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save turlockmike/b49792a0235854fcbcf229e29468ec0f to your computer and use it in GitHub Desktop.
Save turlockmike/b49792a0235854fcbcf229e29468ec0f to your computer and use it in GitHub Desktop.
function addMonths(date: Date, months: number): Date {
const newDate = new Date(date)
const dayOfMonth = newDate.getDate()
// The JS Date object supports date math by accepting out-of-bounds values for
// month, day, etc. For example, new Date(2020, 1, 0) returns 31 Dec 2019 and
// new Date(2020, 13, 1) returns 1 Feb 2021. This is *almost* the behavior we
// want except that dates will wrap around the end of a month, meaning that
// new Date(2020, 13, 31) will return 3 Mar 2021 not 28 Feb 2021 as desired. So
// we'll default to the end of the desired month by adding 1 to the desired
// month and using a date of 0 to back up one day to the end of the desired
// month.
const endOfDesiredMonth = new Date(newDate.getTime())
const monthsPlusOne = Math.abs(months + 1) * (Math.sign(months) !== 0 ? Math.sign(months) : 1)
endOfDesiredMonth.setMonth(newDate.getMonth() + monthsPlusOne, 0)
const daysInMonth = endOfDesiredMonth.getDate()
if (dayOfMonth >= daysInMonth) {
// If we're already at the end of the month, then this is the correct date
// and we're done.
// if we're going back over daylight savings, we find the time-zone difference
// and adjust, so that we don't end up being one day off
const timezoneDiff = date.getTimezoneOffset() - endOfDesiredMonth.getTimezoneOffset()
endOfDesiredMonth.setMinutes(endOfDesiredMonth.getMinutes() + timezoneDiff)
return endOfDesiredMonth
} else {
// Otherwise, we now know that setting the original day-of-month value won't
// cause an overflow, so set the desired day-of-month. Note that we can't
// just set the date of `endOfDesiredMonth` because that object may have had
// its time changed in the unusual case where where a DST transition was on
// the last day of the month and its local time was in the hour skipped or
// repeated next to a DST transition. So we use `date` instead which is
// guaranteed to still have the original time.
newDate.setFullYear(
endOfDesiredMonth.getFullYear(),
endOfDesiredMonth.getMonth(),
dayOfMonth
)
// if we're going back over daylight savings, we find the time-zone difference
// and adjust, so that we don't end up being one day off
const timezoneDiff = date.getTimezoneOffset() - newDate.getTimezoneOffset()
newDate.setMinutes(newDate.getMinutes() + timezoneDiff)
return newDate
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment