ISO 8601 not retaining timezone? - javascript

i am using the timeago Jquery plugin to output strings like "5 mins ago" etc.
The plugin uses ISO 8601 date format.
I have a Date object which looks like this in console:
Sat Jun 15 2013 07:16:23 GMT+0530 (India Standard Time)
To convert it into ISO 8601 I do this:
date = date.toISOString();
Now it looks like this in the console:
2013-06-15T01:46:23Z
Apparently, the time zone has been set to UTC ( Z stand for UTC ).
This messes up with timeago plugin as everything is now in UTC, not in the local timezone.
I tried this:
Date.prototype.toISOStringfix = function() {
function pad(n) { return n < 10 ? '0' + n : n }
return this.getUTCFullYear() + '-'
+ pad(this.getUTCMonth() + 1) + '-'
+ pad(this.getUTCDate()) + 'T'
+ pad(this.getUTCHours()) + ':'
+ pad(this.getUTCMinutes()) + ':'
+ pad(this.getUTCSeconds()) + 'Z';
};
However, I get the same result.
Why does this happen? How can I fix it?

Related

Why the toISOString() applied on a Date object generate a wrong time section converting a date into a string?

I am going crazy with this Date to String conversion into a TypeScript project:
I have this function performing my conversion:
fromDateToString(date: Date) : String {
let dateAsString = date.toISOString().substr(0, 19)
console.log(dateAsString);
return dateAsString;
}
As you can see it take a Date object containing the following value:
Wed Feb 08 2017 07:00:00 GMT-0800 (Ora standard del Pacifico USA)
and, in theory it have to convert this date in the following string format:
2017-02-08T07:00:00"
But I am finding a strange behavior on the time !!! The generated string is not the previous one but it is:
2017-02-08T15:00:00"
The generated time section is not 07:00:00 but it is 15:00:00
Why? What is wrong? What am I missing? How can I fix this issue?
The value isn't wrong; you're disregarding the timezone. toISOString returns a string written in UTC with Z at the end saying it's in UTC. You're stripping off the part of the string saying what timezone it's in.
If you like, you can get that string in local time instead by subtracting the timezone offset:
function fromDateToString(date/*: Date*/)/* : String*/ {
date = new Date(+date);
date.setTime(date.getTime() - (date.getTimezoneOffset() * 60000));
let dateAsString = date.toISOString().substr(0, 19);
return dateAsString;
}
console.log(fromDateToString(new Date()));
Just beware that your code using that string handles it as a local time, not UTC.
2017-02-08T15:00:00 -> this is UTC date (ISO) [UTC has 0:00 offset]
2017-02-08T07:00:00 -> this is GMT-0800
so if we convert 2017-02-08T07:00:00 to UTC we need to add 8:00 hours so 7 + 8 = 15 so its giving 2017-02-08T15:00:00
You directly cannot ignore timezone offset without adding/subtracting.
That happens because the EcmaScript algorithm for generating an ISO string always generates it with the UTC+0 timezone. You don't need (and shouldn't) modify the Date object. Here's an algorithm to get a ISO string with the local timezone:
let toLocalISOString;
{
const pad = function pad(str) {
return String(str).padStart(2, "0");
}
toLocalISOString = function toLocalISOString(date) {
const tzOffset = date.getTimezoneOffset(),
tzPlus = tzOffset >= 0;
return date.getFullYear() +
'-' + pad(date.getMonth() + 1) +
'-' + pad(date.getDate()) +
'T' + pad(date.getHours()) +
':' + pad(date.getMinutes()) +
':' + pad(date.getSeconds()) +
'.' + (date.getMilliseconds() / 1000).toFixed(3).slice(2, 5) +
(tzPlus ? "+" + pad(tzOffset / 60) : "-" + pad((tzOffset / 60).toString().slice(1))) +
":" + pad((tzOffset % 60).toFixed(0));
};
}

Date.toISOString() but local time instead of UTC

Let's say we have this datetime:
var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
Exporting it as a string (console.log(d)) gives inconsistent results among browsers:
Sat Jul 21 2018 14:00:00 GMT+0200 (Paris, Madrid (heure d’été)) with Chrome
Sat Jul 21 14:00:00 UTC+0200 2018 with Internet Explorer, etc.
so we can't send datetime to a server with an unconsistent format.
The natural idea then would be to ask for an ISO8601 datetime, and use d.toISOString(); but it gives the UTC datetime: 2018-07-21T12:00:00.000Z whereas I would like the local-timezone time instead:
2018-07-21T14:00:00+0200
or
2018-07-21T14:00:00
How to get this (without relying on a third party dependency like momentjs)?
I tried this, which seems to work, but isn't there a more natural way to do it?
var pad = function(i) { return (i < 10) ? '0' + i : i; };
var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
Y = d.getFullYear();
m = d.getMonth() + 1;
D = d.getDate();
H = d.getHours();
M = d.getMinutes();
S = d.getSeconds();
s = Y + '-' + pad(m) + '-' + pad(D) + 'T' + pad(H) + ':' + pad(M) + ':' + pad(S);
console.log(s);
There is limited built-in support for formatting date strings with timezones in ECMA-262, there is either implementation dependent toString and toLocaleString methods or toISOString, which is always UTC. It would be good if toISOString allowed a parameter to specify UTC or local offset (where the default is UTC).
Writing your own function to generate an ISO 8601 compliant timestamp with local offset isn't difficult:
function toISOLocal(d) {
var z = n => ('0' + n).slice(-2);
var zz = n => ('00' + n).slice(-3);
var off = d.getTimezoneOffset();
var sign = off > 0? '-' : '+';
off = Math.abs(off);
return d.getFullYear() + '-'
+ z(d.getMonth()+1) + '-' +
z(d.getDate()) + 'T' +
z(d.getHours()) + ':' +
z(d.getMinutes()) + ':' +
z(d.getSeconds()) + '.' +
zz(d.getMilliseconds()) +
sign + z(off/60|0) + ':' + z(off%60);
}
console.log(toISOLocal(new Date()));
The trick is to adjust the time by the timezone, and then use toISOString(). You can do this by creating a new date with the original time and subtracting by the timezone offssetfrom the original time:
var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
var newd = new Date(d.getTime() - d.getTimezoneOffset()*60000);
console.log(newd.toISOString()); // 2018-07-21T22:00:00.000Z
Alternatively, you can simply adjust the original date variable:
var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
d = new Date(d.getTime() - d.getTimezoneOffset()*60000);
console.log(d.toISOString()); // 2018-07-21T22:00:00.000Z
For your convenience, the result from .getTime() is the number of milliseconds since 1 January 1970. However, getTimezoneOffset() gives a time zone difference from UTC in minutes; that’s why you need to multiply by 60000 to get this in milliseconds.
Of course, the new time is still relative to UTC, so you’ll have to ignore the Z at the end:
d = d.slice(0,-1); // 2018-07-21T22:00:00.000
My version:
// https://stackoverflow.com/questions/10830357/javascript-toisostring-ignores-timezone-offset/37661393#37661393
// https://stackoverflow.com/questions/49330139/date-toisostring-but-local-time-instead-of-utc/49332027#49332027
function toISOLocal(d) {
const z = n => ('0' + n).slice(-2);
let off = d.getTimezoneOffset();
const sign = off < 0 ? '+' : '-';
off = Math.abs(off);
return new Date(d.getTime() - (d.getTimezoneOffset() * 60000)).toISOString().slice(0, -1) + sign + z(off / 60 | 0) + ':' + z(off % 60);
}
console.log(toISOLocal(new Date()));
i have found a solution which has worked for me.
see this post: Modifying an ISO Date in Javascript
for myself i tested this with slight modification to remove the "T", and it is working. here is the code i am using:
// Create date at UMT-0
var date = new Date();
// Modify the UMT + 2 hours
date.setHours(date.getHours() + 2);
// Reformat the timestamp without the "T", as YYYY-MM-DD hh:mm:ss
var timestamp = date.toISOString().replace("T", " ").split(".")[0];
and an alternative method is stipulate the format you need, like this:
// Create the timestamp format
var timeStamp = Utilities.formatDate(new Date(), "GMT+2", "yyyy-MM-dd' 'HH:mm:ss");
note: these are suitable in locations that do not have daylight saving changes to the time during the year
it has been pointed out that the above formulas are for a specific timezone.
in order to have the local time in ISO format, first specify suitable Locale ("sv-SE" is the closest and easiest to modify), then make modification (change the space to a T) to be same as ISO format. like this:
var date = new Date(); // Create date
var timestamp = date.toLocaleString("sv-SE").replace(" ", "T").split(".")[0]; // Reformat the Locale timestamp ISO YYYY-MM-DDThh:mm:ss
References:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString)
https://www.w3schools.com/Jsref/jsref_tolocalestring.asp
https://www.w3schools.com/Jsref/tryit.asp?filename=tryjsref_tolocalestring_date_all

How do I convert local time to UTC?

I want to convert my local time (shown below) to UTC. Local time can belong to any timezone which a user belongs to. I just want to convert it to UTC format.
Local Time : '2015-12-04 19:05:48'
How do I achieve this requirement in JavaScript or AngularJS?
Update
My Original date/time string as like this "Mon March 28 2016 23:59:59 GMT -0600 (CENTRAL DAYLIGHT TIME)" and I have converted them to "2016-03-28 23:59:59 CDT" as requested date format. But again, I need to convert this interim output in to UTC in the same interim output format "YYYY-MM-DD HH:MM:SS". How do I achieve this
You can try this.
fiddle: https://jsfiddle.net/shuNaka/v891skuh/
const datetime = "2020-02-12 10:30:00";
const timezone = "Asia/Tokyo";
document.getElementById("localtime").innerHTML = datetime;
document.getElementById("timezone").innerHTML = timezone;
document.getElementById("utctime").innerHTML = getUtcFromLocaltime(datetime, timezone);
function getUtcFromLocaltime (localtimeString, timezone) {
// Change it to moment object
const localTime = moment(localtimeString, 'YYYY-MM-DD HH:mm:ss', true);
// Get the timezone
const timeDiff = moment().tz(timezone).format('ZZ')
// Get the time difference
let numTimeDiff = Number(timeDiff.substr(1)) / 100;
numTimeDiff = hourAdjust(numTimeDiff);
// Get UTC from the timezone(+0900)
if (timeDiff.charAt(0) === '+') {
return localTime.subtract({hours: numTimeDiff}).format('YYYY-MM-DD HH:mm:ss');
} else if (timeDiff.charAt(0) === '-') {
return localTime.add({hours: numTimeDiff}).format('YYYY-MM-DD HH:mm:ss');
}
}
function hourAdjust (hour) {
let mhour = Math.floor(hour);
let min = (Math.floor(hour * 100) % 100) / 6;
return Number(mhour+"."+min);
}
You can create a new local date based on the UTC values of the original (local) date.
var localDate = parseDate('2015-12-04 19:05:48 CDT'); // With or without CDT (still works)
document.body.innerHTML = [
'- Local Time: ' + formatDate(localDate),
'- UTC Time: ' + formatDate(localDateToUTC(localDate))
].join('\n');
function parseDate(dateString) {
var tokens = dateString.split(/[-: ]/g).slice(0, 6).map(function(token) { return parseInt(token, 10); });
tokens[1] -= 1; // Subtract 1 from month.
return newInstance(Date, tokens);
}
function formatDate(date) {
function pad(val, pattern) { return (pattern + val).substr(-pattern.length); }
return date.getFullYear() + '-' + pad(date.getMonth() + 1, '00') + '-' + pad(date.getDate(), '00') + ' ' +
pad(date.getHours(), '00') + ':' + pad(date.getMinutes(), '00') + ':' + pad(date.getSeconds(), '00');
}
function localDateToUTC(localDate) {
return new Date(localDate.getUTCFullYear(), localDate.getUTCMonth(), localDate.getUTCDate(),
localDate.getUTCHours(), localDate.getUTCMinutes(), localDate.getUTCSeconds());
}
function newInstance(clazz, arguments, scope) {
return new (Function.prototype.bind.apply(clazz, [scope].concat(arguments)));
}
body {
font-family: monospace;
white-space: pre;
}
The first step is to parse your original string. Do not remove the timezone offset since timezone names are not standardised, nor are their abbreviations. To parse "Mon March 28 2016 23:59:59 GMT -0600 (CENTRAL DAYLIGHT TIME)" you can use a function like the following.
You can then format a string based on the date, but you must keep the timezone as a string like "2016-03-29 05:59:59" will be treated as local by any implementation that is vaguely consistent with ISO 8601.
In most cases, "2016-03-29 05:59:59Z" will be treated as UTC as the joining "T" can be omitted by agreement (per ISO 8601).
// Parse date string in format:
// "Mon March 28 2016 23:59:59 GMT -0600 (CENTRAL DAYLIGHT TIME)"
function parseDate(s) {
var months = {jan:0,feb:1,mar:2,apr:3,may:4,jun:5,
jul:6,aug:7,sep:8,oct:9,nov:10,dec:11};
var b = s.split(/[\s:]/);
// Calc offset in minutes
// JS offsets are opposite sense to everyone else
var offset = (b[8] < 0? 1 : -1) * (b[8].slice(1,3)*60 + +b[8].slice(-2));
// Create UTC date
return new Date(Date.UTC(b[3], months[b[1].toLowerCase().substr(0,3)],
b[2], b[4], +b[5] + offset, b[6]));
}
// Format date as yyyy-mm-dd hh:mm:ssZ
// The timezone really should be included as this string will
// be treated as local if ISO 8601 rules are used (and the commonly are)
function formatDate(d) {
function z(n){return (n<10?'0':'')+n}
return d.getUTCFullYear() + '-' +
z(d.getUTCMonth() + 1) + '-' +
z(d.getUTCDate()) + ' ' +
z(d.getUTCHours()) + ':' +
z(d.getUTCMinutes()) + ':' +
z(d.getUTCSeconds()) + 'Z';
}
var s = 'Mon March 28 2016 23:59:59 GMT -0600 (CENTRAL DAYLIGHT TIME)';
var d = parseDate(s);
document.write('<br>Original: ' + s + '<br>' +
'Local : ' + d + '<br>' +
'UTC : ' + formatDate(d) + '<br>' +
'ISO 8601: ' + d.toISOString());
body {
font-family: courier, monospace;
font-size: 75%;
white-space: pre;
}

How to convert a string to date format in JavaScript [duplicate]

This question already has answers here:
JavaScript datetime parsing [duplicate]
(2 answers)
Closed 8 years ago.
How do I convert a string to date using JavaScript?
Example:
Wed May 21 2014 02:40:00 GMT+0600 (Central Asia Standard Time)
I want to convert it to:
YYYY-mm-dd H:i:s
The Date constructor accepts strings representing dates, example :
var date = new Date("Wed May 21 2014 02:40:00");
alert(date.getDate()); // 21
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
You must parse and format the string with the Date Class with the methods:
Date.parse()
Date.toISOString()
For example:
var text = "Wed May 21 2014 02:40:00 GMT+0600 (Central Asia Standard Time)";
var date = new Date(Date.parse(text));
var formatedText = date.toISOString();
console.log(formatedText );
If you need a custom format, try to use a custom formater, for example:
function pad(number) {
if ( number < 10 ) {
return '0' + number;
}
return number;
}
function toCustom(date) {
return date.getUTCFullYear() +
'-' + pad( date.getUTCMonth() + 1 ) +
'-' + pad( date.getUTCDate() ) +
' ' + pad( date.getUTCHours() ) +
':' + pad( date.getUTCMinutes() ) +
':' + pad( date.getUTCSeconds() );
};
These may be very useful:
Date.parse()
Date.prototype.toISOString()
You can even translate it with my plugin. It's as simple as:
$(selector).setDate({format: "+Y-+m-+d +H:+ii:+ss"; date: 'Wed May 21 2014 02:40:00'});
https://github.com/Masquerade-Circus/setDate.js
You can pass a date object too, no need to get the string of the date.

Date() converts certain date strings to local time

I'm trying to compare two date strings for equality by wrapping them with the Date() object. I live in Seattle and for some reason, the second date string is converted to PST and then rendered in GMT, resulting in the below:
new Date("January 1, 2012")
>>> Sun Jan 01 2012 00:00:00 GMT-0800 (PST)
new Date("2012-01-01")
>>> Sat Dec 31 2011 16:00:00 GMT-0800 (PST)
Try the above in the chrome console and you should get the same results.
How do I get Date to evaluate the second statement as GMT instead of PST?
Do not use the Date object to parse date strings, it is specified as implementation dependent in ECMAScript ed 3 and doesn't work consistently across browsers. One format of ISO8601 date string is specified in ES5, but that doesn't work consistently either. Manually parse the string.
A couple of functions to convert to and from UTC ISO8601 strings:
if (!Date.prototype.toUTCISOString) {
Date.prototype.toUTCISOString = function() {
function addZ(n) {
return (n<10? '0' : '') + n;
}
function addZ2(n) {
return (n<10? '00' : n<100? '0' : '') + n;
}
return this.getUTCFullYear() + '-' +
addZ(this.getUTCMonth() + 1) + '-' +
addZ(this.getUTCDate()) + 'T' +
addZ(this.getUTCHours()) + ':' +
addZ(this.getUTCMinutes()) + ':' +
addZ(this.getUTCSeconds()) + '.' +
addZ2(this.getUTCMilliseconds()) + 'Z';
}
}
if (!Date.parseUTCISOString) {
Date.parseUTCISOString = function fromUTCISOString(s) {
var b = s.split(/[-T:\.Z]/i);
var n= new Date(Date.UTC(b[0],b[1]-1,b[2],b[3],b[4],b[5]));
return n;
}
}
var s = '2012-05-21T14:32:12Z'
var d = Date.parseUTCISOString(s);
alert('Original string: ' + s +
'\nEquivalent local time: ' + d +
'\nBack to UTC string: ' + d.toUTCISOString());
Taking robg's advice you might look at DateJS or moment.js
That's because of your time zone, you are 8:00 hours late to the "2012-01-01", so its showing like that, for me i got this
new Date("January 1, 2012")
Sun Jan 01 2012 00:00:00 GMT+0530 (IST)
new Date("2012-01-01")
Sun Jan 01 2012 05:30:00 GMT+0530 (IST)

Categories