I use getTimezoneOffset() to get the offset in minutes for a given date object. Is is possible to use javascript to get timezone offset of a city or a country?
for example:
var offset = getCityOffset("Miami"); // returns -240
No, there is nothing built-in to javascript which allows you to get the number of minutes to offset for specific time zones/cities.
getTimeZoneOffset works for the current browser's settings
MomentJS Timezone extensions has some of this sort of functionality, which is of course reliant on the MomentJS library.
If you have access to Lat/Long values, then google provide a timezone API
I'm going to answer this question for Node.js using TypeScript (please remove the types if you want to use with plan JavaScript). For that, we will need 2 NPM packages.
moment-timezone
city-timezones
Disclaimer: Timezones are COMPLEX. Not all timezones are 1 hour apart and some daylight saving time settings are weird with, for example, 15 minutes difference instead of the standard 60 minutes. This is a naive implementation that suited my use case. Use with discretion.
Code:
import * as cityTimeZones from "city-timezones";
import * as moment from "moment-timezone";
/**
* Returns the UTC offset for the given timezone
* #param timezone Example: America/New_York
*/
export function getNormalizedUtcOffset(timezone: string): number | null {
const momentTimezone = moment.tz(timezone);
if (!momentTimezone) {
return null;
}
let offset = momentTimezone.utcOffset();
if (momentTimezone.isDST()) {
// utcOffset will return the offset normalized by DST. If the location
// is in daylight saving time now, it will be adjusted for that. This is
// a NAIVE attempt to normalize that by going back 1 hour
offset -= 60;
}
return offset/60;
}
/**
* Returns the offset range for the given city or region
* #param location
*/
export function getUtcOffsetForLocation(location: string): number[] | null {
const timezones = cityTimeZones.findFromCityStateProvince(location);
if (timezones && timezones.length) {
// timezones will contain an array of all timezones for all cities inside
// the given location. For example, if location is a country, this will contain
// all timezones of all cities inside the country.
// YOU SHOULD CACHE THE RESULT OF THIS FUNCTION.
const offsetSet = new Set<number>();
for (let timezone of timezones) {
const offset = getNormalizedUtcOffset(timezone.timezone);
if (offset !== null) {
offsetSet.add(offset);
}
}
return [...offsetSet].sort((a, b) => a - b);
}
return null;
}
Unit tests (with Jest)
import { getUtcOffsetForLocation } from "../timezone";
describe("timezone", () => {
describe("getUtcOffsetForLocation", () => {
it("should work for Lisbon", () => {
expect(getUtcOffsetForLocation("Lisbon")).toEqual([0]);
});
it("should work for Berlin", () => {
expect(getUtcOffsetForLocation("Berlin")).toEqual([1]);
});
it("should work for Germany", () => {
expect(getUtcOffsetForLocation("Germany")).toEqual([1]);
});
it("should work for the United States", () => {
// when the region has multiple timezones,
expect(getUtcOffsetForLocation("United States")).toEqual( [-10, -9, -8, -7, -6, -5, -4]);
});
it("should return null for a non-existing region", () => {
// when the region has multiple timezones,
expect(getUtcOffsetForLocation("Blablabla")).toEqual( null);
});
});
});
you can use toLocaleTimeString() to find out the time of a particular city of a aprticular country , For example i want to determine the current time in 24 hour of India , so run this script
`let indianTime = new Date().toLocaleTimeString("en-US",
{timeZone:'Asia/Kolkata',timestyle:'full',hourCycle:'h24'})
console.log(indianTime)`
similarly for time of Dhaka/Bangladesh we can do the same
`let bangladeshTime = new Date().toLocaleTimeString("en-US",
{timeZone:'Asia/Dhaka',timestyle:'full',hourCycle:'h24'})
console.log(bangladeshTime)`
here i used a parameter en-Us to get the standard time format
TimeZoneOffset :
var d = new Date()
alert(d.getTimezoneOffset());
toLocaleTimeString() : This converts time to the local.
var d = new Date();
alert(d.toLocaleTimeString());
Using a library: refer, Auto Time zone detection & momentjs
function getTimeOffset(country = 'America/New_York', summerTime = false) {
let date = new Date(new Date().getFullYear(), summerTime ? 6 : 11, 1);
let wordTime = new Date(date.toISOString().substr(0, 19)).getTime();
let localTime = new Date(date.toLocaleString('en', { timeZone: country })).getTime();
return (wordTime - localTime) / 1000 / 60;
}
There is no default method. Although, there are few ways you can do it easily using the same getTimeZoneOffSet method. One such tutorial is here. http://www.techrepublic.com/article/convert-the-local-time-to-another-time-zone-with-this-javascript/
function calcTime(city, offset) {
// create Date object for current location
d = new Date();
// convert to msec
// add local time zone offset
// get UTC time in msec
utc = d.getTime() + (d.getTimezoneOffset() * 60000);
// create new Date object for different city
// using supplied offset
nd = new Date(utc + (3600000*offset));
// return time as a string
return "The local time in " + city + " is " + nd.toLocaleString();
}
Note that this function requires you to pass the difference from GMT manually. You can use your code behind to get that parameter.
Related
I got the following string: "2022/05/01 03:10:00" and I need to create a Date object forcing it to use Chile's UTC offset.
The problem is that because of Daylight saving time (DST) the offset changes twice a year.
How can get that Date object, for example, using the "America/Santiago" TZ db name?
Something like:
new Date("2022/05/01 03:10:00" + getUtcOffset("America/Santiago")).
function getUtcOffset(tzDbName) {
..
}
Returns -3 or -4, depending the time in the year.
EDIT:
I ended using a nice trick for determining if DST was on or off.
reference
const dst = hasDST(new Date(strDate));
function hasDST(date = new Date()) {
const january = new Date(date.getFullYear(), 0, 1).getTimezoneOffset();
const july = new Date(date.getFullYear(), 6, 1).getTimezoneOffset();
return Math.max(january, july) !== date.getTimezoneOffset();
}
Then I could create the date with the correct timezone depending on that variable.
if (dst) {
let d = new Date(strDate + " GMT-0300");
return d;
} else {
let d = new Date(strDate + " GMT-0400");
return d;
}
Thanks everyone!
EDIT2:
I finally found a very nice library that does exactly what I was looking for:
https://date-fns.org/v2.28.0/docs/Time-Zones#date-fns-tz
const { zonedTimeToUtc, utcToZonedTime, format } = require('date-fns-tz')
const utcDate = zonedTimeToUtc('2022-05-05 18:05', 'America/Santiago')
This has been discussed before here.
Haven't tested it, but it appears that the simplest solution is:
// Example for Indian time
let indianTime = new Date().toLocaleTimeString("en-US",
{timeZone:'Asia/Kolkata',timestyle:'full',hourCycle:'h24'})
console.log(indianTime)
You can check the link for more complex answers and libraries
Generals notes
To get the time zone name use:
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)
To get the difference from UTC (in minutes) use:
var offset = new Date().getTimezoneOffset();
console.log(offset);
// if offset equals -60 then the time zone offset is UTC+01
I have this below functionin my webapp to check if from OPEN to CLOSE time has elapse using a country timezone and it's working fine. Am trying to optimize my website, so my question is how can i make a function like this in javascript without the use of moment timezone? The moment timezone file is large and this is the only usage of it on my website.
function isOpen(openTime, closeTime, timezone) {
// handle special case
if (openTime === "24HR") {
return "open";
}
// get the current date and time in the given time zone
const now = moment.tz(timezone);
// Get the exact open and close times on that date in the given time zone
const date = now.format("YYYY-MM-DD");
const storeOpenTime = moment.tz(date + ' ' + openTime, "YYYY-MM-DD h:mmA", timezone);
const storeCloseTime = moment.tz(date + ' ' + closeTime, "YYYY-MM-DD h:mmA", timezone);
let check;
if (storeCloseTime.isBefore(storeOpenTime)) {
// Handle ranges that span over midnight
check = now.isAfter(storeOpenTime) || now.isBefore(storeCloseTime);
} else {
// Normal range check using an inclusive start time and exclusive end time
check = now.isBetween(storeOpenTime, storeCloseTime, null, '[)');
}
return check ? "open" : "closed";
}
const zone = "Asia/Kuala_Lumpur";
console.log("24HR", isOpen("24HR", undefined, zone));
console.log("2:00AM-8:00AM", isOpen("2:00AM", "8:00AM", zone));
console.log("8:00AM-10:00AM", isOpen("8:00AM", "10:00PM", zone));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.27/moment-timezone-with-data-10-year-range.min.js"></script>
You can use the Intl.DateTimeFormat constructor to do the same thing. As I understand it, you want to know if a store that is open say from 8:00 to 12:00 in say Asia/Kuala_Lumpur is currently open.
It's probably possible to convert your code more or less line by line, but I've just refactored it and simplified the logic (call me lazy…). This way it gets the current time in the desired location, converts it to minutes since midnight, then sees if that is before the start time or on or after the closing time.
For midnight (12:00 AM), the time converts to 0 minutes so if closingTime is 0, it's assumed to be end of day so is set to 1,440 (i.e. midnight at end of day).
The test times only work on the same day, if the open time runs over midnight you'll need to refactor it. I've just tested midnight to noon and noon to midnight so one should always show "open" and the other "closed".
You might also consider using Luxon, it does what moment.js + moment.tz does but uses the Intl object instead of included data.
Edit
To deal with times that go over midnight, you can either include dates in the time (not convenient if you want to use a regular daily schedule) or you can have an "inside" and "outside" test so that if closing time is before the open time, you test if the time is not between open and close times. That can be done by comparing openMin and close times and adjusting the test.
This will not deal with overlapping start and end times, but that doesn't really fit a regular daily schedule (though it might fit a weekly or longer schedule).
/* #param {string} location: IANA representative location
** #param {Date} date: date instance to get time from, default is now
** #returns {string} time in location in h:mm ap format
*/
function getTime(location, date = new Date()) {
return date.toLocaleString('en', {
timeZone: location,
hour : 'numeric',
minute: '2-digit',
dayPeriod: 'short'
});
}
/* #param {string} time: h:mm A
** #returns {number} time converted to minutes
*/
function timeToMin(time) {
let [h, m] = time.match(/\d\d?/g);
h = h%12;
if (/pm$/i.test(time)) h +=12
return h * 60 + parseInt(m);
}
/* #param {string} openTime: opening time in h:mm ap format
** #param {string} closeTime: closing time in h:mm ap format
** #param {string} location: IANA representative location
** #return {string} open if current time is within openTime and closeTime in location,
** closed otherwise
*/
function isOpen(openTime, closeTime, location) {
if (openTime == '24HR') return 'open';
let nowTime = getTime(location);
let nowMin = timeToMin(nowTime);
let openMin = timeToMin(openTime);
let closeMin = timeToMin(closeTime) || 1440;
// Open and close on same day
if (openMin < closeMin) {
return nowMin < openMin || nowMin >= closeMin ? 'closed' : 'open';
// Close on day after open
} else {
return nowMin >= openMin && nowMin < closeMin ? 'open' : 'closed';
}
}
// Time in KL
let loc = "Asia/Kuala_Lumpur";
console.log(`In ${loc} it's ${getTime(loc)}`);
// Examples
[["24HR", undefined, loc], // Open 24 hrs
["12:00AM", "12:00PM", loc], // Midnight to noon
["12:00PM", "12:00AM", loc], // Noon to midnight
["6:30PM", "04:00AM", loc], // Over midnight
].forEach(args => console.log(
`${args[0]}${args[1]? '-' + args[1] : ''} ${isOpen(...args)}`
));
I am trying to convert a UTC date to local time on my node server and finally return the localized time in the format of hh:mm:ss (not using Moment JS). I'm passing in the timezone offset from the client to Node, which is GMT-6.
My original time is: 2017-05-05T00:25:11.378Z
// ISOTimeString = `2017-05-05T00:25:11.378Z`
// offsetInMinutes = 360; (GMT - 6)
function isoDateToLocalDate(ISOTimeString, offsetInMinutes) {
var newTime = new Date(ISOTimeString);
return new Date(newTime.getTime() - (offsetInMinutes * 60000));
}
The localized time is 2017-05-04T18:25:11.378Z, which is correct (2017-05-05T00:25:11 - 6 hours = 2017-05-04T18:25:11).
// localIsoDate: 2017-05-04T18:25:11.378Z Date object
function formatTime(localIsoDate) {
var hh = localIsoDate.getHours();
var mm = localIsoDate.getMinutes();
var ss = localIsoDate.getSeconds();
return [hh, mm, ss].join(':');
}
// formatted: 12:25:11
The problem is, while still on the server, when I try to format into hh:mm:ss, it subtracts another 6 hours, giving me 12:25:11. I don't want to convert again, I simply want to format and display 18:25:11 from the already localized time.
How can I do this?
Note: Keep in mind I do not have the option to convert timezones after it's passed back to the client in my case.
The isoDateToLocalDate seems to be OK, however in the formatTime you need to use UTC methods, otherwise you are getting the host local values, not the adjusted UTC values.
Also, in ISO 8601 terms (and general convention outside computer programming), an offset of 360 represents a timezone of +0600, not -0600. See note below.
// ISOTimeString = 2017-05-05T00:25:11.378Z
// ECMAScript offsetInMinutes = 360; (GMT-0600)
function isoDateToLocalDate(ISOTimeString, offsetInMinutes) {
var newTime = new Date(ISOTimeString);
return new Date(newTime.getTime() - (offsetInMinutes * 60000));
}
// localIsoDate: 2017-05-04T18:25:11.378Z Date object
function formatTime(localIsoDate) {
function z(n){return (n<10?'0':'')+n}
var hh = localIsoDate.getUTCHours();
var mm = localIsoDate.getUTCMinutes();
var ss = localIsoDate.getUTCSeconds();
return z(hh)+':'+z(mm)+':'+z(ss);
}
var timeString = '2017-05-05T00:25:11.378Z';
var offset = 360;
console.log(formatTime(isoDateToLocalDate(timeString, offset)))
ECMAScript timezone signs are the reverse of the usual convention. If the client timezone offset is +0600 then their host will show -360.
I found many solution that gives Timezone name from offset value. But I have Timezone name and I want offset value for that. I tried setTimezone('Asia/Kolkata'), but I think their is no method like setTimezone.
example:
Asia/Kolkata should give me -330 ( offset )
This has got the be the easiest way to accomplish this task with modern JavaScript.
Note: Keep in mind that the offset is dependent on whether Daylight Savings Time (DST) is active.
/* #return A timezone offset in minutes */
const getOffset = (timeZone = 'UTC', date = new Date()) => {
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
return (tzDate.getTime() - utcDate.getTime()) / 6e4;
}
console.log(`No arguments: ${getOffset()}`); // 0
{
console.log('! Test Case #1 >> Now');
console.log(`Asia/Colombo : ${getOffset('Asia/Colombo')}`); // 330
console.log(`America/New_York : ${getOffset('America/New_York')}`); // -240
}
{
console.log('! Test Case #2 >> DST : off');
const date = new Date(2021, 0, 1);
console.log(`Asia/Colombo : ${getOffset('Asia/Colombo', date)}`); // 330
console.log(`America/New_York : ${getOffset('America/New_York', date)}`); // -300
}
{
console.log('! Test Case #3 >> DST : on');
const date = new Date(2021, 5, 1);
console.log(`Asia/Colombo : ${getOffset('Asia/Colombo', date)}`); // 330
console.log(`America/New_York : ${getOffset('America/New_York', date)}`); // -240
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
I came across this same issue, and this is the solution I came up with, if you can get an IANA tz database name like the one you mentioned:
const myTimezoneName = "Asia/Colombo";
// Generating the formatted text
// Setting the timeZoneName to longOffset will convert PDT to GMT-07:00
const options = {timeZone: myTimezoneName, timeZoneName: "longOffset"};
const dateText = Intl.DateTimeFormat([], options).format(new Date);
// Scraping the numbers we want from the text
// The default value '+0' is needed when the timezone is missing the number part. Ex. Africa/Bamako --> GMT
let timezoneString = dateText.split(" ")[1].slice(3) || '+0';
// Getting the offset
let timezoneOffset = parseInt(timezoneString.split(':')[0])*60;
// Checking for a minutes offset and adding if appropriate
if (timezoneString.includes(":")) {
timezoneOffset = timezoneOffset + parseInt(timezoneString.split(':')[1]);
}
It's not a very nice solution, but it does the job without importing anything. It relies on the output format of the Intl.DateTimeFormat being consistent, which it should be, but that's a potential caveat.
You can't get it by name alone. You would also need to know the specific time. Asia/Kolkata may be fixed to a single offset, but many time zones alternate between standard time and daylight saving time, so you can't just get the offset, you can only get an offset.
For how to do it in JavaScript, see this answer.
Using countries and timezones npm package:
import {getTimezone} from 'countries-and-timezones';
const australianTimezone = 'Australia/Melbourne';
console.log(getTimezone(australianTimezone));
Prints to the console:
{
name: 'Australia/Melbourne',
country: 'AU',
utcOffset: 600,
utcOffsetStr: '+10:00',
dstOffset: 660,
dstOffsetStr: '+11:00',
aliasOf: null
}
From there, you can use the utcOffset or dstOffset depending on if it is daylight savings time.
To create Date object in UTC, we would write
new Date(Date.UTC(2012,02,30));
Without Date.UTC, it takes the locale and creates the Date object. If I have to create a Date object for CET running the program in some part of the world, how would I do it?
You don't create a JavaScript Date object "in" any specific timezone. JavaScript Date objects always work from a milliseconds-since-the-Epoch UTC value. They have methods that apply the local timezone offset and rules (getHours as opposed to getUTCHours), but only the local timezone. You can't set the timezone the Date object uses for its "local" methods.
What you're doing with Date.UTC (correctly, other than the leading 0 on 02) is just initializing the object with the appropriate milliseconds-since-the-Epoch value for that date/time (March 30th at midnight) in UTC, whereas new Date(2012, 2, 30) would have interpreted it as March 30th at midnight local time. There is no difference in the Date object other than the datetime it was initialized with.
If you need a timezone other than local, all you can do is use the UTC version of Date's functions and apply your own offset and rules for the timezone you want to use, which is non-trivial. (The offset is trivial; the rules tend not to be.)
If you go looking, you can find Node modules that handle timezones for you. A quick search for "node timezone" just now gave me timezone as the first hit. It also gave me links to this SO question, this SO question, and this list of timezone modules for Node.
function getCETorCESTDate() {
var localDate = new Date();
var utcOffset = localDate.getTimezoneOffset();
var cetOffset = utcOffset + 60;
var cestOffset = utcOffset + 120;
var cetOffsetInMilliseconds = cetOffset * 60 * 1000;
var cestOffsetInMilliseconds = cestOffset * 60 * 1000;
var cestDateStart = new Date();
var cestDateFinish = new Date();
var localDateTime = localDate.getTime();
var cestDateStartTime;
var cestDateFinishTime;
var result;
cestDateStart.setTime(Date.parse('29 March ' + localDate.getFullYear() + ' 02:00:00 GMT+0100'));
cestDateFinish.setTime(Date.parse('25 October ' + localDate.getFullYear() + ' 03:00:00 GMT+0200'));
cestDateStartTime = cestDateStart.getTime();
cestDateFinishTime = cestDateFinish.getTime();
if(localDateTime >= cestDateStartTime && localDateTime <= cestDateFinishTime) {
result = new Date(localDateTime + cestOffsetInMilliseconds);
} else {
result = new Date(localDateTime + cetOffsetInMilliseconds);
}
return result;
}