Moment time difference won't use moment timezone but users computer time - javascript

I'm attempting to display a duration ticker for something. The start time is always in London time. It works perfectly for people in England/the same timezone, however when people in other time zones look at the duration it displays the wrong value (If you're in a timezone behind England => negative values/too small values, timezone ahead => value too large).
My solution to this was to use moment-timezone. I added the moment timezone data correctly I've attempted to use this timezone data (code simplified and separated into individual lines for easier readability):
let londonTimeNow = moment().tz('Europe/London'),
jobStartTime = moment(job.start, 'DD/MM/YYYY HH:mm:ss'),
diff = londonTimeNow.diff(jobStartTime);
duration = moment.duration(diff).format('HH:mm:ss', {trim: false});
I was hoping this would then get the current time in London and compare to the start time no matter where you are in the world. However, it seems the diff function converts the time to the user's computer time. I tried formatting the londonTimeNow to be a string, but then the diff function doesn't work.
Note, I've debugged and moment().tz() is working correctly, I've tried with other time zones and it gets the correct time in the zone specified.
Any ideas?
EDIT:
It seems I can get it working by manually setting the offset property of 'londonTimeNow' to 0. However this doesn't feel quite right to me. I'd prefer a solution that seems less like a hack.

You should specify the jobstart time in the same way you declare london time using the same timezone:
jobStartTime = moment.tz(jobStart, 'DD/MM/YYYY HH:mm:ss','Europe/London'),
This will set the job start time using the same timezone.

Could you add expected output or specify what you mean by 'diff converts the time'?
The result of diff is a duration and is not in any timezone.
Other than that, the problem seems to be in not using timezone for task start.
Try this:
let londonTimeNow = moment().tz('Europe/London'),
jobStartTime = moment(job.start, 'DD/MM/YYYY HH:mm:ss').tz('Europe/London'),
diff = londonTimeNow.diff(jobStartTime);
duration = moment.duration(diff).format('HH:mm:ss', {trim: false});

Related

How to subtract the time based on the timezone offset

I tried looking for an answer but could not find a particular answer which does with Timezone offset. Hence, posting the same really sorry if there are any answer present already.
I have a requirement in the project where I need to convert the time to UTC or GMT based on the time specified by the user and timezone offset value provided by the user. Basically, the user provides his local time and timezone offset value according to his local time. I need to convert that into UTC/GMT time.
I am using the Node.js and following is the data which is available in the Node.js Backend:
Time: 2020-11-05T15:00:00.000Z
Timezone offset value: +02:00
As the timezone offset value is +02:00 I need to subtract it from the time to convert it into the UTC format. so I can get the time as: 2020-11-05T13:00:00.000Z. I using the moment-js as well. Can someone please help me how can achieve this using the Node.js or using the Moment.js?
If the offset takes daylight saving time into account you should be able to do:
const the_date = '2020-11-05T15:00:00.000';
const offset = 2;
const utc_time = moment.utc(the_date).subtract(offset, 'hours');
It's not clear which way you are trying to convert. If you are converting from UTC to a fixed offset, you can use the utcOffset function, like this:
const m = moment.utc('2020-11-05T15:00:00.000Z').utcOffset('+02:00');
m.format(); //=> "2020-11-05T17:00:00+02:00"
Or - if you were trying to convert from +02:00, then you would include that offset in the input instead of the Z (Z means UTC). You would then just call the utc function, like this:
const m = moment('2020-11-05T15:00:00.000+02:00').utc();
m.format(); //=> "2020-11-05T13:00:00Z"
However you should be aware that an offset is not the same as a time zone. A time zone can have more than one offset, one of which will apply at a given point in time. Those offsets can change due to daylight saving time and for changes in standard time. Thus, asking a user to pick "his timezone offset value according to his local time" is problematic - as you may be applying that offset to the wrong point in time. See "Time Zone != Offset" in the timezone tag wiki for further details.
You should also understand Moment's project status, and possibly choose a different library.
After some more research and trying, I was able to convert it. Posting the answer in addition to Christians response (https://stackoverflow.com/a/64701083/7584240) so if anyone is looking for the answer they will have another option:
var moment = require('moment');
var time = "2020-11-05T15:00:00.000Z";
var timeoffset = "+02:00";
time = moment.utc(time).local().format('YYYY-MM-DDTHH:mm:SS.000');
time = moment(time, 'YYYY-MM-DDTHH:mm:ss.000').subtract(timeoffset).format('YYYY-MM-DDTHH:mm:ss.000') + 'Z';

MomentJS, how to ignore user's local timezone, and use

Here's the scenario:
1- User opens the website, and enters 07:00 am using a dropdown field, which will give me this :
// 1578600000000 => Save to DB
// Fri Jan 10 2020 07:00:00 GMT+1100 (Australian Eastern Daylight Time)
The user himself is in Sydney, which means his local clock is on GMT+1100
However, he wants to represent this time as Asia/Tehran time, because that's where he's going to be tomorrow. So essentially, he wants me to completely ignore his local time and see him as if he's in Asia/Tehran. So when he's in Tehran tomorrow, he can see his calendar has 07:00am.
On the other hand, are the people all over the world who will see his available time, let's say from Australia/Perth.
I thought something like the below work, as per momentJS documentation, but it doesn't.
First, convert the timezone to Asia/Tehran, which is the user's desired place:
const desiredTimeZone = 'Asia/Tehran';
let OriginalDesired = moment.tz(1578600000000,desiredTimeZone);
Then, when representing it to the people in Australia/Perth make sure it's in their timezone
const PerthTimeZone = 'Australia/Perth`; // this is dynamic, can be anything
OriginalDesired.clone().tz(PerthTimeZone);
I naively thought this should work. But I noticed the original timestamp 1578600000000 is a UTC timestamp, meaning it's not really 07:00am, it's actually 20:00pm, because Javascript
has subtracted 11 hours, which is the very original user's local timezone's offset, from the user entry.
I managed to work around it by adding and subtracting the offset in a dramatic way, but it only works in one scenario.
const originalTime = 1578600000000;
const LocalAdjustment = moment(originalTime).tz("Australia/Sydney").utcOffset() * 60000;
const DesiredAdjustment = moment(originalTime).tz("Asia/Tehran").utcOffset() * 60000;
const newUTC = originalTime + LocalAdjustment - DesiredAdjustment;
And when representing this to the user's in Tehran
moment(newUTC).tz("Asia/Tehran").format('hh:mma'); // 07:00am.
I know this is probably stupid and obviously only works in one scenario, but is this the right path that I'm going? or is there an easier way?
By the way, all the calculations are on my server, which is 'UTC'.
You said:
1- User opens the website, and enters 07:00 am using a dropdown field, which will give me this : // 1578600000000 ...
You've already lost. If the local time zone is not relevant, then don't write code that assumes that it is.
In other words, you probably have something along the lines of:
moment("2020-01-10 07:00")
Instead you should have something like:
moment.tz("2020-01-10 07:00", "Asia/Tehran")
Or rather, you should simply send "2020-01-10 07:00" and "Asia/Tehran" to your database, then later retrieve them and pass them to moment-timezone when you need to know what moment that represents.
As to your other approach, it's generally not a good idea to add or subtract time zone offsets from timestamps. Unix timestamps are inherently UTC based. Adding or subtracting will produce a different moment in time, not adjust for time zone.
Consider that there's also a slim (but not impossible) chance that the offsets returned by your code are incorrect, as they would have to be shifted before being looked up. In other words, Tehran was at UTC+3:30 on the date in question, so the timestamp passed would have to be adjusted by 3 hours 3 minutes before being passed to the moment constructor. This leads to circular logic, and is difficult to resolve. It will show up for timestamps near transitions (either for DST, or for changes to standard time for a particular time zone).

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')

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.

Using timezones with moment.js fromNow() or from()

I want to show users how long has been elapsed since they performed an action.
The date+time of the action happening is stored on the server, in the server's timezone. That's what's causing the trouble, since if the user's computer's timezone is 12 hours ahead of the server's timezone, then if the user adds something right now, moment.js will show '12 hours ago' as the output of fromNow() rather than just now.
To try to solve this, I'm trying the following method:
var actionTime = moment( action.timeStamp);//time of when user performed action
var serverTime = moment().zone('-07:00'); //current server time
console.debug( serverTime);//outputs Wed Sep 11 2013 15:19:51 GMT-0700
var timeAgo = serverTime.from( actionTime);
But despite of all this, timeAgo still shows the difference between the client's timezone and the server's timezone (i.e showing '12 hours ago' instead of 'now');
Anyone know how to fix this or what I'm doing wrong?
Ideally, you would want to pass a UTC timestamp from your server to the client. That doesn't mean you have to switch your whole server over to UTC, it just means that you would convert from the time in your database to UTC on the server before sending it over the web. Sure, it would be even better if you actually stored times in UTC, but you said you aren't in a position to make that sort of change right now. But let's just work off the assumption that you can't change anything at all on the server.
We'll also assume that your server is fixed to the UTC-07:00 offset. In real life, this would only be true for places like Arizona that don't follow daylight saving time. So if you are in Los Angeles and are in Pacific Time, then some of your data is based on UTC-07:00, but some of it is based on UTC-08:00. That requires a lot more work if you want to do it in JavaScript.
Let's also assume that the input is already a string in ISO8601 format. (If it's not, then let me know and I will adjust this code.)
var s = "2013-09-11 18:00:00"; // from action.timeStamp
var actionTime = moment(s + "-07:00", "YYYY-MM-DD HH:mm:ssZ");
var timeAgo = actionTime.fromNow();
The reason your other code didn't work is because in the first line, you are affected by the time zone of the browser. The zone setter in the second line just changes the zone for formatting, not changing the actual moment in time.
Also, when you dump a moment to the console for debugging, make sure you format it for output. Otherwise you are just looking at its internal property values, which may or may not make sense directly.
I have solved it in a different way, maybe this option was not possible back when the question was asked, but might be easier now.
I used moment-timezone.js (which requires moment.js 2.6.0+).
I set the default timezone to my server's timezone like this:
moment.tz.setDefault("America/New_York"); // "America/New_York" in my case
and then just use it normally. fromNow() will use the timezone in the client to calculate the time that has passed since that moment.
moment(myDatetime).fromNow();
i had the same issue and used the above comments to modify my code. I did the following to get it resolved:
transform(value: string) {
var time = moment(value).utc();
return moment(time, "YYYYMMDD").fromNow();
}
I was missing the .utc() to convert it before I applied the .fromNow()
Things to note this is being used within a Pipe for Ionic 3 and the above code is from the pipe logic.

Categories

Resources