Adding 30 minutes to Date causes it to go back 30 minutes - javascript

I have a Javascript Date object equal to 00:30 and when doing:
date.setMinutes(date.getMinutes() + 30);
causes the date object to equal 00:00.
Does anyone know why this is happening?
Here is where the code is being used:
for (var i = openTime; i <= closeTime; i.setMinutes(i.getMinutes() + timeIncrement)) {
var time = i.getHours() + (i.getHours() == 0 ? '0' : '') + ':' + i.getMinutes() + (i.getMinutes() == 3 || i.getMinutes() == 0 ? '0' : '');
$(timeClientId).append($('<option />').val(time).text(time));
}
The above script creates a list of times available from 10:00am all the way to 02:00am the next day.
It runs fine until it reaches midnight 00:00 after many successful iterations.
Can anyone help?
Thanks!
ANSWER/SOLUTION:
This problem was due to a daylight saving issue, so this Saturday the clocks go forward. For some odd reason when adding 30 minutes to 12:30 it reset back to 12:00 using .setMinutes(). This kept it in an endless loop.
The solution was to add minutes using
i.setTime(i.getTime() + timeIncrement * 60 * 1000)
This sorted the issue.
Cheers for all your answers!

You are only setting the minutes. So of course 30 minutes + 30 minutes on the clock equals 60 minutes, i.e. 0 minutes.
Use this clever method (it's clever because it works with all rollovers!):
function addMinutes(inDate, inMinutes)
{
var newdate = new Date();
newdate.setTime(inDate.getTime() + inMinutes * 60000);
return newdate;
}
var date = new Date();
alert(addMinutes(date,-30));​

How are you initialising your date? setMinutes seems to work as expected so perhaps there is an error in your initial value. See below for my quick n dirty test.
var date = new Date(0);
document.write(date);
document.write("<br>");
date.setMinutes(30);
document.write(date);
document.write("<br>");
date.setMinutes(date.getMinutes() + 30);
document.write(date);
document.write("<br>");
Outputs:
Thu Jan 1 00:00:00 UTC 1970
Thu Jan 1 00:30:00 UTC 1970
Thu Jan 1 01:00:00 UTC 1970

date.js has a good way to do this:
new Date().add({ minutes: 30 });
or even more fluid:
new Date().add(30).minutes();

try this:
var date = new Date(2013,2,31,1,59);
document.write(date,"<br>");
date.setMinutes(date.getMinutes()+30);
document.write(date,"<br>");
date.setMinutes(date.getMinutes() + 30);
document.write(date,"<br>");
date.setMinutes(date.getMinutes() + 30);
document.write(date,"<br>");
Sun Mar 31 2013 01:59:00 GMT+0100 (CET)
Sun Mar 31 2013 01:29:00 GMT+0100 (CET)
Sun Mar 31 2013 01:59:00 GMT+0100 (CET)
Sun Mar 31 2013 01:29:00 GMT+0100 (CET)
So obviously the switch to summer time in Germany is causing this result.
But was this intended?
Entering 2013,2,31,2,59 gives same result, as if the hour 1-2 did not exist.
it does not look right to me.

Related

Google Apps Script add month to a date until specific date is reached

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.

Why is getDate() a month off?

I'm trying to iterate over objects that have a date within a specific time frame. These time frames are days in the past week bounded by startDate and endDate.
I have a for loop that sets the startDate to the beginning of the day, and endDate will be the end of the day.
let startDate = new Date(); // startDate: Start bound of average execution time calculation
let endDate = new Date(); // endDate: End bound of average execution time calculation
for (let i = 0; i < 7; i++) {
// i is used to decrement the startDate to i days from today
// set startDate to beginning of the day
startDate = new Date();
startDate.setDate(startDate.getDate() - i);
startDate.setHours(0, 0, 0, 0);
console.log(i + ": " + startDate);
// set endDate to end of the day
endDate.setDate(startDate.getDate());
endDate.setHours(23, 59, 59, 999);
console.log(i + ": " + endDate);
However, even though I'm setting the endDate to the startDate, the console tells me that the endDate is one month off. Why is this happening?
The console shows this:
0: Sat Aug 01 2020 00:00:00 GMT-0700 (Mountain Standard Time)
0: Sat Aug 01 2020 23:59:59 GMT-0700 (Mountain Standard Time)
1: Fri Jul 31 2020 00:00:00 GMT-0700 (Mountain Standard Time)
1: Mon Aug 31 2020 23:59:59 GMT-0700 (Mountain Standard Time)
2: Thu Jul 30 2020 00:00:00 GMT-0700 (Mountain Standard Time)
2: Sun Aug 30 2020 23:59:59 GMT-0700 (Mountain Standard Time)
3: Wed Jul 29 2020 00:00:00 GMT-0700 (Mountain Standard Time)
3: Sat Aug 29 2020 23:59:59 GMT-0700 (Mountain Standard Time)
4: Tue Jul 28 2020 00:00:00 GMT-0700 (Mountain Standard Time)
4: Fri Aug 28 2020 23:59:59 GMT-0700 (Mountain Standard Time)
5: Mon Jul 27 2020 00:00:00 GMT-0700 (Mountain Standard Time)
5: Thu Aug 27 2020 23:59:59 GMT-0700 (Mountain Standard Time)
Think like arrays, where the first element is 0. The same is true here. The months of the year will be 0 - 11, rather than 1 - 12. The getMonth() method returns the month in the specified date according to local time, as a zero-based value (where zero indicates the first month of the year).
Also, keep this in mind: With setDate() the expected values are 1-31, but other values are allowed: 0 will result in the last day of the previous month. -1 will result in the day before the last day of the previous month.
So, I ended up just fixing it by not referring to startDate at all. I'm not sure why I can't call getDate() without getting the wrong date, but for now, I'll just set endDate by repeating whatever I did to startDate.
// set startDate to beginning of the day
startDate = new Date();
startDate.setDate(startDate.getDate() - i);
startDate.setHours(0, 0, 0, 0);
console.log(i + ": " + startDate);
// set endDate to end of the day
endDate = new Date();
endDate.setDate(endDate.getDate() - i);
endDate.setHours(23, 59, 59, 59);
console.log(i + ": " + endDate);
The reason it happens is because you are setting the date but not the month, so when the loop crosses a month boundary, the start goes to the previous month but end stays in the current month.
When run on 1 Aug, in the first iteration both start and end are set to 1 Aug.
In the next iteration, 1 is subtracted from the start so it's 31 July, but then just the date is set for end, so it goes to 31 Aug, and so on…
A reworking of the code:
// Start at random time on 1 Aug
let d = new Date(2020,7,1,15,23,51,3);
for (let start, end, i=0; i<7; i++) {
start = new Date(d.setHours(0,0,0,0));
console.log(i + ' : ' + start.toString());
end = new Date(d.setHours(23,59,59,999));
console.log(i + ' : ' + end.toString());
d.setDate(d.getDate() - 1);
}

Compare time only when time zone changed in 1920

I have a time value of "7:00:00 AM" in my spreadsheet. Hypothetically, I want to compare this to another hour/minute time value (date agnostic) in my Google Apps Script code. When the script reads the value, because no date was attached, it assumes a date of 30-Dec-1899. Now, the issue is, because the time zone where I currently am was GMT+06:42:04 back in 1899, the time value appears as "07:17:56".
How can I compare this time with another to know if the hours and minutes match? This would ideally be done with vanilla JS (i.e. I prefer not to use Moment.js).
function getCellValue() {
var cellValue = SpreadsheetApp.getActiveSheet().getRange("A1").getValue();
Logger.log(cellValue); // Sat Dec 30 07:00:00 GMT+06:42 1899
Logger.log(cellValue.getHours()); // 7.0
Logger.log(cellValue.getMinutes()); // 17.0
Logger.log("Untouched: " + cellValue); // Untouched: Sat Dec 30 1899 07:17:56 GMT+0700 (ICT)
var formattedTime = Utilities.formatDate(cellValue, "GMT+7", "EEE MMM dd yyyy HH:mm z");
Logger.log("Formatted: " + formattedTime); // Formatted: Sat Dec 30 1899 07:17 GMT+07:00
var updatedCellValue = new Date(cellValue.setFullYear(2018));
Logger.log("New Year: " + updatedCellValue); // New Year: Sun Dec 30 2018 07:17:56 GMT+0700 (ICT)
var updatedDate = new Date(cellValue);
Logger.log("Converted: " + updatedDate); // Converted: Sat Dec 30 1899 07:17:56 GMT+0700 (ICT)
Logger.log("Converted & formatted: " + Utilities.formatDate(updatedDate, "GMT+7", "EEE MMM dd yyyy HH:mm z")); // Converted & formatted: Sat Dec 30 1899 07:17 GMT+07:00
}
Both my spreadsheet and script are set to "(GMT+07:00) Bangkok" time zone.
(I understand that 7am today and 7am in 1899 were not the same, but that is irrelevant to this hypothetical. If a user enters 7am in the spreadsheet, they probably mean that to be 7am this century.)
getDisplayValue(), not getValue()
Create a new Date object from value
Use Utilities.formatDate() to get the hour & minutes
Fixed code:
function getCellValue() {
var cellValue = SpreadsheetApp.getActiveSheet().getRange("A1").getDisplayValue();
Logger.log(cellValue); // 7:00:00 AM (or 0.29, if formatted as number)
cellValue = new Date(cellValue);
var hours = Utilities.formatDate(cellValue, "GMT+7", "HH");
var minutes = Utilities.formatDate(cellValue, "GMT+7", "mm");
Logger.log (hours + ":" + minutes); // 07:00
}

Subtracting 1 month to 2015-12-31 gives 2015-12-01

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)

Outputting dates in a JavaScript loop from today until specific number of days

Before I start let me just say how much I hate the JavaScript Date() object! I really hate it and I would usually use a library like date.js however I have to use my own JavaScript for this problem....
Okay, I wish to create an array of dates from today until a specific date or for a specific number of days... in this example I will set it for a number of specific days (like 365). I will use this array to populate a select in my application later.
I wish to capture today's date, then add an integer to it to get the next date, then the next, etc, etc... and format these dates as dd/mm/yyyy (I haven't included this part).
So here is my code and I have noticed that my loop starts to jump/miss days after the second index/loop
var today = new Date(),
dd = today.getDate(),
mm = today.getMonth() + 1,
yyyy = today.getFullYear(),
today,
startDate,
d,
i,
dateArray = [];
if( dd < 10 ){
dd='0' + dd
}
if( mm < 10 ){
mm='0' + mm
}
startDate = yyyy +'-'+ mm +'-' + dd;
d = new Date(startDate)
for(i = 0; i < 365; i++){
d.setDate(d.getDate() + i);
// I will format 'd' to dd/mm/yyyy later
dateArray.push(d)
console.log(d);
}
My console is logging the following (I have shown the first 4 outputs to demonstrate my problem), notice how we miss Friday, then Sunday and Monday:
> Wed Apr 16 2014 02:00:00 GMT+0200 (CEST)
> Thu Apr 17 2014 02:00:00 GMT+0200 (CEST)
> Sat Apr 19 2014 02:00:00 GMT+0200 (CEST)
> Tue Apr 22 2014 02:00:00 GMT+0200 (CEST)
I am obviously going about this the wrong way can someone please advise me on how to use the Date() object correctly and where I am going wrong with my loop.
Thanks in advance
Due to your loop, you're adding 1, 2, then 3 to d. If you want them to be consecutive you change + i to + 1, like:
for(i = 0; i < 365; i++){
d.setDate(d.getDate() + 1);
// I will format 'd' to dd/mm/yyyy later
dateArray.push(d)
console.log(d);
}
You are using the same object over and over. Take this for example:
You start with Apr 16th
You add 1 to Apr 16th, and it becomes Apr 17th
Now you add 2 to the date, which at the moment is Apr 17th, so it becomes Apr 19th
Now you add 3 to the date, which at the moment is Apr 19th, so it becomes Apr 22nd
...
You should see what I am talking about. Finally, you should do something like this:
d = new Date(startDate)
for(i = 0; i < 365; i++){
c = new Date().setDate(d.getDate() + i);
dateArray.push(c)
console.log(c);
}
Or as the Tom Fenech said, you can just add +1 instead of +i to your current date object (d).

Categories

Resources