Javascript date parsing bug - fails for dates in June (??) - javascript

I have some javascript which parses an ISO-8601 date. For some reason, it is failing for dates in June. But dates in July and May work fine, which doesn't make sense to me. I'm hoping a fresh set of eyes will help, because I can't see what I'm doing wrong here.
Function definition (with bug)
function parseISO8601(timestamp)
{
var regex = new RegExp("^([\\d]{4})-([\\d]{2})-([\\d]{2})T([\\d]{2}):([\\d]{2}):([\\d]{2})([\\+\\-])([\\d]{2}):([\\d]{2})$");
var matches = regex.exec(timestamp);
if(matches != null)
{
var offset = parseInt(matches[8], 10) * 60 + parseInt(matches[9], 10);
if(matches[7] == "-")
offset = -offset;
var date = new Date();
date.setUTCFullYear(parseInt(matches[1], 10));
date.setUTCMonth(parseInt(matches[2], 10) - 1); //UPDATE - this is wrong
date.setUTCDate(parseInt(matches[3], 10));
date.setUTCHours(parseInt(matches[4], 10));
date.setUTCMinutes(parseInt(matches[5], 10) - offset);
date.setUTCSeconds(parseInt(matches[6], 10));
date.setUTCMilliseconds(0);
return date;
}
return null;
}
Test code
alert(parseISO8601('2009-05-09T12:30:00-00:00').toUTCString());
alert(parseISO8601('2009-06-09T12:30:00-00:00').toUTCString());
alert(parseISO8601('2009-07-09T12:30:00-00:00').toUTCString());
Output
Sat, 09 May 2009 12:30:00 GMT
Thu, 09 Jul 2009 12:30:00 GMT
Thu, 09 Jul 2009 12:30:00 GMT
Update
Thanks for the quick answers, the problem was that the Date object was initially today, which happened to be July 31. When the month was set to June, before I changed the day, it was temporarily June 31, which got rolled forward to July 1.
I've since found the following to be a cleaner implementation, as it sets all the date attributes at once:
function parseISO8601(timestamp)
{
var regex = new RegExp("^([\\d]{4})-([\\d]{2})-([\\d]{2})T([\\d]{2}):([\\d]{2}):([\\d]{2})([\\+\\-])([\\d]{2}):([\\d]{2})$");
var matches = regex.exec(timestamp);
if(matches != null)
{
var offset = parseInt(matches[8], 10) * 60 + parseInt(matches[9], 10);
if(matches[7] == "-")
offset = -offset;
return new Date(
Date.UTC(
parseInt(matches[1], 10),
parseInt(matches[2], 10) - 1,
parseInt(matches[3], 10),
parseInt(matches[4], 10),
parseInt(matches[5], 10),
parseInt(matches[6], 10)
) - offset*60*1000
);
}
return null;
}

The problem is that today is July 31.
When you set:
var date = new Date();
Then date.getUTCDate() is 31. When you set date.setUTCMonth(5) (for June), you are setting date to June 31. Because there is no June 31, the JavaScript Date object turns it into July 1. So immediately after setting calling date.setUTCMonth(5) if you alert(date.getUTCMonth()); it will be 6.
This isn't unique to June. Using your function on the 31st of any month for any other month that does not have 31 days will exhibit the same problem. Using your function on the 29th (non-leap years), 30th or 31st of any month for February would also return the wrong result.
Calling setUTC*() in such a way that any rollovers are overwritten by the correct value should fix this:
var date = new Date();
date.setUTCMilliseconds(0);
date.setUTCSeconds(parseInt(matches[6], 10));
date.setUTCMinutes(parseInt(matches[5], 10) - offset);
date.setUTCHours(parseInt(matches[4], 10));
date.setUTCDate(parseInt(matches[3], 10));
date.setUTCMonth(parseInt(matches[2], 10) - 1);
date.setUTCFullYear(parseInt(matches[1], 10));

The date object starts off with the current date.
It's the 31st today so setting 2009-06-09 gives:
var date = new Date(); // Date is 2009-07-31
date.setUTCFullYear(2009); // Date is 2009-07-31
date.setUTCMonth(6 - 1); // Date is 2009-06-31 = 2009-07-01
date.setUTCDate(9); // Date is 2009-07-09
If you set the date to the 1st before you begin, then you should be safe.

It's because today is July 31. Grant explained the problem. Here's what I believe is a simpler solution. Initialize your date on Jan 1.
var date = new Date(2009,0,1,0,0,0);

It's the order in which you are changing the date.
The date starts out as July 31, so the set of the month fails because there is no 31 in June. (Actually it is rolling over to jul-1.)
After setting the full year, add this:
date.setYUTCDate(1);
It makes it the first of the month wherein every month is valid.

Looks like a bug?
C:\Documents and Settings\me>java org.mozilla.javascript.tools.shell.Main
Rhino 1.7 release 2 2009 03 22
js> date = new Date();
Fri Jul 31 2009 15:18:38 GMT-0400 (EDT)
js> date.setUTCMonth(5); date.toUTCString();
Wed, 01 Jul 2009 19:18:38 GMT
js> date.setUTCMonth(5); date.toUTCString();
Mon, 01 Jun 2009 19:18:38 GMT
EDIT: Nevermind I guess. Question already answered by somebody more knowledgable.

Related

Javascript Date Object giving out some unusual results [duplicate]

For m = range 1-12 it works as expected, increasing month by 1.
For m = range 13- it doesn't work as expected, increasing year by 1.
As m exceeds 12 inside loop I was expecting result:
Sun Feb 28 2015 00:00:00 GMT+0400 (GET)
Tue Mar 28 2015 00:00:00 GMT+0400 (GET)
Sat Apr 28 2015 00:00:00 GMT+0400 (GET)
Instead I get:
Sun Feb 28 2016 00:00:00 GMT+0400 (GET)
Tue Mar 28 2017 00:00:00 GMT+0400 (GET)
Sat Apr 28 2018 00:00:00 GMT+0400 (GET)
...
var loanAmount = 3800,
loanInterest = 32, // %
loanDuration = 48, // Month
loanGrace = 0,
loanFee = 1, // %
loanInitMonth = 0,
loanInitDay = 28,
loanInitYear = 2014;
var loanStart = new Date(loanInitYear,loanInitMonth,loanInitDay);
for (var m = loanInitMonth; m < loanDuration; m++) {
var d = loanStart;
d.setMonth(m);
console.log(m);
console.log(d);
}
Jsfiddle
As setMonth description states:
If a parameter you specify is outside of the expected range, setMonth
attempts to update the date information in the Date object
accordingly. For example, if you use 15 for monthValue, the year will
be incremented by 1 (year + 1), and 3 will be used for month.
I'm obviously not getting this behavior.
Browser Chrome.
When you do this, you are copying the reference to the Date object instead of creating a new copy of the object:
var d = loanStart;
Then, when you change the date in the d variable, you are also changing the date in the loanStart variable, as it's the same object.
Create a copy of the Date object:
var d = new Date(loanStart.getTime());
The problem is that you aren't adding the time to the original date, you're adding it to the current date. So setting the month > 12 to the current date will always add at least one year, which will increment the year.
You have two solutions:
Make sure you're always modifying the original date by maintaining a copy of that date.
Do real date math - trying to set the month (and rely on side-effects) when what you really want to do is increment the month makes for rather confusing code and will have edge cases when, for example, the day of the month isn't a valid day for that month.
Instead of passing m to setMonth, do d.setMonth(d.getMonth()+1) like below:
var loanAmount = 3800,
loanInterest = 32, // %
loanDuration = 48, // Month
loanGrace = 0,
loanFee = 1, // %
loanInitMonth = 0,
loanInitDay = 28,
loanInitYear = 2014;
var loanStart = new Date(loanInitYear,loanInitMonth,loanInitDay);
for (var m = loanInitMonth; m < loanDuration; m++) {
var d = loanStart;
d.setMonth(d.getMonth()+1);
console.log(m);
console.log(d);
}

Add one Day at the Date change also the hour

I have issue with a date.
I have two date (start and end) and I need to build an array of all date between these two.
My script is something like:
while(currentData < nodeLastDate){
currentData.setDate(currentData.getDate() + 1);
console.log(currentData)
}
But at Sat Mar 30 2019 there is an error and the data change also the time.
if you run this simple script you can see it.
let test = new Date(2019, 2, 30, 2)
console.log(test)
test = test.setDate(test.getDate() + 1)
console.log(new Date(test))
this is the result:
Sat Mar 30 2019 02:00:00 GMT+0100 (Ora standard dell’Europa centrale)
index.js?c69d:385 Sun Mar 31 2019 03:00:00 GMT+0200 (Ora legale dell’Europa central)
Is this normal?
Date.getDate() gets the day of the month, so you lose any other information. If you want to add a day to a date, simply use Date.getTime() and add the number of milliseconds in a day:
let test = new Date(2019, 2, 30, 2)
console.log(test)
test = test.setDate(test.getTime() + (1440 * 60000))
console.log(new Date(test))
Date.getTime returns the number of milliseconds since an arbitrary date known as the epoch date, so adding the number of milliseconds in a day will add exactly one day to your date. (1440 * 60000 is the number of milliseconds in a day because there are 1440 minutes in a day and 60000 milliseconds in a minute)

date manipulation in javascript

I have this string for example "November 8, 2016 - December 7, 2016" which I want to extract the two dates in the this format: YYYY-MM-DD.
Now, I managed to get the dates in the format I want in the following way:
HTML:
<span id="selecDate">November 8, 2016 - December 7, 2016</span>
Javascript:
date = $('#selecDate').text().split('-');
begin = new Date(date[1]);
begin = begin.toISOString().split('T')[0];
The problem is that date = ["November 8, 2016 ", " December 7, 2016"]
and begin = "Wed Dec 07 2016 00:00:00 GMT+0200 (IST)"
when in second line but in the last line the value of begin changes to "2016-12-06", one day earlier. Any idea how can I avoid it?
I'm working from (GMT+02:00) time zone
When you execute toISOString() the date you get back is in UTC time so it takes the time back 2 hours (because of your current timezone). Midnight on Dec 06 in IST is 22:00 in UTC time the day before.
If you wish to keep your timestamps in local time, you can use a .toLocaleDateString(), toLocaleString() or even just .toString() on your date object:
begin = new Date('December 7, 2016').toLocaleDateString();
Note that the date format is slightly different:
a.toLocaleDateString()
"12/7/2016"
a.toLocaleString()
"12/7/2016, 12:00:00 AM"
a.toString()
"Wed Dec 07 2016 00:00:00 GMT+0200 (IST)"
As MDN says in, method toISOString():
The timezone is always zero UTC offset.
And when you create your new Date('December 7, 2016'), what you get is:
Wed Dec 07 2016 00:00:00 GMT+0200
So in UTC, the hours are subtracted by 2, giving you the day before.
Solution:
begin = begin.getFullYear() + '-' + (begin.getMonth() + 1) + '-' + begin.getDate();
will result in: "2016-12-07".
date = ["November 8, 2016 ", " December 7, 2016"];
var b = new Date(date[1]);
//use these get methods to avoid all the confusion
var begin = b.getFullYear()+"-"+(b.getMonth()+1)+"-"+b.getDate();
console.log(_begin);
You should not parse strings with the Date constructor, especially when they are a format other than that specified in ECMA-262 as the behaviour is implementation dependent.
If you need Date objects, you should either use a library (e.g. moment.js, fecha.js) and provide the format to parse or write a simple function to parse the format to a date (see below).
However, if you just want a string in a different format, just reformat the string and avoid Dates altogether:
// Reformat a date string in format MMMM d, yyyy to yyyy-mm-dd
function reformatDate(s) {
var b = s.match(/\w+/g) || [];
var months = {jan:'01',feb:'02',mar:'03',apr:'04',may:'05',jun:'06',
jul:'07',aug:'08',sep:'09',oct:'10',nov:'11',dec:'12'};
return b.length == 3? b[2] + '-' +
months[b[0].toLowerCase().substr(0,3)] + '-' +
('0' + b[1]).slice(-2) : '';
}
console.log(reformatDate('November 8, 2016'))
The following functions parse a date in MMMM d, yyyy format to a Date object, then format it in yyyy-mm-dd format:
// Parse date string in format MMMM d, yyyy e.g. November 8, 2016
function parseDate(s) {
var months = 'jan feb mar apr may jun jul aug sep oct nov dec'.split(' ');
var b = s.match(/\w+/g) || [];
var m = months.indexOf(b[0].toLowerCase().substr(0,3));
var d = new Date(b[2], m, b[1]);
return d && d.getMonth() == m? d : new Date(NaN);
}
function toISODate(d) {
function z(n){return (n<10?'0':'')+n}
return d.getFullYear() + '-' + z(d.getMonth()+1) + '-' + z(d.getDate());
}
console.log(toISODate(parseDate('November 8, 2016')))
Using a library like moment.js you'd do:
'November 8, 2016 - December 7, 2016'.split(' - ').forEach(function(s) {
var d = moment(s,'MMMM D, YYYY').format('YYYY-MM-DD');
console.log(d);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.js"></script>

Javascript date looks wrong when changing the month

My eyes must be deceiving me, but when I call setMonth(1) shouldn't the date of that date object be February? (Today is March 30, 2016)
x = new Date();
y = new Date();
x.setMonth(1);
document.write(x.toString() + '<br>' + y.toString());
Results in:
There is no February 30, 2016
So therefore the date processor correctly changes the date to March 1, 2016

javascript date & time join

I have two date variable separately like following
startDate is a Date instance with the value Tue Jul 17 2012 00:00:00 GMT+0530 (IST)
startTime is a String with the value "11:30 AM"
Now what I need is join of both above date & time, as a Date.
startDateTime = Tue Jul 17 2012 11:30:00 GMT+0530 (IST)
I tried
new Date(startDate + " " + startDate) but outputting invalid date.
Also tried the way shown on this post. But still not working.
You can readily parse startTime if it's in a clearly-defined format, then use setHours and setMinutes: Live example | source
var startDateTime;
var parts = /^(\d+):(\d+) (AM|PM)$/.exec(startTime);
if (parts) {
hours = parseInt(parts[1], 10);
minutes = parseInt(parts[2], 10);
if (parts[3] === "PM" && hours !== 12) {
hours += 12;
}
else if (parts[3] === "AM" && hours === 12) {
hours = 0;
}
if (!isNaN(hours) && !isNaN(minutes)) {
startDateTime = new Date(startDate.getTime());
startDateTime.setHours(hours);
startDateTime.setMinutes(minutes);
}
}
...or something along those lines.
Note that key to this is the fact you've said startDate is a Date instance. The above assumes we're working within the timezone of the JavaScript environment, not across zones. If you were starting with a date string instead, and that string specified a timezone other than the JavaScript environment's timezone, which you were then converting into a Date via new Date("Tues Jul...."), then you'd have to be sure to adjust the resulting Date to use either the local time of the environment, or UTC; if you adjusted it to be UTC, you'd use setUTCHours and setUTCSeconds above instead of setHours and setSeconds. Again, this is only an issue if your starting point is a date string, and that string specifies a timezone different from the timezone in which the code above is running.
You can do This:
var theDate = new Date("Tue Jul 17 2012 00:00:00 GMT+0530 (IST)");
var theTime = "11:30 AM";
var hours = theTime .substr(0,2);
var minutes = theTime .substr(3,2);
var amOrPm = theTime .substr(6,2);
if (hours < 12 && "PM" == amOrPm) {
hours = +hours + 12;
}
theDate.setHours(hours);
theDate.setMinutes(minutes);
Try
new Date(startDate.toDateString() + " " + startTime)
This combines the date string from your Date object with the time string, and should give you a valid date. Note that this ignores the timezone you initially worked with, you might need to add " GMT+0530" again.
However, because your date string is already timezone-biased (Jul 16 2012, 20:30:00 UTC) it might be better to add them together, i.e. like new Date(+startDate + milliseconds):
var startDate = new Date("Tue Jul 17 2012 00:00:00 GMT+0530");
var startTime = "11:30 AM";
return new Date(+startDate + +new Date("1 1 1970 "+startTime))

Categories