I have a string in GMT "2017-01-17 00:00:00.000" and I want to get time and date differently (the string does not have to be in that format).
I tried:
var datetime = new Date(calEvent.start);
var date = datetime.toLocaleDateString(); //output: 1/16/2017
var time = datetime.toLocaleTimeString(); //output: 4:00:00 PM
but the output for date and time I want should be in GMT timezone. Anyone know how to do that without using substring for string?
Thanks
The string "2017-01-17 00:00:00.000" is not consistent with the subset of ISO 8601 specified in ECMA-262 and will not be parsed correctly in some browsers. As it is, it will be treated as local if parsed at all.
You can modify the string by adding a "T" between the date and time and a trailing "Z" for UTC, then let the Date constructor parse it, e.g.
var s = '2017-01-17 00:00:00.000';
var d = new Date(s.replace(' ', 'T') + 'Z');
console.log(d);
Which will work in most modern browsers, however it will fail in older browsers like IE. You can also write a small function to parse the strting as UTC:
function parseAsUTC(s) {
var b = s.split(/\D/);
return new Date(Date.UTC(b[0], b[1] - 1, b[2], b[3], b[4], b[5], b[6]));
}
console.log(parseAsUTC('2017-01-17 00:00:00.000'));
You can also use one of the various libraries available, however if all you need it to parse one particular format, then that's not necessary.
NOTE:
It's generally not recommended to parse strings with the Date constructor (or Date.parse) as they are largely implementation dependent, however the ISO 8601 extended format is now fairly well supported and is specified in ECMA-262.
The time zone must be specified with upper case "Z". Firefox (at least) will generate an invalid date if "z" is used.
Related
I wish to create a new Date in JS, but have it be cast as UTC time. For example, suppose castAsUTC() produces the following desired effect:
var x = new Date('2019-01-01T00:00:00') // In local time (PST)
castAsUTC(x).toISOString(); // => '2019-01-01T00:00:00Z'
// x.toISOString() gives us '2019-01-01T08:00:00Z', which is undesired
Currently, my function looks like this:
function castAsUTC(date) {
return new Date(x.toLocaleString() + '+00:00');
}
Is there a cleaner/nicer way of producing the same effect? Thanks in advance!
EDIT: To be more specific, I'm interested in transforming the date's timezone, without changing its actual value with as little arithmetic as possible. So calling .toISOString() will produce the same date as it is in local time.
I am currently using the moment-timezone library, but I can't seem to get the desired effect using that, either. I would definitely accept an answer that uses Moment.js
You can switch a Moment instance to UTC using the utc function. Then just use format to get whatever the specific output you want from it.
If indeed the string you have is like the one shown, then the easiest thing to do would be to append a Z to indicate UTC.
var input = '2019-01-01T00:00:00';
var date = new Date(input + 'Z');
var output = date.toISOString();
Or, if you would like to use Moment.js, then do this:
var input = '2019-01-01T00:00:00';
var m = moment.utc(input);
var output = m.format();
You do not need moment-timezone for this.
tl;dr;
You formatted the date wrong. Add the letter "Z" to the end of your date string and it will be treated as UTC.
var x = new Date('2019-01-01T00:00:00Z') // Jan 1, 2019 12 AM UTC
These formatting issues are easier to manage with a library like momentjs (utc and format functions) as described in other answers. If you want to use vanilla javascript, you'll need to subtract out the timezone offset before calling toISOString (see warnings in the longer answer below).
Details
Date in javascript deals with timezones in a somewhat counter intuitive way. Internally, the date is stored as the number of milliseconds since the Unix epoch (Jan 1, 1970). That's the number you get when you call getTime() and it's the number that's used for math and comparisons.
However - when you use the standard string formatting functions (toString, toTimeString, toDateString, etc) javascript automatically applies the timezone offset for the local computers timezone before formatting. In a browser, that means it will apply the offset for the end users computer, not the server. The toISOString and toUTCString functions will not apply the offset - they print the actual UTC value stored in the Date. This will probably still look "wrong" to you because it won't match the value you see in the console or when calling toString.
Here's where things really get interesting. You can create Date's in javascript by specifying the number of milliseconds since the Unix epoch using new Date(milliseconds) or by using a parser with either new Date(dateString). With the milliseconds method, there's no timezone to worry about - it's defined as UTC. The question is, with the parse method, how does javascript determine which timezone you intended? Before ES5 (released 2009) the answer was different depending on the browser! Post ES5, the answer depends on how you format the string! If you use a simplified version of ISO 8601 (with only the date, no time), javascript considers the date to be UTC. Otherwise, if you specify the time in ISO 8601 format, or you use a "human readable" format, it considers the date to be local timezone. Check out MDN for more.
Some examples. I've indicated for each if javascript treats it as a UTC or a local date. In UTC, the value would be Jan 1, 1970 at midnight. In local it depends on the timezone. For OP in pacfic time (UTC-8), the UTC value would be Jan 1, 1970 at 8 AM.
new Date(0) // UTC (milliseconds is always UTC)
new Date("1/1/1970"); // Local - (human readable string)
new Date("1970-1-1"); // Local (invalid ISO 8601 - missing leading zeros on the month and day)
new Date("1970-01-01"); // UTC (valid simplified ISO 8601)
new Date("1970-01-01T00:00"); // Local (valid ISO 8601 with time and no timezone)
new Date("1970-01-01T00:00Z"); // UTC (valid ISO 8601 with UTC specified)
You cannot change this behavior - but you can be pedantic about the formats you use to parse dates. In your case, the problem was you provided an ISO-8601 string with the time component but no timezone. Adding the letter "Z" to the end of your string, or removing the time would both work for you.
Or, always use a library like momentjs to avoid these complexities.
Vanilla JS Workaround
As discussed, the real issue here is knowing whether a date will be treated as local or UTC. You can't "cast" from local to UTC because all Date's are UTC already - it's just formatting. However, if you're sure a date was parsed as local and it should really be UTC, you can work around it by manually adjusting the timezone offset. This is referred to as "epoch shifting" (thanks #MattJohnson for the term!) and it's dangerous. You actually create a brand new Date that refers to a different point in time! If you use it in other parts of your code, you can end up with incorrect values!
Here's a sample epoch shift method (renamed from castAsUtc for clarity). First get the timezone offset from the object, then subtract it and create a new date with the new value. If you combine this with toISOString you'll get a date formatted as you wanted.
function epochShiftToUtc(date) {
var timezoneOffsetMinutes = date.getTimezoneOffset();
var timezoneOffsetMill = timezoneOffsetMinutes * 1000 * 60;
var buffer = new Date(date.getTime() - timezoneOffsetMill);
return buffer;
}
epochShiftToUtc(date).toUTCString();
I am trying to extract the date from the following object (that has been stringified.)
I am new to regular expressions, and not sure how to go about it.
I tried /^(\d{4})\-(\d{1,2})\-(\d{1,2})$/gmi -> but it didnot work.
{"Date":"2016-05-16","Package Name":"com.myapp.mobile","Current Device Installs":"15912","Daily Device Installs":"41","Daily Device Uninstalls":"9","Daily Device Upgrades":"3","Current User Installs":"12406","Total User Installs":"23617","Daily User Installs":"27","Daily User Uninstalls":"8"}
Don't use a Regex here.
Do JSON.parse(str).Date, unless there is a really good reason not to (you haven't stated one in your question)
If you want to turn the string "2016-05-16" into 3 variables for Year, Month and day (without using a date library), I'd just use .split():
dateArray = "2016-05-16".split("-")
var year = dateArray[0], month = dateArray[1], day = dateArray[2];
Your regex matches fine, just don't use the /gmi flags
"2016-05-16".match(/^(\d{4})\-(\d{1,2})\-(\d{1,2})$/)
You can make it a bit simpler yet..
"2016-05-16".match(/(\d{4})-(\d{2})-(\d{2})/)
But, you really should be using a library for this, like moment.js, or at least Date which will work fine because this ISO-8601.
const date = new Date("2016-05-16");
date.getYear();
As suggested in comments, you can get the date by parsing the JSON (trimmed in the following for convenience):
var s = '{"Date":"2016-05-16","Package Name":"com.myapp.mobile"}';
var dateString = JSON.parse(s).Date;
document.write(dateString);
If you want a Date object, you can then parse the string. Note that using either the Date constructor or Date.parse for parsing strings is not recommended due to browser inconsistencies. Manually parsing an ISO date is fairly simple, you just need to decide whether to parse it as local or UTC.
Since ECMA-262 requires the date–only ISO format to be parsed as UTC, the following function will do that reliably (and return an invalid date for out of range values):
/* Parse an ISO 8601 format date string YYYY-MM-DD as UTC
** Note that where the host system has a negative time zone
** offset the local date will be one day earlier.
**
** #param {String} s - string to parse
** #returs {Date} date for parsed string. Returns an invalid
** Date if any value is out of range
*/
function parseISODate(s) {
var b = s.split(/\D/);
var d = new Date(Date.UTC(b[0], b[1]-1, b[2]));
return d && d.getMonth() == b[1]-1? d : new Date(NaN);
}
var d = parseISODate('2016-05-16');
document.write('UTC date: ' + d.toISOString() + '<br>' +
'Local date: ' + d.toString());
If you take the following:
var s = "2/8888/2016";
var d = new Date(s);
alert(d);
In Chrome, you'll get:
Invalid Date
But in IE and Firefox, you'll get:
Fri Jun 01 2040 00:00:00 GMT-0500 (Central Daylight Time)
It appears to be just adding 8888 days to Feb 01. Instead, I would expect the date to be considered invalid. Is there a way I can make FireFox and IE think this date string is invalid?
Short answer:
It's a misbehaviuor of the browsers you're mentioning.
You have to check date is in correct format on your own. But it's quite trivial, I suggest this approach:
Split the date in year y, month m, day d and create the Date object:
var date = new Date( y, m - 1, d ); // note that month is 0 based
Then compare the original values with the logical values obtained using the Date methods:
var isValid = date.getDate() == d &&
date.getMonth() == m-1 &&
date.getFullYear() == y;
Before doing all of this you may want to check if the date string is valid for any browser:
Detecting an "invalid date" Date instance in JavaScript
Long answer:
Firefox (and IE) accepting "2/8888/2016" as a correct string sate format seem to be a bug / misbehaviour.
In fact according to ECMAScript 2015 Language Specification when Date() is invoked with a single string argument should behave just as Date.parse()
http://www.ecma-international.org/ecma-262/6.0/#sec-date-value
The latter
attempts to parse the format of the String according to the rules (including extended years) called out in Date Time String Format (20.3.1.16)
..that is specified here
http://www.ecma-international.org/ecma-262/6.0/#sec-date-time-string-format
where you can read
The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ
[...]
MM is the month of the year from 01 (January) to 12 (December).
DD is the day of the month from 01 to 31.
It seems that Firefox is interpreting the string value as when Date() is invoked with multiple arguments.
From
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
Note: Where Date is called as a constructor with more than one argument, if values are greater than their logical range (e.g. 13 is provided as the month value or 70 for the minute value), the adjacent value will be adjusted. E.g. new Date(2013, 13, 1) is equivalent to new Date(2014, 1, 1), both create a date for 2014-02-01 (note that the month is 0-based). Similarly for other values: new Date(2013, 2, 1, 0, 70) is equivalent to new Date(2013, 2, 1, 1, 10) which both create a date for 2013-03-01T01:10:00.
This may explain how "2/8888/2016" turns into 2040-05-31T22:00:00.000Z
There's no way to make IE and FF think it's invalid, except:
you could change their javascript implementations
you use a library instead to deal with that.
We can also expect that Javascript, as a language, evolves and we can cross our fingers that browsers decide to follow a more strict specification. The problem of course is that every "fix" must be also backward compatible with previous versions of the language (does not happen always, Perl for example).
So the best thing by now is to use some library just like momentjs as suggested by Derek in the post comments.
You have stumbled across yet another reason why you should manually parse date strings.
When Date is provided a single string argument, it is treated as a date string and parsed according to the rules in Date.parse. The rules there first attempt to parse it as an ISO 8601 format string. If that doesn't work, it may fall back to any parsing algorithm it wants.
In the case of "2/8888/2016", browsers will first attempt to parse it as an ISO format and fail. It seems from experimentation that IE and Firefox determine that the string is in month/day/year format and effectively call the Date constructor with:
new Date(2016,1,8888);
However, other browsers may attempt to validate the values and decide that 8888 is not a valid date or month, so return an invalid date. Both responses are compliant with ECMA-262.
The best advice is to always manually parse date strings (a library can help, but generally isn't necessary as a bespoke parse function with validation is 3 lines of code) then you can be certain of consistent results in any browser or host environment.
I've this function to add minutes to a date in javascript
function addMinutes(date, minutes) {
var DateObject = new Date(date);
var modifiedDate = DateObject.getTime() + minutes * 60000;
return date = modifiedDate;
}
The script works perfectly on most of my pages but on the current page i'm working on I've this date: 2014-06-07 01:00:00
This works only in google chrome.. I've that browsers like IE/Safari are not able to work with YYYY/MM/DD format.
I've tried to parse it with Date, but this is kinda new for me and I'm not sure what i'm doing wrong.
here's a fixed copy of the orig with suggestions implemented:
function addMinutes(date, minutes) {
var DateObject = new Date(String(date).replace(/\ /g,"T")+"Z"),
modifiedDate = DateObject.getTime() +
(minutes* 60000) +
(new Date(DateObject).getTimezoneOffset()*60*1000) ;
return date = modifiedDate;
}
new Date(addMinutes("2014-06-07 01:00:00", 15)).toLocaleString();
// shows: "6/7/2014 1:15:00 AM"
There are two different problems here that you should mentally separate:
Parsing a string in the particular format of YYYY-MM-DD hh:mm:ss to a Date object.
Adding minutes to a Date object.
Parsing the String
You should be aware that the parsing behavior when you pass a string to the Date constructor is implementation specific, and the implementations vary between browser vendors.
In general, when dashes (-) are present, the values are treated as UTC, and when slashes (-) are present, the values are treated as local to the time zone where the code is running.
However, this only applies when either a time is not present, or when the date and time components are separated with a T instead of with a space. (YYYY-MM-DDThh:mm:ss)
When a space is used to separate date and time components, some browsers (like Chrome) will treat it as local time, but other browsers (like IE and Firefox) will consider it an invalid date.
Replacing the space with a T will allow the date to be parsed, but if that's all you do, then Chrome will treat it as UTC, while IE and Firefox will treat it as local time.
If you also add the trailing Z, (YYYY-MM-DDThh:mm:ssZ) then all browsers will parse it as UTC.
If you want a format that all browsers will recognize as local time, there is only one, and it's not ISO standard: YYYY/MM/DD hh:mm:ss. Thus, you might consider:
var s = "2014-06-07 01:00:00";
var dt = new Date(s.replace(/-/g,'/'));
Adding Minutes
This is much more straightforward:
dt.setMinutes(dt.getMinutes() + 15);
That will simply mutate the Date value to add 15 minutes. Don't worry about overflow - if getMinutes returns 55, setting 70 minutes will properly add 1 hour and 10 minutes.
A Better Solution
Moment.js removes all of the guesswork about parsing variations, and gives you a much cleaner API. Consider:
// parse a string using a specific format
var m = moment("2014-06-07 01:00:00","YYYY-MM-DD HH:mm:ss");
// adding time
m.add(15, 'minutes');
// format the output as desired, with lots of options
var s = m.format("L h:mm:ss A");
I've read this question:
How do you convert a JavaScript date to UTC?
and based on this I implemented this conversion in a dateTools module as follows:
[Update]
var dt, utcTime;
dt = new Date();
utcTime = new Date(Date.UTC(dt.getFullYear(),
dt.getMonth(),
dt.getDate(),
dt.getHours(),
dt.getMinutes(),
dt.getSeconds(),
dt.getMilliseconds()));
Now I'd like to write unit tests. My idea was to check whether the result is actually in UTC, but I don't know how.
All the toString, toUTCString and similar methods seem to be identical for the input (non UTC) and output (UTC) date.
Only the result of the getTime method differs.
Is there a way to check wheter a date is UTC in javascript? If not, is there a better idea to unit test this?
To give more context:
Only converting the it to a UTC string is not that helpful, because in the next step the date is sent to an Asp.net service and therefore converted to a string like:
'/Date([time])/'
with this code
var aspDate = '/Date(' + date.getTime() + ')/';
var aspDate = '/Date(' + date.getTime() + ')/';
This outputs the internal UNIX epoch value (UTC), which represents a timestamp. You can use the various toString methods to get a more verbose string representation of that timestamp:
.toString() uses the users timezone, result is something like "Fri Jan 25 2013 15:20:14 GMT+0100" (for me, at least, you might live in a different timezone)
.toUTCString() uses UTC, and the result will look like "Fri, 25 Jan 2013 14:20:15 GMT"
.toISOString() uses UTC, and formats the datestring according to ISO: "2013-01-25T14:20:20.061Z"
So how do we construct the time value that we want?
new Date() or Date.now() result in the current datetime. No matter what the user's timezone is, the timestamp is just the current moment.
new Date(year, month, …) uses the users timezone for constructing a timestamp from the single values. If you expect this to be the same across your user community, you are screwed. Even when not using time values but only dates it can lead to odd off-by-one errors.
You can use the setYear, setMonth, … and getYear, getMonth … methods to set/get single values on existing dates according to the users timezone. This is appropriate for input from/output to the user.
getTimezoneOffset() allows you to query the timezone that will be used for all these
new Date(timestring) and Date.parse cannot be trusted. If you feed them a string without explicit timezone denotation, the UA can act random. And if you want to feed a string with a proper format, you will be able to find a browser that does not accept it (old IEs, especially).
Date.UTC(year, month, …) allows you to construct a timestamp from values in the UTC timezone. This comes in handy for input/output of UTC strings.
Every get/set method has a UTC equivalent which you can also use for these things.
You can see now that your approach to get the user-timezone values and use them as if they were in UTC must be flawed. It means either dt or utcTime has the wrong value, although using the wrong output method may let it appear correct.
getTimezoneOffset
Syntax: object.getTimezoneOffset( ) This method
returns the difference in minutes between local time and Greenwich
Mean Time. This value is not a constant, as you might think, because
of the practice of using Daylight Saving Time.
i.e.
var myDate = new Date;
var myUTCDate = new Date(myDate - myDate.getTimezoneOffset() * 60000);
alert(myUTCDate);
note: 60000 is the number of milliseconds in a minute;