I wrote a basic function which was intended to grab a timestamp, for logging purposes, for when each visitor hits the page. I actually want to record the time in two ways; the local time (so I can see the distribution of visits based on the users' times) as well as the UTC time (so I can see the distribution of visits from a global, normalised perspective)
var currentTime = new Date();
var timestamp = splitTimestamp(currentTime);
function splitTimestamp(timestamp) {
var splitTimestamp = {};
splitTimestamp.local = new Date(timestamp.getFullYear(), timestamp.getMonth(), timestamp.getDate(), timestamp.getHours(), timestamp.getMinutes(), timestamp.getSeconds()).toISOString();
splitTimestamp.UTC = new Date(timestamp.getUTCFullYear(), timestamp.getUTCMonth(), timestamp.getUTCDate(), timestamp.getUTCHours(), timestamp.getUTCMinutes(), timestamp.getUTCSeconds()).toISOString();
return splitTimestamp;
}
The idea being that I can just refer to each via timestamp.local
or timestamp.UTC
as necessary.
I had assumed that the local time derived via new Date()
would just be the local time as per the browser, i.e. as per the user's local system / OS. However, I have seen a number of records which seem to contradict that. For example, I found a record from a user in New York (UTC 4) where the time had not occurred yet (i.e. it was 11am EST / 3pm UTC when I saw the record but it was showing as 2pm EST / 6pm UTC in the log)
Now - initially, I accounted for this by saying the user may simply be using system settings that don't necessarily align with their physical location (i.e. they were in New York but, for reasons known only to themselves, they keep their system / OS on UTC time) And there's nothing I can do about that, I am comfortable with that.
But... If that were the case, then the local time and the UTC time would be the same? And... they're not. So... that can't be what's happening?
Is there something obvious I'm overlooking here?
CodePudding user response:
If you just want a time string that is in that format, you can duplicate the date object using new Date(date)
(no reason to put the year, month, date, etc), and then subtract the offset and get the ISO formatted string. You should then remove the 'z' at the end of the string to indicate that it's the local timezone rather than a UTC timezone.
var date = new Date();
var utc_time = date.toISOString();
var offset_date = new Date(date);
offset_date.setMinutes(date.getMinutes()-date.getTimezoneOffset());
var local_time = offset_date.toISOString().slice(0, -1);
console.log(utc_time);
console.log(local_time);
You may also be interested in looking at my ProtoDate library. I built it to handle stuff like this.
CodePudding user response:
The timestamp produced by Date.prototype.toString has all the information you need. It has the local* date and time plus the UTC offset, the format is parsable by the built–in parser and it can be converted to an ISO 8601 compliant format (also parsable by the built–in parser) with some reformatting, e.g.
// Return UTC timestamp in ISO 8601 format
// @param {string} timestamp - in format per ECMA-262 Date.prototype.toString
// @returns {string} equivalent timestamp in ISO 8601 format UTC
// e.g. Fri Sep 10 2021 08:08:32 GMT 1000 (ChST) -> 2021-09-09T22:41:11.000Z
function toUTC(timestamp){
return new Date(timestamp).toISOString();
}
// Return offset from ECMA-262 timestamp from Date.prototype.toString
// @param {string} timestamp - in format per ECMA-262 Date.prototype.toString
// @return {string} offset in same format as toUTC
// e.g. Fri Sep 10 2021 08:08:32 GMT 1000 (ChST) -> 10:00
function getOffset(timestamp) {
let offset = (timestamp.match(/[ -]\d{4}/) || [])[0];
return offset? `${offset.slice(0,3)}:${offset.slice(-2)}` : '';
}
// Reformat ECMA-262 default timestamp as ISO 8601 UTC,
// @param {string} timestamp - in format per ECMA-262 Date.prototype.toString
// @return {string} equivalent timestamp in ISO 8601 format with local date
// and time with local offset
// e.g. Fri Sep 10 2021 08:08:32 GMT 1000 (ChST) -> 2021-09-09T08:08:32.000 10:00
function toISOLocal(timestamp) {
let months = [,'Jan','Feb','Mar','Apr','May','Jun',
'Jul','Aug','Sep','Oct','Nov','Dec'];
let pad = n => ('0' n).slice(-2);
let [dayName, monthName, day, year, hour, min, sec, g, offset] = timestamp.split(/\W/);
let sign = /\ /.test(timestamp)? ' ' : '-';
return `${year}-${pad(months.indexOf(monthName))}-${pad(day)}`
`T${hour}:${min}:${sec}.000${getOffset(timestamp)}`;
}
// Examples
// In ECMAScript format, e.g. Fri Sep 10 2021 08:08:32 GMT 1000 (ChST)
let timestamp = new Date().toString();
console.log('Input : ' timestamp
'\nUTC : ' toUTC(timestamp)
'\nLocal : ' toISOLocal(timestamp)
'\nOffset: ' getOffset(timestamp)
);
The only issue is that the format of the offset returned by toString (±HHmm) is different to that produced by toISOString (±HH:mm). The getOffset function returns the latter format, which is easily changed if you need the other format.
Regarding the built–in parser, it's helpful to read Why does Date.parse give incorrect results?
* Where "local" means based on the host system settings, which may or may not match the values for the geographical location of the system.