get timezone offset of another timezone in javascript without using Strings - javascript

I want to calculate the offset from 'users time' to 'WET/WEST'.
I get the users offset with new Date().getTimezoneOffset().
But how do I get the offset for WET/WEST So that I can calcluate the combined offset of both?
For example, if the user is in central europe time (CET/CEST), in winter the combined offset would be -60 (CET) + 0 (WET) = -60. In summer, it would be -120 (CEST) + 60 (WEST) = -60. In this case it is always -60 but the user could also have a timezone without DST.
Is this possible without format it to a string and read out the new timezone from that string?

You can't use time zone abbreviations reliably for input, as they can be interpreted in many different ways. For example, CST might be either "Central Standard Time" in North America, or "China Standard Time", or "Cuba Standard Time". While some abbreviations like WET and WEST are unique, many are ambiguous. Refer to the list of time zone abbreviations here.
Instead, you need to know the IANA time zone identifier. One location that uses WET/WEST is Portugal, which as the IANA identifier of "Europe/Lisbon". You can find a list of identifiers here. Picking the correct identifier is important, as time zones change over time. Each identifier reflects the particular history of each region.
One you know the IANA time zone identifier, then you have options for how to use it:
In some modern browsers that fully support the time zone features of the ECMAScript Internationalization API (ECMA-402), you can do the following:
var d = new Date();
var s = d.toLocaleString(undefined, { timeZone: "Europe/Lisbon" })
This will convert the provided date and time to the correct time zone during formatting. Passing undefined in the first parameter will use the current locale for formatting.
There are a few downsides to this approach, as it is not yet implemented in all browsers, and there is no API for just retrieving the raw time zone offset of a particular point in time.
You can consider using a library that implements this functionality. I list several of them here. My personal preference is for moment.js with the moment-timezone addon, which you can do the following:
var m = moment.tz("Europe/Lisbon");
var s = m.format();
You can pass parameters to the format method to display the output however you like. You can also convert an existing time, such as:
var m = moment.utc("2016-01-01T00:00:00").tz("Europe/Lisbon");
var s = m.format();
You can also get the offset for a particular moment in time like so:
var m = moment.utc("2016-01-01T00:00:00").tz("Europe/Lisbon");
var offsetInMinutes = m.utcOffset();
var offsetAsString = m.format("Z");
You can write your own code for handling a particular time zone. Though this can be error prone and I don't generally recommend it. Updates can be particularly difficult if you go down this route.
Do also keep in mind that the offset for a particular time zone will vary depending on the date and time in effect. Therefore, new Date() which represents "now" may or may not always be the correct input, depending on your scenario.

I wanted to without a library too, but even with moment it has a fixed amount of timezone data so will break after 30 years if you don't keep updating, just erks me a bit. although this does use strings, nothing more complicated than parsing a string which is just an number
function getTimezoneOffset(dt, timezone) {
let getItem = function(format) {
format.timeZone = timezone;
return parseInt(dt.toLocaleString(
'en-US', format));
};
let adjDate = new Date(
getItem({year: 'numeric'}),
getItem({month: 'numeric'}) - 1, // months are zero based
getItem({day: 'numeric'}),
getItem({hour: 'numeric',hour12: false}),
getItem({minute: 'numeric'}));
let noSecs = new Date(dt.getTime());
noSecs.setSeconds(0, 0);
let diff = Math.round((adjDate.getTime() - noSecs.getTime()) / 60000);
return dt.getTimezoneOffset() - diff;
}

I wanted to do this without using a library, so I wrote this function that gives you the timezone offset between the given timezone and utc:
function getTimezoneOffsetFrom(otherTimezone) {
if (otherTimezone === void 0) { otherTimezone = "Europe/Amsterdam"; }
var date = new Date();
function objFromStr(str) {
var array = str.replace(":", " ").split(" ");
return {
day: parseInt(array[0]),
hour: parseInt(array[1]),
minute: parseInt(array[2])
};
}
var str = date.toLocaleString(['nl-NL'], { timeZone: otherTimezone, day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: false });
var other = objFromStr(str);
str = date.toLocaleString(['nl-NL'], { day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: false });
var myLocale = objFromStr(str);
var amsterdamOffset = (other.day * 24 * 60) + (other.hour * 60) + (other.minute);
var myLocaleOffset = (myLocale.day * 24 * 60) + (myLocale.hour * 60) + (myLocale.minute);
return myLocaleOffset - amsterdamOffset + date.getTimezoneOffset();
}
Maybe it can be approved by using en-US as a locale string and then the str.replace maybe needs to filter a comma or something.

function getTimezoneOffset(atTime, timeZone) {
const localizedTime = new Date(atTime.toLocaleString("en-US", {timeZone}));
const utcTime = new Date(atTime.toLocaleString("en-US", {timeZone: "UTC"}));
return (localizedTime.getTime() - utcTime.getTime()) / (60*60*1000);
}
// returns -6 or -7 depending on the time of year
console.log(getTimezoneOffset(new Date(), "America/Denver"));
// returns -5 or -6 depending on the time of year
console.log(getTimezoneOffset(new Date(), "America/Chicago"));

Related

Time in Known Location with Known Offset and NO Daylight Saving Time

With the hundreds of posts concerning Javascript time questions, I'm certain this has been addressed but I've been through a couple of dozen posts so far and none answer my specific question. I know the offset (-7) and in this particular State in the USA (Arizona) there is NO DST. I just want to display the time in Arizona to any user. All the posts I've reviewed seem to imply that I need to use
return new Date().getTimezoneOffset();
from the local computer as part of my calculations but I'm not sure why that would be necessary? Would this be a viable solution?
const now = new Date();
return {
hour: (now.getUTCHours() -7)
minute: now.getMinutes(),
};
There is no DST in Arizona presently, but that doesn't mean there never will be. Sure, unlikely - but not impossible.
A more robust solution, which also accounts for locale formatting preferences of the user, is as follows:
const s = new Date().toLocaleTimeString(undefined, {
timeZone: 'America/Phoenix'
});
console.log(s);
This will work with any IANA time zone identifier, accounting for DST when applicable, or not - depending on the time zone.
If you want the exact output as in your question, try something like this:
const s = new Date().toLocaleString('en', {
timeZone: 'America/Phoenix',
hour12: false,
hour: 'numeric',
minute: 'numeric'
});
const p = s.split(':');
const o = {
hour: parseInt(p[0]),
minute: parseInt(p[1])
};
console.log(o);
You do not need to getTimezoneOffset, but you will need to handle the case when the hours are smaller than 7:
const now = new Date();
console.log( {
hour: ((now.getUTCHours() -7) % 24),
minute: now.getMinutes(),
});
You can use the Intl object and Etc pseudo–timezone for a fixed offset. The EMCAScript version only supports whole hour offsets and the sign is the reverse of convention.
E.g.
console.log('Current time in UTC-7: ' +
new Date().toLocaleString('default',{timeZone:'Etc/GMT+7', hour:'numeric', minute:'2-digit'})
);
console.log('Current time in UTC-7: ' +
new Date().toLocaleTimeString('default',{timeZone:'Etc/GMT+7'})
);

Converting to local time using UTC time zone format in Javascript

Been trying to wrap my head around this for a good time now, but I can't think of a good way to do it.
I have an array with a bunch of different UTC time zones (in just format -07, -01, +03, +10, etc). What I'm trying to achieve is a way to show the local time of those time zones, possibly including the day and month.
Here's an example of the resulting string: Local time: 14:09 23/08
You can use Date.prototype.getTimezoneOffset() to get your local timezone. Calculate the difference between your local timezone and the destination timezone, multiply it with 3600000 and add it to a Date object containing the current time.
const now = new Date();
const timezoneOffset = now.getTimezoneOffset() / 60;
const timezones = ['-07', '-01', '+03', '+10'];
timezones.forEach(timezone => {
const difference = +timezone + timezoneOffset;
const time = new Date(now.getTime() + difference * 3600000);
console.log(`Local time: ${time.toLocaleTimeString([], { timeStyle: 'short', hour12: false })} ${time.toLocaleDateString([], { month: '2-digit', day: '2-digit' })}`);
});

Get Offset of the other Location in Javascript

I am in Asia and I want to calculate the offset of Australia. I know how to calculate the value of the offset the code is written below:
var timezone_offset = new Date().getTimezoneOffset();
But how to calculate it for the other locations? Anyone can guide me??
While this can be done in a short function, it would be best to use a library as there are many quirks to overcome. The offset can be determined using the timezone options of toLocaleString or Intl.DateTimeFormat.
However, if the language used for formatting matches the language of the location, it returns the timezone abbreviation instead of the offset. To deal with that, the following function first uses English and if that returns the abbreviation rather than an offset, it uses French. English offsets start with GMT, French offsets start with UTC. Where the offset is +0, they return just "GMT" or "UTC".
It's been tested with all IANA locations listed by wikipedia and seems to work for all of them but it should be tested more widely. Also, there should be feature tests before attempting to run it (i.e. support for Int.DateTimeFormat constructor, formatToParts method and the timeZoneName option).
// Return offset on date for loc in ±H[:mm] format. Minutes only included if not zero
function getTimezoneOffset(date, loc) {
// Try English to get offset. If get abbreviation, use French
let offset;
['en','fr'].some(lang => {
// Get parts - can't get just timeZoneName, must get one other part at least
let parts = new Intl.DateTimeFormat(lang, {
minute: 'numeric',
timeZone: loc,
timeZoneName:'short'
}).formatToParts(date);
// Get offset from parts
let tzName = parts.filter(part => part.type == 'timeZoneName' && part.value);
// timeZoneName starting with GMT or UTC is offset - keep and stop looping
// Otherwise it's an abbreviation, keep looping
if (/^(GMT|UTC)/.test(tzName[0].value)) {
offset = tzName[0].value.replace(/GMT|UTC/,'') || '+0';
return true;
}
});
// Format offset as ±HH:mm
// Normalise minus sign as ASCII minus (charCode 45)
let sign = offset[0] == '\x2b'? '\x2b' : '\x2d';
let [h, m] = offset.substring(1).split(':');
return sign + h.padStart(2, '0') + ':' + (m || '00');
}
let d = new Date();
console.log('Current offset for following locations:');
['Australia/Yancowinna',
'Australia/Lord_Howe',
'Australia/Canberra',
'Pacific/Honolulu',
'Europe/London',
'Canada/Eastern'
].forEach( loc =>
console.log(loc + ': ' + getTimezoneOffset(d, loc))
);
I don't suggest you use this function, it's really to show how messy getting the offset for a specific location can be.
Note that Australia has a number of offsets and some places observe daylight saving and others don't.
The accepted answer is correct, but if you are fine with some of the timezones to appear without the percise offset, a shorter way that doesn't require string parsing / manipulation will be:
// Return offset on date for loc in ±H[:mm] format.
function getTimezoneOffset(date, loc) {
return new Intl.DateTimeFormat('en-US', { timeZone: loc, timeZoneName: "shortOffset" })
.formatToParts(date)
.filter(e => e.type === "timeZoneName")[0].value
}
let d = new Date();
console.log('Current offset for following locations:');
['Australia/Yancowinna',
'Australia/Lord_Howe',
'Australia/Canberra',
'Pacific/Honolulu',
'Europe/London',
'Canada/Eastern',
'America/Los_Angeles',
'Asia/Kolkata'
].forEach(loc =>
console.log(loc + ': ' + getTimezoneOffset(d, loc))
);
You can see the the options for the timezone name format here [1]:
timeZoneName - The localized representation of the time zone name.
Possible values are:
"long" Long localized form (e.g., Pacific Standard Time, Nordamerikanische Westküsten-Normalzeit)
"short" Short localized form (e.g.: PST, GMT-8)
"shortOffset" Short localized GMT format (e.g., GMT-8)
"longOffset" Long localized GMT format (e.g., GMT-0800)
"shortGeneric" Short generic non-location format (e.g.: PT, Los Angeles Zeit).
"longGeneric" Long generic non-location format (e.g.: Pacific Time, Nordamerikanische Westküstenzeit)
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#syntax:~:text=timeZoneName,Nordamerikanische%20Westk%C3%BCstenzeit)

How do I compare a JavaScript date against a date in a specific timezone?

I want to see if the current time is greater than Monday of this week at 5:00 pm mountain time, no matter what timezone this code is running in. I've got something like this:
function isAfterMondayEvening() {
var now = new Date();
var mondayEvening = dateFns.setHours(dateFns.setDay(now, 1), 17);
return dateFns.compareAsc(now, mondayEvening);
}
console.log(isAfterMondayEvening());
<script src="//cdn.rawgit.com/date-fns/date-fns/a0005af7d1c3f70c88b8e619bfdff4bf85122863/dist/date_fns.js"></script>
If the server or browser this is running in is in a different timezone, then it will compare the time to Monday at 5 in their timezone. I want to to be compared to Monday at 5 pm in mountain time, no matter what timezone this code runs in. How I can do this?
A method I have used is to normalize both dates to GMT. Assuming your server date is in GMT already, you can convert the browser time to GMT by subtracting the timezone offset.
For example, I am in Atlantic Standard Time (GMT+4). To get the current time as if I was in GMT, I use the formula:
2018-02-09T15:00:00+0400 - (4 * 60 * 60 * 1000) = 2018-02-09T15:00:00Z`
...where 4 is the offset in hours.
Specifically in JS:
const browserOffset = new Date().getTimezoneOffset();
const timeFromServer = getTimeFromServer();
const currentTimeAsGmt = new Date(Date.now() - (browserOffset * 60 * 1000));
// Now compare timeFromServer (already in GMT) to currentTimeAsGmt
In JS, Date#getTimezoneOffset returns the offset in minutes so I omit an extra * 60.
If you are cool with IE 10+, use luxon (https://moment.github.io/luxon/index.html)
function isAfterMondayEvening() {
const currentTime = luxon.DateTime.local().setZone('America/Denver');
const mondayEvening = currentTime.set({weekday: 1, hour: 17}).startOf('hour');
return currentTime > mondayEvening;
}
Assuming you can figure out this week's Monday year/month/day indexes, I think you can do this without libraries. The trick is to represent your known Mountain time in terms of UTC:
let utcMonday = new Date(Date.UTC(2018, 2 - 1, 5, 24));
let cstDate = new Date();
let currentTimeGreater = cstDate > utcMonday;
// The rest isn't needed, but shows some console logs of what is going on
let locale = "en-US";
let timeFormatOptions = {
weekday: "short",
hour: "numeric",
minute: "numeric",
timeZoneName: "long",
timeZone: "America/Denver"
};
let formattedMountainMonday = utcMonday.toLocaleDateString(
locale,
timeFormatOptions
);
let formattedCurrentTime = cstDate.toLocaleDateString(
locale,
timeFormatOptions
);
console.log(formattedMountainMonday);
console.log(
`Current time in relation to Mountain time: ${formattedCurrentTime}`
);
console.log(
`Current time is greater than this week's Mountain Monday at 5:00 PM: ${currentTimeGreater}`
);
The US Mountain Time (MT) offset is -0700. Javascript dates are UTC at heart, so all you need to do is compare the internal UTC time value to MT. By "Monday of this week" I presume you want true for Sunday and Monday up to 17:00. If you just mean Monday, then remove the d.getUTCDay() == 0 || part.
function isBefore1700MT(date) {
// Copy date so don't affect original
var d = new Date(date);
// Adjust internal UTC to MT
d.setUTCHours(d.getUTCHours() - 7);
// Return true if the UTC day is Sunday or Monday before 17:00
return d.getUTCDay() == 0 || (d.getUTCDay() == 1 && d.getUTCHours() < 17);
}
// Test using current local date and time
var date = new Date();
console.log(date.toString() + ' : ' + isBefore1700MT(date));
When you say "mountain time", are you always talking about Mountain Standard Time or are you wanting to account for Mountain Daylight Time as well? If you're always talking about Mountain Standard Time, you can ignore my answer since your case is simpler. I'm going to address the possibility that you want to account for Mountain Daylight Time as well, which is a more complex problem.
If you're taking daylight time into account, keep in mind that not every place inside the Mountain Time zone observes Mountain Daylight Time, so you actually need to know the location inside the Mountain Time zone (e.g., is it Arizona or Utah?). Once you know that, you need a library like Moment Timezone that has adequate time zone information. You then have to find the most accurate tz database timezone name and use it, with the help of Moment Timezone to figure out the actual UTC time for "Monday of this week at 5:00 pm mountain time in this particular location". Your code would look something like this:
// Use whatever time and timezone you're trying to compare with.
const targetDate = moment()
.tz('America/Denver')
.day(1) // Monday
.hour(17) // 5pm
.minute(0)
.second(0)
.millisecond(0)
.toDate();
if (new Date() > targetDate) {
console.log('after target time');
} else {
console.log('before target time');
}

How can I convert datetime microformat to local time in javascript?

I have a page that is currently using the datetime microformat to display a timestamp, but I have only been showing the human-readable time for my own time zone:
<abbr class="published" title="2009-01-09T09:16:00-05:00">
Friday, January 9, 2009 at 9:16 am (EST)</abbr>
What I'd like to do is rewrite the innerHTML for the abbr tag to be the same format, but in the user's local timezone. So for a reader in Seattle, the above should be converted to:
<abbr class="published" title="2009-01-09T09:16:00-05:00">
Friday, January 9, 2009 at 6:16 am (PST)</abbr>
I've looked at the Javascript Date object, which allows me to get the local timezone offset. But I have a few problems:
I don't see an easy way to create a new Date object from an ISO-8601 timestamp. (I suppose I could parse with substrings or regex if there's no faster way.)
I don't see a way to get the named abbreviation for the timezone. For example, for a reader in Seattle, I'd want the time to have "(PST)" appended to the end, otherwise it is not clear to that user that the timestamp has been converted (especially if he is a frequent visitor and has become accustomed to the fact that my times are in EST).
Here is code of mine that parses an ISO timestamp:
function isoDateStringToDate (datestr) {
if (! this.re) {
// The date in YYYY-MM-DD or YYYYMMDD format
var datere = "(\\d{4})-?(\\d{2})-?(\\d{2})";
// The time in HH:MM:SS[.uuuu] or HHMMSS[.uuuu] format
var timere = "(\\d{2}):?(\\d{2}):?(\\d{2}(?:\\.\\d+)?)";
// The timezone as Z or in +HH[:MM] or -HH[:MM] format
var tzre = "(Z|(?:\\+|-)\\d{2}(?:\\:\\d{2})?)?";
this.re = new RegExp("^" + datere + "[ T]" + timere + tzre + "$");
}
var matches = this.re.exec(datestr);
if (! matches)
return null;
var year = matches[1];
var month = matches[2] - 1;
var day = matches[3];
var hour = matches[4];
var minute = matches[5];
var second = Math.floor(matches[6]);
var ms = matches[6] - second;
var tz = matches[7];
var ms = 0;
var offset = 0;
if (tz && tz != "Z") {
var tzmatches = tz.match(/^(\+|-)(\d{2})(\:(\d{2}))$/);
if (tzmatches) {
offset = Number(tzmatches[2]) * 60 + Number(tzmatches[4]);
if (tzmatches[1] == "-")
offset = -offset;
}
}
offset *= 60 * 1000;
var dateval = Date.UTC(year, month, day, hour, minute, second, ms) - offset;
return new Date(dateval);
}
Unfortunately, it doesn't handle timezone abbreviations either. You would have to modify the "tzre" expression to accept letters, and the only solution I know of to deal with timezone abbreviations in Javascript is to have a look-up table which you keep up to date manually in the event of changes to regional daylight savings times.
EcmaScript formalized the addition of an ISO-8601 style string as an imput for a JavaScript date. Since most JS implementations don't support this, I created a wrapper to the Date object, that has this functionality. If you set the title tags to output in UTC/GMT/Z/Zulu offset, you can use my EcmaScript 5 extensions for JS's Date object.
For DateTime values that are to be used in client-side scripts, I generally try to always do the following. Store date+time in UTC zone (even in databases). Transmit date-times in UTC zone. From client to server, you can use the .toISOString() method in the above link. From server-to client this is relatively easy.
Via jQuery (with extension):
$('.published').each(function(){
var dtm = new Date(this.title);
if (!isNaN(dtm)) {
this.text(dtm.toString());
}
});
I don't recall if I added support for non-utc date-times in the input, but wouldn't be too hard to account for them.

Categories

Resources