I'm working on a function that should check if a given dateString has the same timeZone as the browser timeZone. To get the browser timeZone I can use either a) Intl.DateTimeFormat().resolvedOptions().timeZone which will return Europe/Amsterdam for example or b) new Date().getTimeZoneOffset() that will return -60. Both are fine.
The tricky part is to get the timeZone from the dateString I want to pass, for example from: 2021-01-01T00:00:00-05:00 (which should be America/New_York or 300 AFAIK). How can I get the timeZone from that date? I tried to do: new Date('2021-01-01T00:00:00-05:00').getTimeZoneOffset() but that will convert it to the timeZone of my browser again, returning -60.
Example of function:
function isSameTimeZone(date) {
// function to get time zone here
const a = getTimeZone(date)
return a === new Date().getTimeZoneOffset() || Intl.DateTimeFormat().resolvedOptions().timeZone
}
Testcases
2021-01-01T00:00:00-08:00 (SF)
2021-01-01T00:00:00-05:00 (NY)
2021-01-01T00:00:00+05:30 (Mumbai)
2021-01-01T00:00:00+01:00 (Amsterdam)
Anyone out there with a solution? Thanks in advance!
Here's my method;
function checkTimezone(dateString) {
const testDate = new Date(dateString);
const dateRegex = /\d{4}-\d{2}-\d{2}/;
const myDate = new Date(testDate.toISOString().match(dateRegex)[0]);
return !(testDate - myDate);
}
const testCases = ['2021-01-01T00:00:00-05:00', '2021-01-01T00:00:00-08:00', '2021-01-01T00:00:00-05:00', '2021-01-01T00:00:00+05:30', '2021-01-01T00:00:00+01:00'];
testCases.forEach(testCase => console.log(checkTimezone(testCase)));
So here's how it works, you pass in your date string and create a new Date() instance with it.
Then I get the ISO string with the Date.ISOString() method and match it with the original string to get the date and create another Date instance from it without the time.
Then I find the difference (comes in milliseconds), and convert it to minutes
So I've been testing around a bit and in the end I came up with the following solution, based on the dates I provided in my question.
const getTimeZoneOffsetFromDate = date => {
/*
Check if the offset is positive or negative
e.g. +01:00 (Amsterdam) or -05:00 (New York)
*/
if (date.includes('+')) {
// Get the timezone hours
const timezone = date.split('+')[1]
// Get the hours
const hours = timezone.split(':')[0]
// Get the minutes (e.g. Mumbai has +05:30)
const minutes = timezone.split(':')[1]
/*
Amsterdam:
const offset = 01 * -60 = -60 + -0 = -60
*/
const offset = hours * -60 + parseInt(-minutes)
return offset === new Date().getTimezoneOffset()
}
// Repeat
const timezone = date.slice(date.length - 5)
const hours = timezone.split(':')[0]
const minutes = timezone.split(':')[1]
/*
New York:
const offset = 05 * 60 = 300 + 0 = 300
*/
const offset = hours * 60 + parseInt(minutes)
return offset === new Date().getTimezoneOffset()
}
console.log(getTimeZoneOffsetFromDate('2021-01-01T00:00:00+01:00'))
Related
How can I calculate the difference between UTC (that I generate with new Date()) and European Standard time?
Something like
const amsterdam = new Date('Europe/Amsterdam')
amsterdam.getTimezoneOffset() // returns the minutes of offset in this case 60
I can't simply use 1 hour since the time shifts in the winter and summer! :(
You can get the offset for a particular location for any date using Intl.DateTimeFormat with suitable options, e.g.
/* #param {string} loc - IANA representative location
* #param {Date} date - default to current date
* #returns {string} offset as ±H[mm]
*/
function getOffsetForLoc(loc, date = new Date()) {
// Use Intl.DateTimeFormat to get offset
let opts = {hour: 'numeric', timeZone: loc, timeZoneName:'short'};
let getOffset = lang => new Intl.DateTimeFormat(lang, opts)
.formatToParts(date)
.reduce((acc, part) => {
acc[part.type] = part.value;
return acc;
}, {}).timeZoneName;
let offset = getOffset('en');
// If offset is an abbreviation, change language
if (!/^UCT|GMT/.test(offset)) {
offset = getOffset('fr');
}
// Remove GMT/UTC
return offset.substring(3);
}
// Get current offsets for following locations
['Europe/Amsterdam',
'America/New_York',
'Asia/Kolkata']
.forEach(loc => console.log(`${loc} : ${getOffsetForLoc(loc)}`));
// Get offsets in Amsterdam
[new Date(2021,0), // 1 Jan 2021
new Date(2021,5) // 1 Jun 2021
].forEach(d => console.log(`Offset for Amsterdam on ${d.toLocaleDateString()} ${getOffsetForLoc('Europe/Amsterdam', d)}`));
What I eventually ended up doing and what worked perfectly for me was the following
const timezoneOffsetInHours =
moment().tz('Europe/Amsterdam').hour() - new Date().getHours()
This returns the hours that Amsterdam is ahead of UTC.
For others that are looking for a solution using luxon,
const {DateTime} = require('luxon');
const shortOffset = DateTime.utc().setZone('Europe/Amsterdam').toFormat('ZZ');
// +01:00
const narrowOffset = DateTime.utc().setZone('Europe/Amsterdam').toFormat('Z');
// +1
const techieOffset = DateTime.utc().setZone('Europe/Amsterdam').toFormat('ZZZ');
// +0100
Although, I've seen multiple similar questions here on SO but none of them could help me to figure out what's wrong with my calculation. I know I can use library such as Moment.JS to simplify my solution but I want native JavaScript only solution.
I'm trying to calculate duration (in hours and minutes) between two Date objects but I'm getting negative (incorrect) duration.
function padNumber(number, width = 2, padWith = '0') {
const strNum = number.toString();
return strNum.length >= width ? strNum : new Array(width - strNum.length + 1).join(padWith) + strNum;
}
// Get UTC date time from PHP date (Y-m-d) and time (H:i:s) strings
function getUTCDateTime(date, time, timezoneOffset = -480) {
const dateParts = date.split('-').map((el) => Number(el)); // Y-m-d
const timeParts = time.split(':').map((el) => Number(el)); // H:i:s
const dateTimeUTC = new Date(Date.UTC(dateParts[0], dateParts[1], dateParts[2], timeParts[0], timeParts[1], timeParts[2]));
// Set back Singapore specific time (GMT+8:00)
dateTimeUTC.setUTCHours(dateTimeUTC.getUTCHours() + timezoneOffset / 60);
return dateTimeUTC;
}
function getDuration(timeStart, timeEnd = new Date()) {
const msDiff = timeEnd.getTime() - timeStart.getTime();
const minDiff = msDiff / 60000;
const hourDiff = Math.floor(msDiff / 3600000);
return {
hours: this.padNumber(hourDiff, 2),
minutes: this.padNumber(Math.floor(minDiff - 60 * hourDiff), 2)
};
}
// Got from server (in Singapore timezone)
const serverDate = '2018-10-18';
const serverTime = '00:22:51';
// Convert server date and time (timezone specific) strings to Date object
const serverUTC = getUTCDateTime(serverDate, serverTime);
// Get duration between server time and now
const duration = getDuration(serverUTC);
// Expected positive value but getting negative as server time is in past
console.log(duration);
I expected positive value in console log but I'm getting negative. Have I missed anything?
The problem stems from the fact that months are zero-based in JavaScript (i.e. January is 0, February is 1, and so on). Your date construction in getUTCDateTime() doesn't take this into account.
This line:
const dateTimeUTC = new Date(Date.UTC(dateParts[0], dateParts[1], dateParts[2], timeParts[0], timeParts[1], timeParts[2]));
Should be:
const dateTimeUTC = new Date(Date.UTC(dateParts[0], dateParts[1] - 1, dateParts[2], timeParts[0], timeParts[1], timeParts[2]));
Complete snippet:
function padNumber(number, width = 2, padWith = '0') {
const strNum = number.toString();
return strNum.length >= width ? strNum : new Array(width - strNum.length + 1).join(padWith) + strNum;
}
// Get UTC date time from PHP date (Y-m-d) and time (H:i:s) strings
function getUTCDateTime(date, time, timezoneOffset = -480) {
const dateParts = date.split('-').map((el) => Number(el)); // Y-m-d
const timeParts = time.split(':').map((el) => Number(el)); // H:i:s
const dateTimeUTC = new Date(Date.UTC(dateParts[0], dateParts[1] - 1, dateParts[2], timeParts[0], timeParts[1], timeParts[2]));
// Set back Singapore specific time (GMT+8:00)
dateTimeUTC.setUTCHours(dateTimeUTC.getUTCHours() + timezoneOffset / 60);
return dateTimeUTC;
}
function getDuration(timeStart, timeEnd = new Date()) {
const msDiff = timeEnd.getTime() - timeStart.getTime();
const minDiff = msDiff / 60000;
const hourDiff = Math.floor(msDiff / 3600000);
return {
hours: this.padNumber(hourDiff, 2),
minutes: this.padNumber(Math.floor(minDiff - 60 * hourDiff), 2)
};
}
// Got from server (in Singapore timezone)
const serverDate = '2018-10-18';
const serverTime = '00:22:51';
// Convert server date and time (timezone specific) strings to Date object
const serverUTC = getUTCDateTime(serverDate, serverTime);
// Get duration between server time and now
const duration = getDuration(serverUTC);
// Expected positive value but getting negative as server time is in past
console.log(duration);
I have a challenge where backend data is always stored in UTC time. Our front-end data is always presented in CST. I don't have access to this 'black box.'
I would like to mirror this in our data warehouse. Which is based in Europe (CET). So "local" conversion will not work.
I'm wondering the simplest, most straightforward way to accurately convert UTC time (I can have it in epoch milliseconds or a date format '2015-01-01 00:00:00') to Central Standard Time. (which is 5 or 6 hours behind based on Daylight Savings).
I see a lot of threads about converting to 'local' time ... again I don't want this, nor do I simply want to subtract 6 hours which will be wrong half the year.
Anyone have any ideas? This seems to be a very common problem but I've been searching for a while, and have found nothing.
Using moment.js with the moment-timezone add-on makes this task simple.
// construct a moment object with UTC-based input
var m = moment.utc('2015-01-01 00:00:00');
// convert using the TZDB identifier for US Central time
m.tz('America/Chicago');
// format output however you desire
var s = m.format("YYYY-MM-DD HH:mm:ss");
Additionally, since you are referring to the entire North American Central time zone, you should say either "Central Time", or "CT". The abbreviation "CST" as applied to North America explicitly means UTC-6, while the "CDT" abbreviation would be used for UTC-5 during daylight saving time.
Do be careful with abbreviations though. "CST" might mean "China Standard Time". (It actually has five different interpretations).
You can use the time zone offset to determine whether 5 or 6 hours should be subtracted.
var dateJan;
var dateJul;
var timezoneOffset;
var divUTC;
var divCST;
// Set initial date value
dateValue = new Date('10/31/2015 7:29:54 PM');
divUTC = document.getElementById('UTC_Time');
divCST = document.getElementById('CST_Time');
divUTC.innerHTML = 'from UTC = ' + dateValue.toString();
// Get dates for January and July
dateJan = new Date(dateValue.getFullYear(), 0, 1);
dateJul = new Date(dateValue.getFullYear(), 6, 1);
// Get timezone offset
timezoneOffset = Math.max(dateJan.getTimezoneOffset(), dateJul.getTimezoneOffset());
// Check if daylight savings
if (dateValue.getTimezoneOffset() < timezoneOffset) {
// Adjust date by 5 hours
dateValue = new Date(dateValue.getTime() - ((1 * 60 * 60 * 1000) * 5));
}
else {
// Adjust date by 6 hours
dateValue = new Date(dateValue.getTime() - ((1 * 60 * 60 * 1000) * 6));
}
divCST.innerHTML = 'to CST = ' + dateValue.toString();
<div id="UTC_Time"></div>
<br/>
<div id="CST_Time"></div>
Maybe you can use something like the following. Note, that is just an example you might need to adjust it to your needs.
let cstTime = new Date(createdAt).toLocaleString("es-MX", {
timeZone: "America/Mexico_City" });
You can use below code snippet for converting.
function convertUTCtoCDT() {
var timelagging = 6; // 5 or 6
var utc = new Date();
var cdt = new Date(utc.getTime()-((1 * 60 * 60 * 1000) * timelagging));
console.log("CDT: "+cdt);
}
let newDate = moment(new Date()).utc().format("YYYY-MM-DD HH:mm:ss").toString()
var m = moment.utc(newDate);
m.tz('America/Chicago');
var cstDate = m.format("YYYY-MM-DD HH:mm:ss");
You can use below code snippet
// Get time zone offset for CDT or CST
const getCdtCstOffset = () => {
const getNthSunday = (date, nth) => {
date.setDate((7*(nth-1))+(8-date.getDay()));
return date;
}
const isCdtTimezoneOffset = (today) => {
console.log('Today : ', today);
let dt = new Date();
var mar = new Date(dt.getFullYear(), 2, 1);
mar = getNthSunday(mar, 2);
console.log('CDT Start : ', mar);
var nov = new Date(dt.getFullYear(), 10, 1, 23, 59, 59);
nov = getNthSunday(nov, 1);
console.log('CDT End : ', nov);
return mar.getTime()< today.getTime() && nov.getTime()> today.getTime();
}
var today = new Date()// current date
if (isCdtTimezoneOffset(today)) {
return -5
} else {
return -6
}
}
let cstOrCdt = new Date();
cstOrCdt.setHours(cstOrCdt.getHours()+getCdtCstOffset())
console.log('CstOrCdt : ', cstOrCdt);
The date is stored in the database as datetimeoffset(7).
The MVC controller gets the date from the database in the format "10/8/2015 6:05:12 PM -07:00" and passes it to the view as it is. I am trying to convert it to the correct date to show that in the view as mm/dd/yyy by doing the following:
var myDate = "10/8/2015 6:05:12 PM -07:00";
var newDate = New Date(myDate);
Then I'm formatting it to mm/dd/yyyy format after extracting the day, month and year.
IE11 and Safari do not like this and show error "Invalid date" in the console at the line
var newDate = New Date(myDate)
Chrome or Firefox doesn't show any problems.
Now I know that "10/8/2015 6:05:12 PM -07:00" is not a valid datestring. So my question is, how to handle this situation so all major browsers will show the correct date?
This function should work, I'm aware it needs some refactoring but it may be a good starting point.
function readDate(inputDate) {
// extract the date that can be parsed by IE ("10/8/2015 6:05:12 PM")
// and the timezone offset "-07:00"
var parsedDate = /([\s\S]*?)\s*([+-][0-9\:]+)$/.exec(inputDate);
//create new date with "datetimeoffset" string
var dt = new Date(parsedDate[1]);
// get millisecs
var localTime = dt.getTime();
// get the offset in millisecs
// notice the conversion to milliseconds
// the variable "localTzOffset" can be negative
// because the standar (http://www.w3.org/TR/NOTE-datetime)
var localTzOffset = dt.getTimezoneOffset() * 60 * 1000;
// to get UTC time
var utcTime = localTime + localTzOffset;
// extract hours and minutes difference
// for given timezone
var parsed = /([+-][0-9]+)\:([0-9]+)/.exec(parsedDate[2]);
var hours = parseInt(parsed[1]);
var minutes = parseInt(parsed[2]);
// convert extracted difference to milliseconds
var tzOffset = Math.abs(hours * 60 * 60 * 1000) + (minutes * 60 * 1000);
// taking into accout if negative
tzOffset = hours > 1 ? tzOffset : tzOffset * -1;
// construct the result Date
return new Date(utcTime + tzOffset);
}
Usage:
var date = readDate("10/8/2015 6:05:12 PM -07:00");
This question already has answers here:
Why does Date.parse give incorrect results?
(11 answers)
Closed 8 years ago.
I need to parse a string to date which comes in below format
2012-05-23 12:12:00-05:00
Date.parse("2012-05-23 12:12:00-05:00")
It is working fine with chorme but not in Firefox and IE. Any suggestions would be helpful.
I deleted my previous (less relevant) answer. This function should suit your purposes much better.
The key strategy is to get an absolute representation of your time without the time zone, and then adjust it backward or forward based on the specified timezone. What it spits out is a Date object in your browser's local time zone, but it will represent exactly the same moment in time as the string does.
Also, the format you specified isn't quite to spec, so the regex I wrote accepts your version as well as ISO8601.
Date.createFromString = function (string) {
'use strict';
var pattern = /^(\d\d\d\d)-(\d\d)-(\d\d)[ T](\d\d):(\d\d):(\d\d)([+-Z])(\d\d):(\d\d)$/;
var matches = pattern.exec(string);
if (!matches) {
throw new Error("Invalid string: " + string);
}
var year = matches[1];
var month = matches[2] - 1; // month counts from zero
var day = matches[3];
var hour = matches[4];
var minute = matches[5];
var second = matches[6];
var zoneType = matches[7];
var zoneHour = matches[8] || 0;
var zoneMinute = matches[9] || 0;
// Date.UTC() returns milliseconds since the unix epoch.
var absoluteMs = Date.UTC(year, month, day, hour, minute, second);
var ms;
if (zoneType === 'Z') {
// UTC time requested. No adjustment necessary.
ms = absoluteMs;
} else if (zoneType === '+') {
// Adjust UTC time by timezone offset
ms = absoluteMs - (zoneHour * 60 * 60 * 1000) - (zoneMinute * 60 * 1000);
} else if (zoneType === '-') {
// Adjust UTC time by timezone offset
ms = absoluteMs + (zoneHour * 60 * 60 * 1000) + (zoneMinute * 60 * 1000);
}
return new Date(ms);
};
Use moment.js's date parsing:
var date = moment("2012-05-23 12:12:00-05:00");
See http://momentjs.com/docs/#/parsing/string/