I have a time string like this 2021-12-29T01:30:00.105Z. I want to add offset for a timezone without changing the date and time. So it should look like this after conversion - 2021-12-29T01:30:00+08:00 for "Asia/Kuala_Lumpur" timezone.
So 2021-12-29T01:30:00.105Z -> 2021-12-29T01:30:00+08:00
I cannot do a string replace since the timezone will be dynamic and does not always has to be "Asia/Kuala_Lumpur".
I am using moment and moment-timezone libraries and have tried different ways to get the required result but none seems to work.
Please help.
If you have the IANA name for the timezone and you want to change the timezone of a date in +XX:XX format without modifying the time, you can calculate the offset and replace it in the string:
const timeString = '2021-12-29T01:30:00.105Z';
const timezone = 'Asia/Kuala_Lumpur';
const offset = Intl.DateTimeFormat([], {timeZone: timezone, timeZoneName: 'longOffset'}).formatToParts().find(o => o.type === 'timeZoneName').value.match(/\+.*$/)[0];
const newTimeString = timeString.replace(/\..+Z$/, offset);
console.log(timeString);
console.log(newTimeString);
Related
I'm quite new (and confused) with time in JavaScript..
I currently have time data to work with, and they are in the format of DD-MMM-YYYY, meaning it would be 23-Feb-2021. This time is already in its own timezone, GMT-10. I'm trying to initialize it as GMT-10 so that I could get its appropriate epoch time.
I've done this:
date = new Date("23-Feb-2021") // This results in 2021-02-23T00:00:00.000Z
But what I'm trying to achieve is to get the time to be 2021-02-23T10:00:00.000Z, which I could then do a getTime() to get its epoch in ms. I understand I could probably hard code to +10 to the time I have, but the data I work with might vary so I'd like to figure a way to initialize the date with a specific timezone.
EDIT:
Here's an example of an outcome I'd want:
date = ("23-Feb-2021")
date = moment(date).format(); // 2021-02-23T00:00:00+00:00
date = date.replace("+00","+10");
date = new Date(msg.date); // 2021-02-22T14:00:00.000Z
date = date.getTime(); // 1614002400000 (2021-02-22T14:00:00.000Z)
In the end, 2021-02-22T14:00:00.000Z is what I'm trying to get, without having to iterate it a bunch of times like above and adding +10
You can add the timezone offset to your input string, and use an explicit string format to parse it:
let date = "23-Feb-2021"
date = moment(date + "-10:00", "D-MMM-YYYYZ")
console.log(date.format()) // 2021-02-23T11:00:00+01:00 (if local is GMT+1)
console.log(date.utc().format()) // 2021-02-23T10:00:00+00:00
It seems you're already using moment.js, so add moment–timezone so you can parse timestamps in whatever IANA timezone you want. You can either choose a location with the offset rules you want (e.g. Pacific/Honolulu or Pacific/Tahiti for -10) or just a fixed offset like etc/GMT+10.
You can then format the value in any timezone, as UTC, or as a time value, e.g.
// Timestamp
let d = "23-Feb-2021";
// Parse in specific IANA timezone
let m = moment.tz(d, 'D-MMM-YYYY', 'Pacific/Honolulu');
// Trigger UTC mode
m.utc()
// Show result
console.log(m.format())
// Get time value (ms since epoch)
console.log(m.valueOf());
// Parse using generic timezone/fixed offset
let g = moment.tz(d, 'D-MMM-YYYY', 'etc/GMT+10');
console.log(g.utc().format());
// Display timestamp for another timezone
console.log(moment.tz(g, 'Asia/Riyadh').format());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data-10-year-range.js"></script>
Note that for fixed offset timezones like etc/GMT+10, the sign is the opposite of the common offset (e.g. etc/GMT+10 is UTC-10) to be consistent with POSIX notation. However, moment.tz only has limited POSIX support in that it only recognises one hour offsets, not the full POSIX timezone notation, so you can't do say "etc/GMT-530" instead of Asia/Kolkata.
I am using date-fns to format dates
If I pass a date that ends in Z, it knows that it is UTC, and I can format(date, "yyyy-MM-dd") and it will be in local computer time.
If the date I want to express in local computer time is originally CST, is there something to add at the end instead of the Z, that will be understood by the format function as a CST date?
Sorry if this is a bad question
Edit: is there a way to do zonedTimeToUtc(myDate, 'UTC-6') in date-fns? (instead of using a time zone name)
If you have a string that you always want parsed as CST (US central standard time) using date-fns, you can include date-fns-tz and set the timezone when parsing (I've assumed an ISO 8601 loose format without the timezone). Note that to avoid DST, you have to pick a location that is UTC-6 all year round, e.g. Canada/Saskatchewan.
// Setup
var {parse} = require('date-fns');
var {zonedTimeToUtc, utcToZonedTime, format } = require('date-fns-tz');
// Parse using location for offset
let loc = 'Canada/Saskatchewan';
let s = '2020-08-14 13:05:52';
let fIn = 'yyyy-MM-dd HH:mm:ss';
let utcDate = zonedTimeToUtc(s, loc);
// Show local equivalent
console.log(utcDate);
This leaves you somewhat at the mercy of the administrators of Saskatchewan, who might change the offset or introduce DST. An alternative is to append the exact offset you want to the timestamp and include it in the parse tokens:
// Parse using string for offset
let tz = '-06';
let utcDate2 = parse(s + ' ' + tz, fIn + ' X', new Date());
// Show local equivalent, should be same as above
console.log(utcDate2);
The advantage of the second method is that it doesn't require date-fns-tz and you aren't beholden to historic or future changes to Saskatchewan's offset (or that of any other IANA location).
Apparently there is a UTC module in development that will allow setting specific offsets like -6 rather than using IANA locations (can't find a link to that comment atm).
At this point the string has been parsed as GMT-6, but is still just a plain Date (i.e. just a time value with no idea of the timezone that was associated with the original string).
Once you have the date you can then show it as CST for output. To use an IANA location for the offset in call to format, you have to use format from date-fns-tz, not plain date-fns, otherwise it will just use the host system offset.
Note that the value in the format call is just setting the value to use for the offset string, it doesn't do anything to the actual date and time, that adjustment has already been applied by utcToZonedTime.
// Adjust to CST
let dCST = utcToZonedTime(utcDate2, loc);
// Format strings:
let fOut1 = 'yyyy-MM-dd HH:mm:ss XXX'; // -0600
let fOut2 = 'yyyy-MM-dd HH:mm:ss z'; // CST
// Format using location
console.log(format(dCST, fOut1, {timeZone: loc}));
console.log(format(dCST, fOut2, {timeZone: loc}));
I prefer the -0600 version as it avoids questions of whether DST is observed or not (and is really what the code is doing). Also, in the "z" version you might get the offset or the timezone name (probably depending on the host default language and location, which is a quirk of date-fns-tz using Intl.DateTimeFormat I think).
You can also manually add the timezone using a format string like:
let fOut = 'yyyy-MM-dd HH:mm:ss \'-0600\'';
which will produce an output like:
"2020-08-14 13:05:52 GMT-0600"
I don't think there is any way to set a specific offset like "-0600" for both parsing and formatting without including it in the call. I think moment.js and luxon allow it.
For completeness, here's some code you can run at npm.runkit.com since there's no CDN for the current date-fns version to allow the code to run here.
var {parse} = require('date-fns');
var {zonedTimeToUtc, utcToZonedTime, format } = require('date-fns-tz');
// Parse using location for offset
let loc = 'Canada/Saskatchewan';
let s = '2020-08-14 13:05:52';
let fIn = 'yyyy-MM-dd HH:mm:ss';
let utcDate = zonedTimeToUtc(s, loc);
// Show local equivalent
console.log(utcDate);
// Parse using string for offset
let tz = '-06';
let utcDate2 = parse(s + ' ' + tz, fIn + ' X', new Date());
// Show local equivalent, should be same as above
console.log(utcDate2);
// Format using location:
let fOut1 = 'yyyy-MM-dd HH:mm:ss XXX'; // -0600
let fOut2 = 'yyyy-MM-dd HH:mm:ss z'; // CST
let dCST = utcToZonedTime(utcDate2, loc);
console.log(format(dCST, fOut1, {timeZone: loc}));
console.log(format(dCST, fOut2, {timeZone: loc}));
Try using moment libraries to solve your time problems: moment.js, and its complement moment-timezone.js
To output the current time converted to CST timezone:
moment().tz('America/Chicago').format('hh:mm:ss z')
06:43:34 CST
moment().tz('America/Chicago').format('hh:mm:ss z Z')
06:43:35 CST -06:00
moment().tz('America/Chicago').format()
2020-08-13T15:52:09-06:00
Or maybe use a function as below:
const calcTime = (cityOffset) => {
var now = new Date();
// convert to msec and add local time zone offset and get UTC time in msec
var utc = now.getTime() + (now.getTimezoneOffset() * 60000);
// create new Date object for different city using supplied offset
var newTime = new Date(utc + (3600000 * cityOffset));
return newTime.toLocaleString();
}
I have total 4 different input i.e.:
Date string (2020-05-05)
Time string (15:30)
Timezone offset (-09:00)
I want to combine these strings into one datetime object like (2020-05-05T15:30:00-09:00) no-matter what my local browser timezone is. The issue is when I combine these strings and I try to make date object using new Date() function, my datetime gets converted into UTC timestamp.
I tried this:
const date =
moment(this.actualDateOfSurgeryDate).format(YYYYMMDD) +
'T' +
moment(this.actualDateOfSurgeryTimeDropDown + ' ' + this.actualDateOfSurgeryTimeAM_PMDropDown, ['h:mm A']).format('HH:mm:ss') +
offsetTime;
this.caseDetail.actualDateOfSurgery = new Date(date);
This gives me output something like: 2020-05-05T04:30:00.000Z
How can I get my desired output: 2020-05-05T15:30:00-09:00 ??
I have moment js available in my project
I want to combine these strings into one datetime object like (2020-05-05T15:30:00-09:00)
Date objects are extremely simple, they're just a time value that is an offset in milliseconds since 1970-01-01T00:00:00Z, so are inherently UTC. The built–in parser is unreliable and lacks any functionality such as format tokens.
So if you have separate values like:
Date string (2020-05-05)
Time string (15:30)
Timezone offset (-09:00)
then you can create a string that is compliant with the format defined in ECMA-262 and that should be parsed correctly by the built–in parser, e.g.
new Date('2020-05-05T15:30:00.000-09:00')
However, general advice is to avoid the built–in parser due to differences in implementations. Also, the format must be exact (e.g. including seconds and milliseconds in the timestamp, colon (:) in the offset) or some implementations will reject it as malformed and return an invalid date.
Once you have a Date object, getting a "local" timestamp with offset is an issue of formatting, which has been answered many times before (e.g. How to format a JavaScript date). There aren't any decent built–in formatting functions (toLocaleString with options is OK for some purposes but generally lacking in functionality), so you'll have to either write your own function, or use a library.
The following examples use Luxon, which is suggested as the upgrade path from moment.js.
With Luxon, if you specify a representative location, you'll get the offset for that location at the date's date and time. Alternatively, you can fix the offset to a set value, essentially setting it for a timezone without a representative location, so it doesn't have any reference to daylight saving or historic offset changes:
let DateTime = luxon.DateTime;
// Offset per the specified location
let d0 = DateTime.fromISO('2020-01-01', {zone: 'America/Yakutat'});
let d1 = DateTime.fromISO('2020-06-30', {zone: 'America/Yakutat'});
console.log(d0.toString());
console.log(d1.toString());
// Fixed offset per the supplied string
let d2 = DateTime.fromISO('2020-05-05T15:30:00.000-09:00', { setZone: true});
let d3 = DateTime.fromISO('2020-01-01T15:30:00.000-09:00', { setZone: true});
console.log(d2.toString());
console.log(d3.toString());
<script src="https://cdn.jsdelivr.net/npm/luxon#1.24.1/build/global/luxon.min.js"></script>
I get 16:30 due to DST
A date before March or after October will give 15:30
let dateString = "2020-05-05"+"T"+"15:30"+":00"+"-09:00"
console.log(dateString)
const date = new Date(dateString)
console.log(date)
const Anchorage = date.toLocaleString('en-US', {timeZone: 'America/Anchorage', hour12: false})
console.log(Anchorage)
let options = {}
options.timeZone = 'America/Anchorage';
options.timeZoneName = 'short';
console.log(date.toLocaleDateString('en-US'), date.toLocaleTimeString('en-US', options));
I know I can get the local timezone offset via new Date().getTimeZoneOffset(). But where did Javascript get that information? Is there a way I can set it, so that all future Date objects have the offset I want? I tried searching the DOM in Firebug, but couldn't find anything.
What I am trying to accomplish is converting epoch times to readable format, but it needs to be in US/Central, no matter what the browser's OS setting. Because I am using US/Central, it's not a fixed difference from GMT. So instead of a bunch of super nasty conversion steps, why can't I just tell Javascript that I'm actually in US/Central?
Currently, Moment-Timezone enables us to set the "browser's" default timezone by using moment.tz.setDefault().
You'll have to use moment() instead of Date(), but this is still a nice upgrade over the weird JS Date object.
I know I can get the local timezone offset via new Date().getTimeZoneOffset(). But where did Javascript get that information?
An implementation of ECMAScript is expected to determine the local time zone adjustment.
Is there a way I can set it, so that all future Date objects have the offset I want?
No.
So instead of a bunch of super nasty conversion steps, why can't I just tell Javascript that I'm actually in US/Central?
Have you considered using a library?
I realize this is an old post, but momentJS is a powerful javascript library to manipulate date/time objects
Output format
If you are concerned about the output format, you always need to format you Date object prior to outputting it if you need it in a local timezone (e.g. using Intl) or you a library like dayjs or moment.
Create a new Date object from a date with a non-UTC timezone
You can set an offset in pure JS: new Date('2022-10-29T12:50:00.000+02:00') will contain 2022-10-29T10:50:00.000Z. You just have to always specify the timezone offset in /^[+-][0-2]\d:[0-5]\d$/ format.
console.log(new Date('2022-10-29T12:50:00.000+02:00').toISOString())
// Output
// 2022-10-29T10:50:00.000Z
Get timezone offset string from timezone offset number
Now, if you want to get an offset in that format from (new Date()).getTimezoneOffset() (e.g. -120), you need to
const tzOffsetNumber = (new Date()).getTimezoneOffset()
const tzOffsetString = `${tzOffsetNumber > 0 ? '+' : '-'}${Math.floor(Math.abs(tzOffsetNumber) / 60).toString().padStart(2, '0')}:${(Math.abs(tzOffsetNumber) % 60).toString().padStart(2, '0')}`
Get timezone offset string from IANA timezone
// Note: We need to specify a date in order to also consider DST settings.
const date = new Date()
const ianaTimezone = 'Europe/Bratislava'
const tzOffsetString = new Intl.DateTimeFormat('en', {timeZone: ianaTimezone, timeZoneName: 'longOffset'}).format(date).match(/[\d+:-]+$/)?.[0]
I'm wondering on the correct way to convert a string date in a non-ISO format to a different offset/timezone.
I am currently given 3 values:
the date in format MM/DD/YYYY (23/11/2016)
the time in 24h format (23:13)
timezone offset (-07:00)
I would like to convert said date to the user's timezone.
I am trying to convert the format to the format accepted by moment timezone's moment.tz() function ('2016-11-23T23:13-07:00') but I am not sure how to do that without splitting the date array and converting it to said date.
Moment's timezone has the tools I need to convert the date afterwards to the local timezone. For example:
moment.tz('2016-11-23T23:13-07:00', moment.tz.guess());
Any thoughts on how to convert 23/11/2016 23:13 with offset -07:00 to the local date preferably using momentJS?
Why not just format as an ISO 8601 string with offset and give that to moment.js?
function customToISOString(date, time, offset){
return date.split(/\D/).reverse().join('-') + 'T' + time + offset;
}
document.write(customToISOString('23/11/2016','23:13','-07:00')); // 2016-11-23T23:13-07:00
Most modern browsers will also parse that, but don't do it as there are still plenty of older browsers around where it will fail.
I like Rob's answer, but I'll also give you it in moment.js.
First, you don't need moment-timezone, and you definitely don't need to guess the time zone id just to convert to that zone. In ISO format, it would just be like this:
var m = moment('2016-11-23T23:13-07:00');
This will read in the offset during parsing, apply it, then convert to the local time zone, returning a moment object in "local mode". This is the default mode, so it just works.
With the requirements you described it would be like this:
// your inputs
var d = "23/11/2016";
var t = "23:13";
var o = "-07:00";
var m = moment(d + ' ' + t + o, 'MM/DD/YYYY HH:mmZ');
Note that I add the space between the date and time just for safety, so there's no risk of mixing the year and the hour components.
Again it will automatically apply the offset and convert to the local time zone, since that's the default behavior. If you want some other behavior, there are ways to do that as well.