I've been using moment.js for a short while now, and it's made date manipulation a lot easier but I have a specific case that is failing, and I can't see why.
When calculating the diff between today (31st October 2013) and the 1st February 2014, the months diff comes back as 2, although there are 3 complete months and one day between the two dates.
Diff between 31st October and 31st January works fine: 3 months and zero days.
var mStartDate = moment([ periodStartDate.getFullYear(), periodStartDate.getMonth(), periodStartDate.getDate() ]);
var mTermDate = moment([ someDate.getFullYear(), someDate.getMonth(), someDate.getDate() ]);
console.log('periodStartDate: ' + periodStartDate);
console.log('someDate: ' + someDate);
// Years
var yearsDiff = mTermDate.diff(mStartDate, 'years');
// Months
var monthsDiff = mTermDate.diff(mStartDate, 'months', true);
The console logs the following:
periodStartDate: Thu Oct 31 2013 11:13:51 GMT+0000 (GMT)
someDate: Sat Feb 01 2014 11:13:51 GMT+0000 (GMT)
monthsDiff: 2
If I pass true as the boolean not to round, the months diff is
monthsDiff: 2.983050847457627
Is this just a bug in Moment.js.diff()? Every single one of my other test cases pass successfully.
I think this has to do with the 'special handling' as described in The Fine Manual:
It is optimized to ensure that two months with the same date are
always a whole number apart.
So Jan 15 to Feb 15 should be exactly 1 month.
Feb 28 to Mar 28 should be exactly 1 month.
Feb 28 2011 to Feb 28 2012 should be exactly 1 year.
Moment.js applies this special handling when dealing with 31 Jan and 31 Oct (having the same day):
// 31 Oct 2013 - 1 Feb 2014
> moment([2014, 1, 1]).diff(moment([2013, 9, 31]), 'months', true)
2.983050847457627
// 31 Oct 2013 - 31 Jan 2014
> moment([2014, 0, 31]).diff(moment([2013, 9, 31]), 'months', true)
3
// 31 Oct 2013 - 30 Jan 2014
> moment([2014, 0, 30]).diff(moment([2013, 9, 31]), 'months', true)
2.967741935483871
So the 2.98 value is correct, it's just that the second example turns the result into a 'calender months' difference.
(as for rounding down to 2, that's also documented on the same page)
I went a different route trying to get the difference between two months
function getAbsoluteMonths(momentDate) {
var months = Number(momentDate.format("MM"));
var years = Number(momentDate.format("YYYY"));
return months + (years * 12);
}
var startMonths = getAbsoluteMonths(start);
var endMonths = getAbsoluteMonths(end);
var monthDifference = endMonths - startMonths;
This made sense to me and since moment is doing some strange things with diff I just decided to make it clear what my result will be.
Simple And Easy Solution with correct value difference between two months if you are using moment Library
const monthDifference = moment(new Date(endDate)).diff(new Date(startDate), 'months', true);
If you want to add the number of days in endDate
const monthDifference = moment(new Date(endDate.add(1, 'days'))).diff(new Date(startDate), 'months', true);
Try out this method;
function getMonthDifference(startDate, endDate) {
const increment = startDate.getMonth() === endDate.getMonth() ? 2 : 1;
const diff = moment(endDate).diff(moment(startDate), 'months', true);
return Math.ceil(diff) + increment; // this increment is opitional and totally depends on your need.
}
Related
I have the following code using Google Apps Script, but when I log it out I get the following results. I want GAS to log the next month and stop once it gets to "lastDateofYear ". For whatever reason, the year doesn't change in my results, it just keeps repeating the current year. Please help.
var thisDate = "Mon Dec 20 00:00:00 GMT-06:00 2021";
var nextYear = Number(currentYear)+1;
var lastDateofYear = new Date("12-31-"+nextYear);
for(var i=thisDate; i <= lastDateofYear; ){
var currentiDate = new Date(i);
var month = currentiDate.getMonth()+1;
i.setMonth((month) % 12);
i.setDate(currentiDate.getDate());
Logger.log(currentiDate);
}
RESULTS:
Mon Dec 20 00:00:00 GMT-06:00 2021
Wed Jan 20 00:00:00 GMT-06:00 2021
Sat Feb 20 00:00:00 GMT-06:00 2021
Sat Mar 20 00:00:00 GMT-05:00 2021
Tue Apr 20 00:00:00 GMT-05:00 2021
Thu May 20 00:00:00 GMT-05:00 2021
Sun Jun 20 00:00:00 GMT-05:00 2021
Tue Jul 20 00:00:00 GMT-05:00 2021
Fri Aug 20 00:00:00 GMT-05:00 2021
Mon Sep 20 00:00:00 GMT-05:00 2021
Wed Oct 20 00:00:00 GMT-05:00 2021
Sat Nov 20 00:00:00 GMT-06:00 2021
Mon Dec 20 00:00:00 GMT-06:00 2021
Wed Jan 20 00:00:00 GMT-06:00 2021
Sat Feb 20 00:00:00 GMT-06:00 2021
Sat Mar 20 00:00:00 GMT-05:00 2021
Tue Apr 20 00:00:00 GMT-05:00 2021
As I understand it, you want to print each month from the given date to the last month of the next year of the given date in the log.
You can do this in the following code:
let start = new Date("Mon Dec 20 00:00:00 GMT-06:00 2021");
let currentYear = new Date().getFullYear();
let nextYear = currentYear + 1;
let end = new Date(nextYear, 11, 31);
while (start <= end) {
// You can use Logger.log() here if you want. I use console.log() for demo purpose
console.log(new Date(start).toDateString());
start.setMonth(start.getMonth() + 1);
}
If I got any part wrong, feel free to point it out to me in the comments.
There is a lot to say about your code:
var thisDate = "Mon Dec 20 00:00:00 GMT-06:00 2021";
That timestamp format is not supported by ECMA-262, so don't use the built–in parser to parse it, see Why does Date.parse give incorrect results?
var nextYear = Number(currentYear)+1;
Where does currentYear come from?
var lastDateofYear = new Date("12-31-"+nextYear);
Parsing of an unsupported format, see above. In Safari it returns an invalid date.
for(var i=thisDate; i <= lastDateofYear; ){
Sets i to the string value assigned to thisDate. Since lastDateOfYear is an invalid date in Safari and Firefox, so the test i <= NaN is never true and the loop is never entered.
var currentiDate = new Date(i);
Parses i, see above.
var month = currentiDate.getMonth()+1;
i.setMonth((month) % 12);
i is a string, which doesn't have a setMonth method so I'd expect a Type error like "i.setMonth is not a function" if the loop actually runs.
i.setDate(currentiDate.getDate());
Another Type error as above (but it won't get this far).
Logger.log(currentiDate);
}
It seems you want to sequentially add 1 month to a date until it reaches the same date in the following year. Trivially, you can just add 1 month until you get to the same date next year, something like:
let today = new Date();
let nextYear = new Date(today.getFullYear() + 1, today.getMonth(), today.getDate());
let result = [];
do {
result.push(today.toString());
today.setMonth(today.getMonth() + 1);
} while (today <= nextYear)
However, adding months is not that simple. If you add 1 month to 1 Jan, you'll get 2 or 3 Mar depending on whether it's a leap year or not. And adding 1 month to 31 Aug will return 1 Oct.
Many "add month" functions check to see if the date rolls over an extra month and if it does, set the date back to the end of the previous month by setting the date to 0, so 31 Jan + 1 month gives 28 or 29 Feb.
But if you cycle over a year using that algorithm, you'll get say 31 Jan, 28 Feb, 28 Mar, 28 Apr etc. rather than 31 Jan, 28 Feb, 31 Mar, 30 Apr, etc.
See JavaScript function to add X months to a date and How to add months to a date in JavaScript?
A more robust way is to have a function that adds n months to a date and increment the months to add rather than the date itself so the month–end problem can be dealt with separately for each addition, e.g.
/* Add n months to a date. If date rolls over an extra month,
* set to last day of previous month, e.g.
* 31 Jan + 1 month => 2 Mar, roll back => 28 Feb
*
* #param {number} n - months to add
* #param {Date} date - date to add months to, default today
* #returns {Date} new Date object, doesn't modify passed Date
*/
function addMonths(n, date = new Date()) {
let d = new Date(+date);
let day = d.getDate();
d.setMonth(d.getMonth() + n);
if (d.getDate() != day) d.setDate(0);
return d;
}
/* return array of n dates at 1 month intervals. List is
* inclusive so n + 1 Dates returned.
*
* #param {Date} start - start date
* #param {number} n - number of months to return
* #returns {Array} array of Dates
*/
function getMonthArray(n, start = new Date()) {
let result = [];
for (let i=0; i<n; i++) {
result.push(addMonths(i, start));
}
return result;
}
// Examples
// Start on 1 Dec
getMonthArray(12, new Date(2021,11,1)).forEach(
d => console.log(d.toDateString())
);
// Start on 31 Dec
getMonthArray(12, new Date(2021,11,31)).forEach(
d => console.log(d.toDateString())
);
The functions don't attempt to parse timestamps to Dates, that responsibility is left to the caller.
I have problem with boostrap- datepicker. The core of my problem in such example
var array = [2017, 12, 27];
var date = new Date(array);
console.log(date);
//Wed Dec 27 2017 00:00:00 GMT+0300 (RTZ 2 (winter))
var day = date.getUTCDate();
console.log(day);
//26 -> I WANT 27
console.log(month);
//11 -> I WANT 12
console.log(year);
//2017
As you can see at the end i get 26 (the day what i need is 27) and 11 (the month what i need is 12).
How can i change this code (remove my timezone) to get 27, 12, 2017
There are two problems you're running into:
The difference between your timezone (GMT+0300) and UTC means that midnight December 27th 2017 in your timezone is 9p.m. December 26th 2017 UTC.
I'm assuming you got month via getUTCMonth. Month values start with 0 = January, which is why you're getting 11.
To get midnight December 27th 2017 in a Date, use Date.UTC:
var dt = new Date(Date.UTC(2017, 12 - 1, 27));
console.log(dt.getUTCDate()); // 27
console.log(dt.getUTCMonth() + 1); // 12
console.log(dt.getUTCFullYear()); // 2017
Note the + 1 after getUTCMonth, since again, month numbers are 0 = January through 11 = December.
I'm trying to subtract one month from 2015-12-31 but it gives me 2015-12-01 instead of 2015-11-30. Why ?
Code:
var date1 = new Date('2015-12-31');
var date2 = new Date(date1);
date2.setMonth(date1.getMonth() - 1);
console.log(date1);
console.log(date2);
Output:
Thu Dec 31 2015 01:00:00 GMT+0100 (CET)
Tue Dec 01 2015 01:00:00 GMT+0100 (CET)
Any workaround?
When subtracting months, you can check whether the day of the adjusted Date is different to the day of the initial Date. If it is, then it must have rolled over to the next month, so set the day of the adjusted Date to 0, so it goes to the last day of the previous month, e.g.
function subtractMonths(date, months) {
var day = date.getDate();
date.setMonth(date.getMonth() - months);
if (date.getDate() != day) date.setDate(0);
return date;
}
// 31 Mar 2016 - 1 month = 29 Feb 2015
[[new Date(2016,2,31), 1],
// 29 Feb 2016 - 12 months = 28 Feb 2015
[new Date(2016,1,29), 12],
// 15 Feb 2016 - 3 months = 15 Nov 2015
[new Date(2016,1,15), 3]].forEach(function(arr){
document.write('<br>' + subtractMonths(arr[0], arr[1]));
})
The same algorithm can be used for adding months. Note that this is why date arithmetic is not symmetric, e.g.
31 May + 1 month => 30 June
30 June - 1 month => 30 May
i.e. If A + B = C, then C - B = A may or may not be true (and vice versa).
Try this
var date1 = new Date('2015-12-31');
var date2 = new Date(date1);
date2.setDate(date2.getDate()-date1.getDate());
alert(date2)
Per the setMonth documentation, ‘If you do not specify the [optional] dayValue parameter, the value returned from the getDate() method is used’. Since you’re not specifying the optional parameter, your code tries to set the date to 2015-11-31, which isn’t valid. JavaScript resolves this situation by setting the date to one day after 2015-11-30, which is 2015-12-01.
As for a workaround, it depends on what you’re actually trying to do. Are you trying to go 31 days back from 31 December? Or are you trying to get the last day of the month before December? Date semantics are extremely complicated; what are you going to do when the inevitable edge cases arise?
It is producing the requested result, which is subtracting 1 month from the date given. But remember a month is a variable amount of time. November 31 is actually December 1 (just like November 55th would actually be December 25, Christmas). To get the last day of the previous month you could do something like this:
var date = new Date('2015-12-31');
date.setDate(-1)
This question already has answers here:
How to get first and last day of the current week in JavaScript
(32 answers)
Closed 7 years ago.
I was using a function to return me the start and end day of a given week number from the year. Here is the function:
function getWeekRange(week){
var d = new Date("Jan 01, " + $scope.today.getFullYear() + " 01:00:00");
var w = d.getTime() + 604800000 * (week);
var d1 = new Date(w);
var d2 = new Date(w + 518400000);
return {
startDate: d1,
endDate: d2,
};
}
I set the year dynamically, and let's see an example where the year is 2015. Considering the week number starting by 0, if I use getWeekRange(0) I will receive the following result:
{
startDate: Thu Jan 01 2015 01:00:00 GMT-0200 (BRST),
endDate: Wed Jan 07 2015 01:00:00 GMT-0200 (BRST)
}
The problem is that this code does not consider the year of 2015 starting on a Thursday. The correct result for getWeekRange(0) should be:
{
startDate: Thu Jan 01 2015 01:00:00 GMT-0200 (BRST),
endDate: Sat Jan 03 2015 01:00:00 GMT-0200 (BRST)
}
and the result for getWeekRange(1) should be:
{
startDate: Sun Jan 04 2015 01:00:00 GMT-0200 (BRST),
endDate: Sat Jan 10 2015 01:00:00 GMT-0200 (BRST)
}
Does anyone have a clue?
-- EDIT --
My question is different from this one, because I don't have a given day of the year, I have only a week number of the year (from 0 to 51), and my case considers that the first week of the year is only a part of a full week, as mentioned by likeitlikeit.
I could find a simple solution for my question, since only the first week of my year could cause me problems. Here is the code:
function getWeekRange(week){
var d = new Date("Jan 01, " + $scope.today.getFullYear() + " 01:00:00");
var firstWeekDays = 7 - d.getDay();
var d1, d2;
if(week > 0) {
var w = d.getTime() + 604800000 * (week-1) + 24*60*60*1000 * firstWeekDays;
d1 = new Date(w);
d2 = new Date(w + 518400000);
} else {
d1 = d;
d2 = new Date(d1.getTime() + 24*60*60*1000 * (6 - d1.getDay()));
}
return {
startDate: d1,
endDate: d2,
};
}
There are 2 main diferences from the initial code.
For the week of the year I simply get the initial day of the year, and then based on the day of the week it starts I can find the end day of the week
For the other weeks, I sum 7*week-1 days to initial day (this sum does not consider the number of days of the first week), and also add firstWeekDays which is the number of days of first week (because it is not always 7 as the other weeks).
If anyone has a better solution, I will be glad to listen.
This answer shows how to get the first day of a week using getDate to calculate the offset from the current day.
Using this method, you can use getDate to determine which day of the week the first day of the year falls on. You can then subtract the last day of the week from this value to know how many days to add to your d2 in order to compute the date for Saturday by adjusting your date from the first day of the year + the offset in days till the end of the week for Jan 1.
I am doing a check of two different date times to see if one is greater than the other:
Here is my (now) current date time: Thu Aug 01 2013 10:27:40 GMT-0500 (CDT)
And here is my date time that I am seeing if it is greater or less than: Thu Aug 01 2013 12:15:00 GMT-0500 (CDT) - (that should be 12:15 am by the way)
Here is my code:
var current_date_time = new Date();
var date_time_checking_against = new Date(date_segment[0], date_segment[1]-1, date_segment[2], time_segment[0], time_segment[1]);
Which comes out to Thu Aug 01 2013 12:15:00 GMT-0500 (CDT). And then I am doing a simple if check:
if(current_date_time >= date_time_checking_against){ }
This is not working as 10:27:40 is not after 12:15:00. But it should be, seeing as how both times are AM. I need to know if this is the right way, or if there is a way to change it to 24 hour format or add am pm in there somehow. Any help is greatly appreciated, let me know if you need more clarity.
Thanks!
EDIT:
Here is the date time array:
var date_time_str = date+' '+time;
date_time_str = date_time_str.split(' ');
["2013-08-01", "12:15", "am"] // result from split above
var date_segment = date_time_str[0].split('-');
var time_segment = date_time_str[1].split(':');
var date_time_checking_against = new Date(date_segment[0], date_segment[1]-1, date_segment[2], time_segment[0], time_segment[1]);
Given the following data sources, this is how you'd properly create the Date object for it...
date_time_str = ["2013-08-01", "12:15", "am"];
var date_segment = date_time_str[0].split('-');
var time_segment = date_time_str[1].split(':');
var date_time_checking_against = new Date(
date_segment[0], // year
date_segment[1]-1, // month of year
date_segment[2], // day of month
(time_segment[0]%12) + (date_time_str[2] == 'pm' ? 12 : 0), // hour of day
time_segment[1]); // minute of hour
console.log(new Date() >= date_time_checking_against); // true, we've already passed this time