Related
Is there a way to get a time that is local to a specified timezone in JavaScript? Basically, I'm looking for a way to say, what is the ISO time string of 2pm in New York?
I have a hack to do so, where the date is a parse-able date string, and tz is a timezone identifier, such as America/New_York.
function getDateInTZ(date, tz) {
const formatter = new Intl.DateTimeFormat([], {
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
fractionalSecondDigits: 3,
timeZone: tz,
});
const localDate = new Date(date);
const localDateAtTZ = new Date(formatter.format(localDate));
const tzOffset = localDate.getTime() - localDateAtTZ.getTime();
return new Date(localDate.getTime() + tzOffset);
}
and it has the following behavior
getDateInTz("2021-07-01 20:05", "America/Chicago").toISOString(); // 2021-07-02T01:05:00.000Z
getDateInTz(new Date("2021-12-05 20:05"), "America/Chicago").toISOString(); // 2021-12-06T02:05:00.000Z
getDateInTz("2021-12-06T02:05:00.000Z", "America/New_York").toISOString(); // 2021-12-06T02:05:00.000Z if local time is NY
getDateInTz("2021-12-06T02:05:00.000Z", "America/New_York").toISOString(); // 2021-12-06T07:05:00.000Z if local time is UTC
While the above solution works in Chrome, it doesn't work on Firefox because FF is unable to do Date.parse on the output of formatter.format(). Which leads me to think that it's not a correct solution.
Has anyone run into this requirement before, and have a good solution for it?
As far as I know this is not possible without the help of a library like luxon or day.js
In luxon this would be the way to go
let local = luxon.DateTime.local(); // 2021-10-31T20:26:15.093+01:00
let localInCT = local.setZone("America/Chicago"); //2021-10-31T14:26:15.093-05:00
Have a look at this project using these methods
We're limited by the JavaScript native Date object here.
The internal state of a Date object is the number of milliseconds that have elapsed since 1970-01-01 00:00 UTC, and no timezone is stored in the Date object.
So, we can create Dates somewhat equivalent to ones created in another timezone, but they won't behave exactly the same:
The timezone offset for Date.toString() will show the local client timezone (e.g. GMT+0000) rather than the offset in the desired timezone.
DST rules may not work as expected. We can get the equivalent date for say America/New_York, but DST transitions will obey the local timezone rules and not the New York rules.
Having said that, a variant of the approach you're using will give what I would call equivalent dates in the desired timezone.
How we do this:
First use Date.toLocaleString() to format an ISO timestamp in the desired timezone. We use a hack to do this, passing the 'sv' locale to the function. This will create an ISO timestamp, e.g. yyyy-MM-dd HH:mm:ss.
Pass this timestamp to the Date() constructor.
TLDR: This can't really be done. But for some contexts and use cases we can approximate the desired behaviour. I would recommend using a library like luxon for this purpose.
Example below:
function getDateInTimezone(date, timeZone) {
// Using a locale of 'sv' formats as an ISO date, e.g. yyyy-MM-dd HH:mm.
const timeInTimeZone = date.toLocaleString('sv', { timeZone } );
// Pass this to the Date constructor
return new Date(timeInTimeZone);
}
const localTime = new Date();
const timeZoneList = ['Asia/Tokyo', 'Europe/Berlin','America/Los_Angeles'];
console.log(`Local Time: ${localTime.toLocaleTimeString()}`);
for(let timeZone of timeZoneList) {
const dt = getDateInTimezone(localTime, timeZone);
console.log(`Time (${timeZone}): ${dt.toLocaleTimeString()}`);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
Timezones in luxon are a lot easier to handle, also when we call .toString() on a Luxon DateTime, we get the correct UTC offset.
const { DateTime } = luxon;
const localTime = DateTime.now();
const timeZoneList = ['Asia/Tokyo', 'Europe/Berlin','America/Los_Angeles'];
console.log(`Local Time: ${localTime.toFormat('HH:mm:ssZZ')}`);
for(let zone of timeZoneList) {
const dt = DateTime.fromMillis(Date.now(), { zone });
console.log(`Time (${zone}): ${dt.toFormat(' HH:mm:ssZZ')}`);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.0.2/luxon.min.js" integrity="sha512-frUCURIeB0OKMPgmDEwT3rC4NH2a4gn06N3Iw6T1z0WfrQZd7gNfJFbHrNsZP38PVXOp6nUiFtBqVvmCj+ARhw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
You can use something like this:
const actualDate = new Date()
actualDate.toLocaleString('en-US', { timeZone: 'America/New_York' })
Does this solve your question?
function getDateInTZ(date, tz) {
return new Date(date).toLocaleString('en-US', { timeZone: tz })
}
console.log(getDateInTZ("2021-07-01 20:05", "Asia/Kolkata"))
console.log(getDateInTZ("2021-07-01 20:05", "America/Chicago"))
console.log(getDateInTZ("2021-12-06T02:05:00.000Z", "America/New_York"))
let date = new Date();
let time = date.toLocaleTimeString([], { hour12: true })
if we are using hour12 true it will return the time in 12h format else it will return the time in 24h format
make sure we are using TZ on .env file it always been a good practice and helps to erase all the extra code.
you can simply use
TZ=America/New_York
I want to get the current UTC date in JavaScript, but display it in the local date format (like Date.toLocaleDateString() does).
I first tried to just get the current UTC Date with Date.toUTCString() but that doesn't actually print out in the local format.
I then tried using the options configuration in toLocaleDateString(), but that just printed the local date and not the UTC date in the local format.
e.g. new Date().toLocaleDateString(options = {timeZone: "UTC"})
I then tried formatting using Intl.DateTimeFormat(), but that just gives the same results as Date.toLocaleDateString() does.
If there was a way to get the locale format then I'd be happy to use that format to format the UTC Date, but as far as I can tell there is none.
For example, given the new Date("Sat, 30 Mar 2019 00:27:19 GMT"), In the US, I should print out "3/30/2019", in Europe I should print out "30/3/2019", and so on, for every supported locale.
However, new Date("Sat, 30 Mar 2019 00:27:19 GMT").toLocaleDateString(options = {timeZone: "UTC"}) will print out "3/29/2019" instead.
I also wanted to display a date using localized string settings, like toLocaleDateString() does, but using the date's UTC value, instead of the local time zone.
For example:
I do want the localized string format, but I also want the UTC value instead of the local time zone value. The desired output in this example would be 4/3/2019, instead of 4/2/2019.
I acknowledged #AndersonHappens suggestion, but I have not used it. Here is what I did, instead:
There is a getTimezoneOffset() function, which provides the local time zone offset.
We can use this function result and create a new Date, applying the diff. Then, we can use toLocaleDateString() directly, as usual, to get the date in localized string format:
The solution as a function could be like this:
function toLocaleUTCDateString(date, locales, options) {
const timeDiff = date.getTimezoneOffset() * 60000;
const adjustedDate = new Date(date.valueOf() + timeDiff);
return adjustedDate.toLocaleDateString(locales, options);
}
Given a date, you can get the locale format with new Intl.DateTimeFormat().
You can then use formatToParts in order to get the formatting of the date and each specific component.
Following the example in the formatToParts documentation, I created the following method to get the UTC date in the locale string.
function toLocaleUTCDateString(date) {
let formatter = new Intl.DateTimeFormat();
return formatter.formatToParts(date).map(({type, value}) => {
switch (type) {
case 'day': return date.getUTCDate();
case 'hour': return date.getUTCHours();
case 'minute': return date.getUTCMinutes();
case 'month': return date.getUTCMonth() + 1;
case 'second': return date.getUTCSeconds();
case 'timeZoneName': return "UTC";
case 'year': return date.getUTCFullYear();
default : return value;
}
}).reduce((string, part) => string + part);
}
Do note however that this method does not remember number versus string formatting. For example, if you want to display the month of March as "March" (or the specific language month string), and not as the number 3, you would have to figure out how to do that yourself. It also doesn't handle discrepancies in weekdays, so if the local date is "Friday" and the UTC Date is "Saturday", you would have to figure that out separately as well.
You almost got it right - the timeZone option is available in the second argument. The first argument to toLocaleDateString is for the locales.
This example from your question works when options are the second argument (and is much simpler than other answers):
const usLocale = 'en-US'
new Date('Sat, 30 Mar 2019 00:27:19 GMT').toLocaleDateString(usLocale, {
timeZone: 'UTC',
})
// '3/30/2019'
const ukLocale = 'en-GB'
new Date('Sat, 30 Mar 2019 00:27:19 GMT').toLocaleDateString(ukLocale, {
timeZone: 'UTC',
})
// '30/03/2019'
var dateToPrint = new Date(Date.UTC(2020, 3, 23, 15, 0, 0));
new Date(
dateToPrint.getUTCFullYear(),
dateToPrint.getUTCMonth(),
dateToPrint.getUTCDate(),
dateToPrint.getUTCHours(),
dateToPrint.getUTCMinutes(),
dateToPrint.getUTCSeconds()
).toLocaleString('es-ES')
see image
Inside the toLocaleDateString you can pass options. There is an option name TimeZone. You have to set it to 'UTC'
const dateToParse = new Date()
dateToParse.toLocaleDateString(locate, { timeZone: 'UTC' })
Check out Intl.DateTimeFormat on MDN!
Using it is as simple as
// do this once in a central library file
const formatter = new Intl.DateTimeFormat('en', {
timeZone: 'UTC',
timeZoneName: 'short',
month: 'short',
day: '2-digit',
hour12: false,
hour: '2-digit',
minute: '2-digit',
})
// reuse anywhere for consistency
const myDate = new Date(
'Thu Feb 10 2022 12:50:14 GMT+0100'
)
formatter.format(myDate) === 'Feb 10, 11:50 UTC' // true
Open your console, copy-paste the code and start playing!
Again, Intl.DateTimeFormat on MDN.
Enjoy!
toLocaleString receives some parameters, the first parameter is the locale. To solve your issue specifically, you need to understand that UTC is not a locale. What you could to to accomplish what you want is to pass either 'en-US' for 3/29/2019 and 'en-GB' for 29/03/2019 hope that helps!
I have a list of list of string dates like this: '17/12/2017 19:34'. They are CET dates.
How can I transform it to the user's browser date?
I'm doing this:
const tzGuess = moment.tz.guess()
export const toTimeZone = (time) => {
const format = 'DD/MM/YYYY HH:mm'
return moment(time, format).tz(tzGuess).format(format)
}
console.log(toTimeZone('17/12/2017 19:34', tzGuess))
but how can I say to moment that the date I'm passing at first is a CET one?
Thanks!
You can use moment.tz function for parsing time string using a given timezone (e.g. 'Europe/Madrid').
The issue is: what do you mean with CET? If your input has fixed UTC+1 offset (like Central European Time), then you can use RobG's solution. If you have to consider both CET and CEST, I think that the best soution is to use moment.tz.
Here a live code sample:
const tzGuess = moment.tz.guess()
const toTimeZone = (time) => {
const format = 'DD/MM/YYYY HH:mm'
return moment.tz(time, format, 'Europe/Madrid').tz(tzGuess).format(format)
}
console.log(toTimeZone('17/12/2017 19:34', tzGuess))
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.4/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.13/moment-timezone-with-data-2012-2022.min.js"></script>
A great resource about timezone is the timezone tag info page.
Without moment.js, parse the string to a Date, treating it as UTC, then adjust for the CET offset (+0100). You can then format it using local time values for the client:
// Parse date in format DD/MM/YYYY HH:mm
// Adjust for CET timezone
function parseCET(s) {
var b = s.split(/\D/);
// Subtract 1 from month and hour
var d = new Date(Date.UTC(b[2], b[1]-1, b[0], b[3]-1, b[4]));
return d;
}
var s = '17/12/2017 19:34';
console.log(parseCET(s).toString());
However, if the time needs to observe daylight saving (CEST) for the source time stamp, you'll need to account for that.
I have two variables in javascript like:
time: 02:00
date: 25-08-2017
and I'm wondering if I can put these into a Date() object and get the UTC date and time out of it in hours/minutes using getUTCDay(), getUTCHours() and getUTCMinutes() and place them back in the format I got them.
I can load moment.js for it but if I don't have to it would be nice if I can do it without.
Convert your strings to an ISO 8601 string and use it in the Date constructor. From there, you can get whatever data you need
let time = '02:00'
let date = '25-08-2017'
let iso8601Date = date.split('-').reverse().join('-')
let dt = new Date(`${iso8601Date}T${time}`) // will be interpreted as local time
const digitFormatter = new Intl.NumberFormat(undefined, {minimumIntegerDigits: 2})
console.info('Parsed date:', dt)
console.info('UTC date in dd-mm-yyyy:',
`${digitFormatter.format(dt.getUTCDate())}-${(digitFormatter.format(dt.getUTCMonth() + 1))}-${dt.getUTCFullYear()}`
)
console.info('UTC time:', `${digitFormatter.format(dt.getUTCHours())}:${digitFormatter.format(dt.getUTCMinutes())}`)
I am struggling to find out the beginning of day factoring in timezones in javascript. Consider the following:
var raw_time = new Date(this.created_at);
var offset_time = new Date(raw_hour.getTime() + time_zone_offset_in_ms);
// This resets timezone to server timezone
var offset_day = new Date(offset_time.setHours(0,0,0,0))
// always returns 2011-12-08 05:00:00 UTC, no matter what the offset was!
// This has the same issue:
var another_approach_offset_day = new Date(offset_time.getFullYear(),offset_time.getMonth(),offset_time.getHours())
I expect when i pass a Pacific Timezone offset, to get: 2011-12-08 08:00:00 UTC and so on.
What is the correct way to achieve this?
I think that part of the issue is that setHours method sets the hour (from 0 to 23), according to local time.
Also note that I am using javascript embedded in mongo, so I am unable to use any additional libraries.
Thanks!
Jeez, so this was really hard for me, but here is the final solution that I came up with the following solution. The trick was I need to use setHours or SetUTCHours to get the beginning of a day -- the only choices I have are system time and UTC. So I get the beginning of a UTC day, then add back the offset!
// Goal is given a time and a timezone, find the beginning of day
function(timestamp,selected_timezone_offset) {
var raw_time = new Date(timestamp)
var offset_time = new Date(raw_time.getTime() + selected_timezone_offset);
offset_time.setUTCHours(0,0,0,0);
var beginning_of_day = new Date(offset_time.getTime() - selected_timezone_offset);
return beginning_of_day;
}
In JavaScript all dates are stored as UTC. That is, the serial number returned by date.valueOf() is the number of milliseconds since 1970-01-01 00:00:00 UTC. But, when you examine a date via .toString() or .getHours(), etc., you get the value in local time. That is, the local time of the system running the script. You can get the value in UTC with methods like .toUTCString() or .getUTCHours(), etc.
So, you can't get a date in an arbitrary timezone, it's all UTC (or local). But, of course, you can get a string representation of a date in whatever timezone you like if you know the UTC offset. The easiest way would be to subtract the UTC offset from the date and call .getUTCHours() or .toUTCString() or whatever you need:
var d = new Date();
d.setMinutes(d.getMinutes() - 480); // get pacific standard time
d.toUTCString(); // returns "Fri, 9 Dec 2011 12:56:53 UTC"
Of course, you'll need to ignore that "UTC" at the end if you use .toUTCString(). You could just go:
d.toUTCString().replace(/UTC$/, "PST");
Edit: Don't worry about when timezones overlap date boundaries. If you pass setHours() a negative number, it will subtract those hours from midnight yesterday. Eg:
var d = new Date(2011, 11, 10, 15); // d represents Dec 10, 2011 at 3pm local time
d.setHours(-1); // d represents Dec 9, 2011 at 11pm local time
d.setHours(-24); // d represents Dec 8, 2011 at 12am local time
d.setHours(52); // d represents Dec 10, 2011 at 4am local time
Where does the time_zone_offset_in_ms variable you use come from? Perhaps it is unreliable, and you should be using Date's getTimezoneOffset() method. There is an example at the following URL:
http://www.w3schools.com/jsref/jsref_getTimezoneOffset.asp
If you know the date from a different date string you can do the following:
var currentDate = new Date(this.$picker.data('date'));
var today = new Date();
today.setHours(0, -currentDate.getTimezoneOffset(), 0, 0);
(based on the codebase for a project I did)
var aDate = new Date();
var startOfTheDay = new Date(aDate.getTime() - aDate.getTime() % 86400000)
Will create the beginning of the day, of the day in question
You can make use of Intl.DateTimeFormat. This is also how luxon handles timezones.
The code below can convert any date with any timezone to its beginging/end of the time.
const beginingOfDay = (options = {}) => {
const { date = new Date(), timeZone } = options;
const parts = Intl.DateTimeFormat("en-US", {
timeZone,
hourCycle: "h23",
hour: "numeric",
minute: "numeric",
second: "numeric",
}).formatToParts(date);
const hour = parseInt(parts.find((i) => i.type === "hour").value);
const minute = parseInt(parts.find((i) => i.type === "minute").value);
const second = parseInt(parts.find((i) => i.type === "second").value);
return new Date(
1000 *
Math.floor(
(date - hour * 3600000 - minute * 60000 - second * 1000) / 1000
)
);
};
const endOfDay = (...args) =>
new Date(beginingOfDay(...args).getTime() + 86399999);
const beginingOfYear = () => {};
console.log(beginingOfDay({ timeZone: "GMT" }));
console.log(endOfDay({ timeZone: "GMT" }));
console.log(beginingOfDay({ timeZone: "Asia/Tokyo" }));
console.log(endOfDay({ timeZone: "Asia/Tokyo" }));