'Javascript Date and DST, add months modify hours
I know it's a famous question and i know to modify date, but i can't find anything regarding to this problem.
I have an ISO 8061 date and i want to add/substract month without modifying the hours and days:
I have the following input:
var add1Month = "2018-10-01T23:59:59.000Z";
var add10Month = "2018-12-01T00:00:00.000Z";
I want to add 1 month to add1Month and 10 to add10Month to have the following output:
var resAdd1Month = "2018-11-01T23:59:59.000Z";
var resAdd10Month = "2019-10-01T00:00:00.000Z";
Note that days and hours aren't modified and musn't been modified.
Usually i use the couple setMonth/getMonth or moment to modify it but i can't have the good result with it :
// With date
var add1Month = new Date("2018-10-01T23:59:59.000Z");
add1Month.setMonth(add1Month.getMonth() + 1);
console.log(add1Month.toJSON()); // 2018-11-02T00:59:59.000Z
var add10Month = new Date("2018-12-01T00:00:00.000Z")
add10Month.setMonth(add10Month.getMonth() + 10);
console.log(add10Month.toJSON()); // 2019-09-30T23:00:00.000Z
// With moment
var add1Month = new Date(moment("2018-10-01T23:59:59.000Z").add(1, 'M').format()).toJSON();
console.log(add1Month); // 2018-11-02T00:59:59.000Z
var add10Month = new Date(moment("2018-12-01T00:00:00.000Z").add(10, 'M').format()).toJSON()
console.log(add10Month); // 2019-09-30T23:00:00.000Z
There's an easy way to add month to a date without modifying date or hours in case of DST cross ?
The only solution i've found now it's to add it by hand :
function addMonth(isoDate, month){
var splitDate = isoDate.substr(0,10).split("-").map(function(e){return parseInt(e)});
// modify month
splitDate[1] = splitDate[1]+month;
// modify year if needed
splitDate[0] = splitDate[0] + Math.floor((splitDate[1] || -12) / 12);
// remove extra month aleady added in year and take account negative for month substraction
splitDate[1] = Math.abs((12 + splitDate[1]) % 12) || 12;
// reformat with 2 digit month and days
splitDate[1] = ("00"+splitDate[1]).substr(-2);
splitDate[2] = ("00"+splitDate[2]).substr(-2);
// manage end day of month 31/30 => 2018-07-31 - 1 month => 2018-06-31 => 2018-06-30 (remove day until valid date found)
while(new Date(splitDate[0]+"-"+splitDate[1]+"-"+splitDate[2]+isoDate.substr(10)).toJSON() !=
splitDate[0]+"-"+splitDate[1]+"-"+splitDate[2]+isoDate.substr(10)
)
{
splitDate[2] = parseInt(splitDate[2]) - 1;
splitDate[2] = ("00"+(splitDate[2])).substr(-2);
}
return splitDate[0]+"-"+splitDate[1]+"-"+splitDate[2]+isoDate.substr(10);
}
addMonth("2018-10-01T23:59:59.000Z", 1); // "2018-11-01T23:59:59.000Z"
addMonth("2018-12-01T00:00:00.000Z", 10); // "2019-10-01T00:00:00.000Z"
addMonth("2018-06-01T00:00:00.000Z", -11); // "2017-05-01T00:00:00.000Z"
addMonth("2018-03-31T23:59:59.000Z", -1); // "2018-02-28T23:59:59.000Z"
Thanks in advance !
Solution 1:[1]
That "by hand" can be beautified a bit:
function addMonths(date, add) {
const [day, time] = date.split("T");
let [years, months, days] = day.split("-");
months = +months + add;
years = +years + Math.floor(months / 12);
months = months % 12;
return [years, months.padStart(2, 0), days].join("-") + "T" + time;
}
Solution 2:[2]
Came across the same issue and worked around it with a rather simple way - assuming the shift is always gonna be small compared to a day's length, I round up the milliseconds difference to a day's length and re-apply the difference :
// Return new date with added days without DST shift (relative to UTC)
function add_days(date, count) {
return new Date(date.getTime() + count*86400000);
}
// Return new date with added months without DST shift (relative to UTC)
function add_months(date, count) {
let date2 = new Date(date);
date2.setMonth(date.getMonth() + count);
let numDays = Math.round((date2-date) / 86400000);
return add_days(date, numDays);
}
// Test results
for (let i=1; i<=12; i++) {
let d = new Date(new Date().getFullYear()+"-"+i+"-01Z");
console.log("Add 1m to", d.toISOString());
let d2 = new Date(d);
d2.setMonth(d.getMonth()+1);
console.log(" js :", d2.toISOString());
let d3 = add_months(d,1);
console.log(" fn :", d3.toISOString());
if (d2.toISOString() != d3.toISOString())
console.log("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
If your country/locale does not use DST, all results should be the same.
In my case I am experiencing the following differences :
Add 1m to 2022-03-01T00:00:00.000Z
js : 2022-03-31T23:00:00.000Z
fn : 2022-04-01T00:00:00.000Z
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add 1m to 2022-10-01T00:00:00.000Z
js : 2022-11-01T01:00:00.000Z
fn : 2022-11-01T00:00:00.000Z
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | PAPLabs |
