Moment.js - Anywhere in the world, use the same Timezone - javascript

I found a couple other posts but they didn't have something specific to what I was looking for.
Here's the scenario:
A user in China (although it could be anywhere in the world) inputs a date time into a field that represents a local time in the U.S. So even though their local machine might be 1:11 AM 02/15/2018 in Beijing, the user is entering a date in Austin, TX for 11:11 AM 02/14/2018 into the date time field.
This is the string I'm pulling from the input field:
'2018-02-14T11:11'
How can I use moment.js to make sure that when I convert '2018-02-14T11:11' to UTC, the UTC string always reflects Austin time, not Beijing time? At the end of the day, we won't know which timezone the user is from, but we will always know the entered timezone will be in Central Standard Time.
What seems to be happening with the below is that when I use these to convert to UTC, the dates are still not reflecting CST, or they are offset incorrectly by several hours.
moment('2018-02-14T11:11').zone("America/Chicago").format()
moment('2018-02-14T11:11').tz("America/Chicago").format()
moment('2018-02-14T11:11', 'CST').tz("America/Chicago").format()
moment('2018-02-14T11:11', "America/Chicago").tz("America/Chicago").format()
To UTC:
moment.utc('2018-02-14T11:11').tz("America/Chicago").toISOString()
I'm definitely missing something. Any advice would be appreciated!
Thanks in advance!

You're close - you will need the Moment-Timezone addon, and then it's like this:
moment.tz('2018-02-14T11:11', "America/Chicago").utc().format()
Let's break that down:
// create the moment with a specific time zone
var m = moment.tz('2018-02-14T11:11', "America/Chicago");
// convert to UTC
m.utc();
// format the output
var s = m.format();

Just add another scenario that I got:
Parse '2018-02-14Z', but ignore 'Z' (UTC), just use local time zone or a specified timezone.
You can simply remove 'Z' from the string or do these:
moment('2018-02-14Z', 'YYYY-MM-DD') will ignore 'Z' or any unmatched characters.
moment.tz('2018-02-14Z', 'YYYY-MM-DD', 'America/Chicago') will use the given timezone.
Of course, you should not do this in the first place. A correct timezone should be used.

Related

How to make Moment.js ignore the user's timezone?

I've got a form where I input an event that starts at a certain time. Let's say 9am.
To assign a date/time object I'm using MomentJs. The issue comes when displaying it in different time-zones.
In London will show up 9am as intended - in Kiev will show 11am.
How can I make MomentJS and the browser ignore which timezone is relevant for the user, and just displaying the time I'm giving?
Here's my code:
<p>
Start time:
{moment(event.startDate).format("HH:mm")}
</p>
Assuming you have stored the date as utc (which in this case you probably should have), you could use the following:
moment.utc(event.startDate).format("HH:mm")
Let me provide an alternative answer in Vanilla JavaScript. If you want to make it timezone 'neutral', you can first convert it to UTC using toISOString().
const current = new Date();
const utcCurrent = current.toISOString();
console.log(utcCurrent);
If you want to convert it to a specific timezone, such as London, you can use toLocaleString(). Do take note of the browser support for the timezone though.
const londonTime = new Date().toLocaleString('en-US', { timeZone: 'Europe/London' })
console.log(londonTime);
What you want is a normalized Datetime. This can get a little confusing since the concept of timezones is a rather arbitrary construct.
I like to think of Datetime values as "absolute" and "relative". An "absolute" Datetime is one that is true regardless of which timezone you're in. The most common example of these are UTC(+000) and UNIX Time (also known as Unix epoch, POSIX Time or Unix Timestampe).
UTC is pretty obvious. Its the current time at +000 timezone. UNIX time is a bit more interesting. It represents the number of seconds that have elapsed since January 1, 1970.
You should always store data, in both client and backend, as an "absolute" time. My preference is UNIX time since its represented as a single integer (nice and clean).
moment.js does this for you. When you instantiate your moment object, you can use:
var date = moment.utc(utcString)
or for Unix Time
var date = moment.unix(unixInt)
You can then use this object to display the date in any form you wish:
console.log(date.tz.("America/Toronto"))
The only way I could solve this is by removing the timezone and milliseconds info from the string. I used date-fns lib but I imagine moment will work the same way.
import { format } from 'date-fns'
const myDateTimeString = '2022-02-22T19:55:00.000+01:00'
const dateTimeWithoutTimezone = myDateTimeString.slice(0, 16) // <- 2022-02-22T19:55
format(new Date(dateTimeWithoutTimezone), 'HH:mm')

How to guess user's timezone using date-fns?

I have a requirement, where I need to show a user's time in their local time zone. This needs to be done using date-fns.
The following code checks the current time and then given a timezone converts it into local time as per the time zone.
const { formatToTimeZone } = require('date-fns-timezone')
let date = new Date()
const format = 'D.M.YYYY HH:mm:ss [GMT]Z (z)'
const output = formatToTimeZone(date, format, { timeZone: 'Asia/Calcutta' })
However, how do I guess the user's timezone on the fly?
In moment.js, you can get it with moment.tz.guess(). But how can I do it without moment.js and by using date-fns?
https://runkit.com/embed/835tsn9a87so
UPDATE: I will be using this inside a VueJS application. So, if there are any other related solutions, those are welcomed as well. Thanks.
To get the user's IANA time zone identifier, most modern browsers now support the following:
Intl.DateTimeFormat().resolvedOptions().timeZone
That said, the usual reason you would need to use formatToTimeZone from date-fns-timezone is when you need to use a different time zone other than the user's local zone. Otherwise, you can usually just use the format function from date-fns.
However, in your case, you are also trying to use the z format specifier to display the user's time zone abbreviation. This isn't provided by date-fns directly, so if that is critical then you will indeed need to get the user's time zone with the Intl api shown above and use formatToTimeZone.
Do keep in mind though that these abbreviations are whatever the IANA data has recorded, which are in English only, and it doesn't have abbreviations for every time zone. For those that don't, you will see a numeric value instead, such as -02.
Also, many abbreviations can be ambiguous (such as the I in IST possibly meaning India, Israel, or Ireland, and many others...). Thus, in most cases, if you don't need the abbreviation, you're often better off without it.
Just solved a similar problem myself. The trick is to use the format function from date-fns-tz instead of the one from date-fns.
import { format } from "date-fns";
console.log(format(new Date(), "yyyy-MM-dd HH:mm z"));
// 2021-11-29 13:55 GMT-8
import { format } from "date-fns-tz";
console.log(format(new Date(), "yyyy-MM-dd HH:mm z"));
// 2021-11-29 13:55 PST
See documentation here:
https://date-fns.org/v2.27.0/docs/Time-Zones

Dates manipulation with moment.js

I receive the following date from the server:
"2018-11-21 07:00:00 UTC"
Then, I convert it using userTimzone variable (since I wish the editor would use the user timezone):
dateOfAction: moment(dateOfLoss, 'YYYY-MM-DD HH:mm Z').tz(userTimzone).unix(), So dateOfAction is 1542776400. That is: Wednesday, November 21, 2018 5:00:00 AM - and so far so good. That is the dateOfAction in UTC with after right offset
I'm using react-datetime as the calendar the edit the date, using UTC.
When performing the save action, without touching the date, the calendar output is still 1542783600, but I wish to be 1542783600 - i.e the same value as in the beginning, reverting the offset at the other direction.
How can I achieve that?
A few things:
Moment doesn't map the Z token to the string "UTC". Since you're parsing in local mode, you are actually getting a moment based on the local computer's time zone rather than UTC. Thus, change the first part of your code to:
moment.utc(dateOfLoss, 'YYYY-MM-DD HH:mm [UTC]')
The brackets are to treat UTC as a literal string, which isn't strictly required so you can omit it if you like and the result will be the same.
moment.utc(dateOfLoss, 'YYYY-MM-DD HH:mm')
There's no need to call .tz(userTimezone) if you're just going to call .unix() subsequently. Unix timestamps are always UTC based. Though it's not clear why you're asking for a Unix timestamp, as react-datetime doesn't need one.
The two values you gave in the last paragraph of your question are identical, so I'm not sure specifically what you were looking for. The time you gave is indeed 1542783600, not 1542776400.
The readme file of react-datetime describes all the options you can use. You can simply pass the moment object obtained above to the value prop. You might need to use the utc or displayTimeZone props if you want to change the behavior. You might also need to call .local() or .tz(userTimezone) on the moment object before passing it in, but I'm not certain if that is required or not for this particular component.

Convert hour to date time without adding +1

Hi im using moment js to convert this string 20:00 I tried:
var a = moment("20:00", "HH:mm")
console.log(a.format()) // 2016-09-08T20:00:00+01:00
the problem when I store in mongodb it become
2016-09-10T19:00:00.000Z
I want to store 2016-09-10T20:00:00.000Z
anyway can explain why please ?
When you say that you want to store 2016-09-10T20:00:00.000Z what you are saying is that you want to assume that your date and time is UTC.
To assume that the date you are parsing is a UTC value, use moment.utc
var a = moment.utc("20:00", "HH:mm")
console.log(a.format()) // 2016-09-08T20:00:00Z
Note that when you parse a time without a date, moment assumes the current date. This may not be the behavior that you want.
I'm also not sure if you want a UTC date (which is what you are saying), or a local date without an offset indicator. If you want a local date without an offset indicator, simply use a format without an offset:
moment.utc("20:00", "HH:mm").format('YYYY-MM-DDTHH:mm:ss.SSS')
"2016-09-08T20:00:00.000"
If you are dealing with local dates that do not have a time zone association, I recommend using moment.utc to parse, as this will ensure that the time does not get shifted to account for DST in the current time zone.
For more information about how to parse dates into the time zone or offset that you would like in moment, see my blog post on the subject.
This it how it should look:
var a = moment("20:00", "HH:mm")
console.log(a.utcOffset('+0000').format())
<script src="http://momentjs.com/downloads/moment.min.js"></script>
Doe, the problem is that you are using timezones when you create the date.
MomentJS uses your current timezone automatically.
Mongo however saves the time as it would be in another timezone.
Therefore, if you want the two strings to format the same way, you need to set the timezone.

How to change only timezone without modifying the time in momentz

I have date and time in 2016-06-21T10:00:00-07:00 format which represets 06/21/2016 5 PM in PST, I just want to change this to 06/21/2016 5 PM in EST and vice versa. How can I do it with momentz?
JSFiddle
debugger;
var dateTime = moment('2016-06-21T10:00:00-07:00');
var newDateTime = dateTime.clone();
newDateTime.tz('US/Eastern');
//dateTime = dateTime.utc();
console.log(dateTime.utcOffset());
console.log(newDateTime.utcOffset());
console.log(newDateTime.utcOffset() - dateTime.utcOffset());
//console.log(utc.format());
dateTime = dateTime.add(newDateTime.utcOffset(), 'minutes');
console.log(dateTime.format());
console.log(new Date(Date.parse(dateTime.format())).toJSON());
EDIT:
given input = 2016-06-21T08:00:00-07:00 (PST)
expected output = 2016-06-21T08:00:00-04:00 (EST)
So when I convert that to UTC then it should become
2016-06-22T15:00:00Z for PST
2016-06-22T12:00:00Z for EST
I think you are confused about how ISO8601 format works. This format always represents local time with a time zone offset. Thus 2016-06-21T10:00:00-07:00 represents June 21 2016 at 10 AM in a timezone that is currently UTC-7 (this could be US pacific, among many others).
It sounds like you want to take the local time, but put it in a new timezone. This opens up some interesting questions about why you are receiving the date in the format that you are. If the date is meant to be interpreted as an exact point on the global timeline, then the format you are receiving it in is good. If however, the date is meant to be interpreted as a local time (not relative to UTC), it might be worth considering the possibility that the format of the date needs to be changed at the source. For instance, if you are making an ajax request to an API, and it is returning a date in this format, but that date actually has no relationship to UTC, it would be good to try to change that API to only send the local time (without the offset). If you were able to do that, then the following code would work:
moment.tz('2016-06-21T10:00:00', 'America/New_York').format()
"2016-06-21T10:00:00-04:00"
If you are unable to do that, or if the date is meant to be interpreted as an exact point on the global timeline, but you wish to ignore that in your specific use case, that can be done. You will need to specify a parse format that ignores the timezone offset on your initial time stamp. The code would be as follows:
moment.tz('2016-06-21T10:00:00-07:00', 'YYYY-MM-DDTHH:mm:ss', 'America/New_York').format()
"2016-06-21T10:00:00-04:00"
You might benefit from the material in this blog post, as it covers how ISO8601 format works, and how all of moment's constructor functions work.
Checkout moment().utcOffset() You can pass in the offset as parameter to this function and the date would use that locale.
Assuming you know beforehand the utcOffsets required which in your case are -420 and -240 or -300(EST with DayLightSaving). Below can be done
var dateTime = moment('2016-06-21T10:00:00-07:00');
dateTime.utcOffset(-420).format();
"2016-06-21T10:00:00-07:00"
dateTime.utcOffset(-240).format()
"2016-06-21T13:00:00-04:00"
NOTE: With -04:00, it should 13:00:00 and not 07:00:00 - http://www.timeanddate.com/time/zones/est
EDIT: This answer was posted to the earlier version of question, where same time was needed in different timezones. If it is incorrect, kindly please elaborate on how it is.
Thanks!

Categories

Resources