I'm in javascript, running this in the console
d = new Date();
d.setMonth(1);
d.setFullYear(2009);
d.setDate(15);
d.toString();
outputs this:
"Sun Mar 15 2009 18:05:46 GMT-0400 (EDT)"
Why would this be happening? It seems like a browser bug.
That's because when you initialize a new Date, it comes with today's date, so today is Oct 30 2008, then you set the month to February, so there is no February 30, so set first the day, then the month, and then the year:
d = new Date();
d.setDate(15);
d.setMonth(1);
d.setFullYear(2009);
But as #Jason W, says it's better to use the Date constructor:
new Date(year, month, date [, hour, minute, second, millisecond ]);
It's probably best to construct a Date object in one step to avoid the Date object being in an ambiguous or invalid state:
d = new Date(2009, 1, 15);
d = new Date();
d.setDate(15);
d.setMonth(1);
d.setFullYear(2009);
d.toString();
This works.
After a bunch of testing in FF3 on XP with Firebug, here are the things I can tell you
Calling Date.setDate() after calling Date.setMonth() will generate this odd behavior.
Date.setMonth() forces the timezone to be CST (or, some non DST-aware zone)
Date.setDate() forces the timezone to be CDT (or, some DST-aware zone)
So, there's definitely something wonky going on with setMonth() and setDate() in respect to the timezone.
The only solution I can offer is this: Set the date before you set the month.
This will work generally to avoid the rollover behavior of the javascript Date API:
d.setDate(1);
d.setFullYear(year);
d.setMonth(month);
d.setDate(day);
Given that year + month + day are in a "valid" combination, e.g. taken from another Date object using getFullYear(), getMonth(), getDate().
The important parts are:
starting with setDate(1) to avoid possible rollover when the current date value is 29, 30 or 31
call setMonth(month) before setDate(day) to avoid the same rollover in case the current month value is "problematic" (because then the initial setDate(1) would be without effect)
Related
I'm trying to construct a Date from a year, month, day tuple, however I'd like to be able to construct "historic" dates as well. For instance Nero's birthday: 15 December 37. The straightforward implementation
function constructDate(year, month, day) {
return new Date(year, month-1, day);
}
however returns Wed Dec 15 1937 00:00:00 GMT+0100 for constructDate(37, 12, 15), because of the constructor's magic to treat years in the range 0-100 as 1900-2000. I'm trying to find a good way to bypass this behavior.
A naive approach would be to construct a temporary date with an arbitrary year, and then call setFullYear() to get the actual target date. However, I have a few concerns:
Trying to do so for Nero's birthday in Firefox, returns Tue Dec 15 0037 00:00:00 GMT+0053. What has happened to the time zone offset there?
I'm not sure if there are subtleties e.g. when constructing February 29. If the target year is a leapyear, but the temporary year isn't, i.e., doesn't have February 29, am I in trouble?
Internally the construction from year/month/day requires the computation of the underlying Unix epoch timestamp. With the setFullYear() work-around there would be two timestamp computations, one of them all in vain.
Is there a way to construct such a Date without a temporary / computing the timestamp twice internally?
Both the above new date formats give different results.
new Date(2015,2,30) : Date 2015-03-29T18:30:00.000Z
new Date('2015-2-30') : Invalid Date
Why is this different ?
EDITS
Points :
1. Month index in the first format start with 0.
2. The first format handles the overflow of the dates and hence is not a prefered way to test for invalid dates.
For eg : new Date(2015, 1, 30) {when the user is looking for 30,Feb,2015} will be converted to 01,March,2015. That is the extra date is carry forwarded to the month. Pretty indecent a convert according to me. However, if you write new Date("2015-2-30"){when the user is looking for 30,Feb,2015} , this will be an invalid date.
You're calling the Date constructor with different types of parameters :
in the first case you're providing integers which it uses to populate its fields (the first three being the year, the month and the day of the month) ; note that the monthes are 0-indexed : a value of 1 in that field will correspond to February rather than January
in the second case you're providing a String which will be parsed as if passed to Date.parse, that is as an ISO 8601 Extended Format date (YYYY‐MM‐DDTHH:mm:ss.sssZ)
Check docs.
It's because by first example you pass arguments
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);
And by second one you pass date string that is not valid. Missing 0 before 2
new Date(dateString);
You can create new Date in these ways:
var d = new Date();
var d = new Date(milliseconds);
var d = new Date(dateString);
var d = new Date(year, month, day, hours, minutes, seconds, milliseconds);
For dateString it must be provided by one of these input:
ISO Date "2015-03-25" (The International Standard)
Short Date "03/25/2015" or "2015/03/25"
Long Date "Mar 25 2015" or "25 Mar 2015"
Full Date "Wednesday March 25 2015"
There is a fundamental rule with ECMAScript dates: never use the Date constructor or Date.parse (they are equivalent for parsing) to parse strings.
Given new Date(2015,2,30) the values are treated as "local" for 30 March 2015, so the UTC time value is adjusted for the timezone offset on the host machine for that date. The outcome will be identical in all implementations consistent with all versions of ECMAScript from ed 3 onward at least.
Given the string new Date('2015-3-30'), the browser parsing algorithm is used. That string it might be treated as:
An invalid date, which is the result in Safari since it's ISO–like but not the correct format and the day is invalid for February
ISO 8601 and hence UTC for 30 March 2015, which is the result in Firefox and is also consistent with the standard since it's not valid ISO 8601 and hence can be parsed any way the implementation wishes to
A local date for 30 March 2015 which is the result in Chrome.
Also, given new Date('2015-2-30'):
Safari and Firefox give an invalid date
Chrome gives 2 March 2015 as a local date (i.e. 30 February rolls over to 2 March, which is the same behaviour as new Date(2015,1,30))
All the above outcomes are consistent with ECMAScript 2016. Hence the opening statement.
Edit
The quickest way to validate the values of a date while parsing is to test the month, in the simplest case of parsing a date in d/m/y format:
function parseDMY(s) {
var b = s.split(/\D/);
var d = new Date(b[2], --b[1], b[0]);
// If month is wrong, return invalid date
return d && d.getMonth() == b[1]? d : new Date(NaN);
}
// Dates in d/m/y format
['23/10/2015','32/10/2015','12/13/2015'].forEach(function(s){
var d = parseDMY(s);
console.log(s + ' : ' + (isNaN(d)? 'Invalid' : 'Valid'));
});
The output will be
console.log(new Date(2015,2,30)); // here 2 represents the march, ,month starts from 0 where 0 represents first month
console.log(new Date('2015-3-30'));
Mon Mar 30 2015 00:00:00 GMT+0530 (India Standard Time)
Mon Mar 30 2015 00:00:00 GMT+0530 (India Standard Time)
new Date('2015-2-30') // it means 30th day in February; which will convert it to second march
I send this date from my controller in java (Spring-MVC) the type in mysql is datetime
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "..") public Date getYy() {
return this.yy;
}
as : [2015-09-30 00:00:00.0]
When i get this dates with ajax as 1443567600000 :
new Date(1443567600000) convert to Tue Sep 29 2015 23:00:00 GMT+0000 (Maroc)
So why i get wrong date off by one hour?
SOLUTION
We resolve it by
d = new Date(value) ;
d.setTime( d.getTime() - new Date().getTimezoneOffset()*60*1000 );
because it was Daylight saving time (DST) or summer time problem. good article
This JS handling of Date is a quite a head-flip.
I'm in the UK... "easy" because we're on GMT (UTC)... except during the summer months, when there's DST (British Summer Time, BST). Clocks go forward in summer and back in winter (stupidly by the way, but that's another issue!) by one hour. One day in March what is 4pm GMT is now called 5pm (BST).
summer month:
If you do new Date( '2017-08-08' ) this will give you (toString) 'Date 2017-08-08T00:00:00.000Z'.
If you do new Date( '2017-08-08 00:00' ), however, this will give you 'Date 2017-08-07T23:00:00.000Z'!
In the second case it appears JS is trying to be "helpful" by assuming that because you stipulated the hour you were specifying BST time. So it adjusts to GMT/UTC. Otherwise it doesn't... though (of course) it still produces a Date object which is specific right down to the milliseconds. Quite a gotcha!
Confirmation: a winter month... when BST is not applied:
new Date( '2018-01-01 00:00' )/ new Date( '2018-01-01' ): both give 'Date 2018-01-01T00:00:00.000Z'
As for adjusting, it appears that to undo the automatic adjustment you just go
jsDate.setTime( jsDate.getTime() + jsDate.getTimezoneOffset() * 60 * 1000 );
... this is a bit like what Youssef has written... except you have to obtain the offset from the specific Date in question... and my experiments seem to prove that you add this, not subtract it.
Really it would be much more helpful if the default string representation of JS Date gave the UTC time followed by the TZ (time zone) offset information, as with some of the ISO date formats: clearly, this information is included.
I think maybe this is a Daylight Saving Time problem. You can check your client's timezone, and your server's timezone. (web server or SQL Server)
We should probably need more data about it, but it could be that nothing is wrong here, it depends how you set and get back your date.
Basically 1443567600000 doesn't contains timezone. It represent Tue Sep 29 2015 23:00:00 from Greenwich. It's a moment in time that, of course, it different from any location that has a different timezone. The same moment, happens at different time (the midnight of GMT+1 is the 11pm of GMT).
You have to store both the time and the timezone in your DB, and possibly send back to JS, otherwise it will be always interpreted differently depends by the local time of the client.
To make an example:
var d = new Date(2015, 8, 30);
console.log(d.toJSON()); // In my case I got "2015-09-29T22:00:00.000Z"
console.log(d.toDateString()); // "Wed Sep 30 2015"
To be more specific
time = new Date("2018-06-01 " + time);
var offset = time.getTimezoneOffset();
offset = Math.abs(offset / 60);
time.setHours(time.getHours() + offset);
in this case time is equal to hours with leading zeros:minutes with leading zeros.
This will add the hours difference to the UTC.
If you pass a string to date it is treated as UTC.
I send this date from my controller in java (Spring-MVC) the type in mysql is datetime
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "..") public Date getYy() {
return this.yy;
}
as : [2015-09-30 00:00:00.0]
When i get this dates with ajax as 1443567600000 :
new Date(1443567600000) convert to Tue Sep 29 2015 23:00:00 GMT+0000 (Maroc)
So why i get wrong date off by one hour?
SOLUTION
We resolve it by
d = new Date(value) ;
d.setTime( d.getTime() - new Date().getTimezoneOffset()*60*1000 );
because it was Daylight saving time (DST) or summer time problem. good article
This JS handling of Date is a quite a head-flip.
I'm in the UK... "easy" because we're on GMT (UTC)... except during the summer months, when there's DST (British Summer Time, BST). Clocks go forward in summer and back in winter (stupidly by the way, but that's another issue!) by one hour. One day in March what is 4pm GMT is now called 5pm (BST).
summer month:
If you do new Date( '2017-08-08' ) this will give you (toString) 'Date 2017-08-08T00:00:00.000Z'.
If you do new Date( '2017-08-08 00:00' ), however, this will give you 'Date 2017-08-07T23:00:00.000Z'!
In the second case it appears JS is trying to be "helpful" by assuming that because you stipulated the hour you were specifying BST time. So it adjusts to GMT/UTC. Otherwise it doesn't... though (of course) it still produces a Date object which is specific right down to the milliseconds. Quite a gotcha!
Confirmation: a winter month... when BST is not applied:
new Date( '2018-01-01 00:00' )/ new Date( '2018-01-01' ): both give 'Date 2018-01-01T00:00:00.000Z'
As for adjusting, it appears that to undo the automatic adjustment you just go
jsDate.setTime( jsDate.getTime() + jsDate.getTimezoneOffset() * 60 * 1000 );
... this is a bit like what Youssef has written... except you have to obtain the offset from the specific Date in question... and my experiments seem to prove that you add this, not subtract it.
Really it would be much more helpful if the default string representation of JS Date gave the UTC time followed by the TZ (time zone) offset information, as with some of the ISO date formats: clearly, this information is included.
I think maybe this is a Daylight Saving Time problem. You can check your client's timezone, and your server's timezone. (web server or SQL Server)
We should probably need more data about it, but it could be that nothing is wrong here, it depends how you set and get back your date.
Basically 1443567600000 doesn't contains timezone. It represent Tue Sep 29 2015 23:00:00 from Greenwich. It's a moment in time that, of course, it different from any location that has a different timezone. The same moment, happens at different time (the midnight of GMT+1 is the 11pm of GMT).
You have to store both the time and the timezone in your DB, and possibly send back to JS, otherwise it will be always interpreted differently depends by the local time of the client.
To make an example:
var d = new Date(2015, 8, 30);
console.log(d.toJSON()); // In my case I got "2015-09-29T22:00:00.000Z"
console.log(d.toDateString()); // "Wed Sep 30 2015"
To be more specific
time = new Date("2018-06-01 " + time);
var offset = time.getTimezoneOffset();
offset = Math.abs(offset / 60);
time.setHours(time.getHours() + offset);
in this case time is equal to hours with leading zeros:minutes with leading zeros.
This will add the hours difference to the UTC.
If you pass a string to date it is treated as UTC.
For the following code:
var d = new Date("2013-07-01");
console.log(d.toDateString());
It outputs Sun Jun 30 2013, which is one day less of what was input. What happened to the object? What date is actually stored?
The date is parsed as UTC date, but the toDateString() outputs what that time is in the local timezone.
Try this instead
var d = new Date(2013, 6, 1); //6 instead of 7 because the mont argument is zero-based
console.log(d.toDateString());
What date is actually stored?
2013-07-01 00:00:00 UTC
Parsing a Date from a string can yield different results depending on what browser/runtime you are using. There is a chart here that lists some of the browsers and what they support.
But in general, a string like "2013-07-01" is treated as if it were at midnight in UTC.
If you wanted to parse it in local time, you could use "2013/07/01". But that's a bit strange. You might be tempted to use "07/01/2013", but that might be interpreted as either Jan 7th or July 1st. Any of these are still implementation specific.
If you want a general purpose, "works everywhere" solution, you have a few different options:
Option 1
Don't parse it. Use the constructor that accept integers.
var d = new Date(2013,6,1); // watch out for the zero-based month
console.log(d.toDateString());
// output: "Mon Jul 01 2013"
With this code, the actual UTC time stored is going to vary depending on your own time zone. But since .toDateString() also takes that time into account, then you should be ok.
Option 2
Parse it as UTC, and display it as UTC/GMT:
var d = new Date("2013-07-01");
console.log(d.toUTCString());
// output: "Mon, 01 Jul 2013 00:00:00 GMT"
But this output probably has more than you were looking for, and it may still look different in various browsers.
Option 3
Use the moment.js library, where you can control the formatting exactly however you desire.
var m = moment("2013-07-01", "YYYY-MM-DD"); // explicit input format
console.log(m.format("ddd MMM DD YYYY")); // explicit output format
// output: "Mon Jul 01 2013"
IMHO, this is the best solution. But it does require you use a library.
Option 4
This approach works pretty good and doesn't require 3rd party libraries.
var parts ='2013-07-01'.split('-');
var mydate = new Date(Date.UTC(parts[0],parts[1]-1,parts[2])); //please put attention to the month
You can simply append your timezone before parsing as well.
var d = new Date("2013-07-01" + "T00:00:00.000-05:00");
console.log(d.toDateString());