Preserve Local DateTime in Javascript - javascript

My customer's store is in GMT +5:30 timezone but the user's locale is in GMT +8 timezone.
Currently, I'm using javascript's .toISOString() function to convert to UTC and storing UTC in the database. I retrieve UTC from the database and send exactly that the browser, so the new Date('2019-11-15T00:00:00Z') function converts the UTC to the browser's locale.
But, if the user opens a record created by GMT +8 timezone user or vice-versa, the dates are getting messed up.
I'm thinking it would be good if I can transfer the exact date the user enters in the browser and send that exact date to the backend to easily offset using the store's timezone?
The frontend is in VueJs and the backend is in C#.

Always store UTC time in database
Just Store the UTC time in your database, In your client-side, Get client's current timezone,
Here, I am getting IANA timezone from client's system
// get client's timezone (From user's system)
var clientTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
Then, Convert the UTC depends on the timezone and show it to user,
// assume 2019-11-15T00:00:00Z is the UTC date from your database
var convertedTime = new Date('2019-11-15T00:00:00Z').toLocaleString("en-US", {timeZone: clientTimezone});
Example
var clientTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
var timeUtc = '2019-11-15T00:00:00Z';
var convertedTime = new Date(timeUtc).toLocaleString("en-US", {timeZone: clientTimezone});
console.log("Time to Current User : " + convertedTime);
var timeInUsa = new Date(timeUtc).toLocaleString("en-US", {timeZone: "America/New_York"});
console.log("Time in America (New York) : " + timeInUsa);
var timeInAustralia = new Date(timeUtc).toLocaleString("en-US", {timeZone: "Australia/Brisbane"});
console.log("Time in Australia (Brisbane) : " + timeInAustralia);

Note that the js Date object is simply a number that represents an absolute time (independent of any timezone).
So the best encoding/format for transferring and storing a date is that number. IMO this is much simpler than storing a UTC string.
So, on the end-user's machine you would Date.parse the date string provided by the user and this would take account of the user's time zone for you and give you the absolute time number for sending and storing on your backend.
Don't do any formatting or parsing of dates on the backend if you are using Node because there are serious gotcha's and because you shouldn't need to anyway: any client device that needs a date string will do the formatting locally which will automatically convert it to the correct format for their locale and timezone. [edit: the Q did not specify the backend when I wrote this.]
You will need to watch for some gotcha's in the Date.parse function but these are minor compared to the node problems. The most significant IMO is that it will interpret dates in YYYY-MM-DD as ISO 8601 dates (which makes some sense) but then assume that they are GMT if no timezone is specified so you should make sure there is a timezone specified if you use that format.

Related

Moment unable to convert in different timezone

My server is returning me dates in UTC and I want to convert them to the timezone of the server (even if the customer browser is in a different timezone !)
Here is one example, my server is in Europe/Berlin.
var dateAsString = '2022-04-11T22:00:00.000Z'; // 2022-04-12 00:00:00 in Europe/Berlin
var utcDate = moment.utc(dateAsString);
console.log(utcDate.format());
var serverTzDate = utcDate.clone().tz('Europe/Berlin');
console.log(serverTzDate.format());
console.log(serverTzDate.format());
var test = moment.utc('2022-04-11T22:00:00.000Z').tz('Europe/Berlin').format();
console.log(test);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.2/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>
If my browser is in the Europe/Berlin timezone the output is correct:
2022-04-11T22:00:00Z
2022-04-12T00:00:00+02:00
2022-04-12T00:00:00+02:00
But if I change my timezone (for my example I took Pacific/Niue), then the result is totally wrong, I suppose moment is doing something with the timezone of the browser but how to avoid this ? Here is the output:
2022-04-11T09:00:00Z
2022-04-11T11:00:00+02:00
2022-04-11T11:00:00+02:00
I need to have the date in the timezone of the server and not in the timezone of the browser
With another timezone: Pacific/Kiritimati (-840)
2022-04-12T10:00:00Z // Wanted 2022-04-11T22:00:00Z (UTC Date as entered)
2022-04-12T12:00:00+02:00 // Wanted 2022-04-12T00:00:00Z (Converted in Europe/Berlin)
2022-04-12T12:00:00+02:00 // Wanted 2022-04-12T00:00:00Z (Converted in Europe/Berlin)
2022-04-12T12:00:00+02:00 // Wanted 2022-04-12T00:00:00Z (Converted in Europe/Berlin)
Again all the dates are wrong, moment seems not able to parse my date as an UTC date ? It apply the timezone offset of the browser even if I'm using the utc method
moment.utc('2022-04-11T22:00:00.000Z').tz('Europe/Berlin').format()
please check this document. https://momentjs.com/timezone/docs/#/using-timezones/
below is my code result, whatever timezone you changed, the result is '2022-04-12T00:00:00+02:00'

Javascript displaying incorrect date

Im currently testing out running a server with routes etc. and am trying to display the date of the previous person to request that route. Usually when i use the new Date() object i get it in my local time, but for some reason it is displaying it in UTC (coordinated universal time) and im not sure how to change it.
my code is
var date;
var dateArr = [];
var counter = 0;
router.get('/last.txt', function(req, res) {
counter++;
date = new Date().toString();
dateArr.push(date);
if (counter == 1) {
res.send("");
} else {
res.send(dateArr[counter-1]);
}
});
but i keep receiving the date in the format:
Fri Mar 26 2021 07:24:50 GMT+0000 (Coordinated Universal Time)
Any advice would be appreciated!
EDIT: I live in Australia so i think it usually outputs in AEDT
It would appear that UTC is the timezone in place for the server where this code is running. That's fine, you can convert that to the local timezone on the client quite easily. Rather than using toString, though, I would store either the Date object, or its time value (the result of .valueOf()), or the result of .toISOString() and then send either the time value (a number) or the ISO string value to the client. That way, you're not relying on the timezone of the server. When you pass either of those (the time value or the ISO string) into new Date on the client site, the resulting Date will have the same date/time, but because it's operating in the client's timezone, its local time formatting methods like toString, getFullYear, etc., will use the local timezone of the client.
E.g., change toString to (for instance) toISOString in your code, and on the client:
const dt = new Date(theStringFromTheServer);
console.log(dt.toString()); // Will use the client's timezone to show the date/time

How to save correct time in database?

I have one object called appointment which has two properties: StartDate and EndDate.
When I make POST request I send these values using ISOString time .
this.appointment.StartDate.toISOString()
On the server-side, I received these properties with correct values. Also, it seems to be correct when I create model in order to save appointment to the database. I used .ToUniversalTime() method.
var newAppointment = new Appointment()
{
StartDate =Convert.ToDateTime(model.StartDate).ToUniversalTime(),
EndDate = Convert.ToDateTime(model.EndDate).ToUniversalTime(),
SpecialityId = speciality.Id,
LocationId = location.Id,
PatientId = patient.Id,
UserId = user.Id,
Observations = model.Observations
};
But in database I found another values. Can explain somebody why is this behaviour ?
For instance, I used 2017.09.01 11:00 for StartDate and in database i found 2017-09-01 08:00
The server and database is located in the westeurope.
A few things:
Don't call ToUniversalTime in a web application. It's designed to convert from the server's local time zone to UTC. The server's time zone should be irrelavent to your application. Web applications should never use ToUniversalTime, ToLocalTime, DateTime.Now, TimeZoneInfo.Local, DateTimeKind.Local or any other method that uses the time zone of the computer it's running on.
Ideally, on the server side, your model.StartDate and model.EndDate would already be DateTime objects, because they'd have been deserialized that way. Therefore, you probably don't need to call Convert.ToDateTime. If they are strings, then I would adjust your model class accordingly.
On the client side, assuming StartDate and EndDate are JavaScript Date objects, and they were created using local time values (that is, the time zone of the browser), when you call toISOString, you're not just getting a string in ISO 8601 format - it is also converting it from the browser's time zone to UTC.
In your example, the UTC time is 3 hours ahead of UTC for the date and time shown. From your profile, I see you are located in Romania, which is indeed UTC+3 for this date, because it is currently observing Eastern European Summer Time. When Summer Time ends (on October 29, 2017 at 04:00), it will return to UTC+2. For this reason, you cannot simply add three hours to all values.
If you want to send local time values from the client, you should send them in ISO 8601 format, without any Z or offset, for example 2017-09-01T11:00. There are several ways to achieve this:
The best way is to not have them in a Date object to begin with. For example, if your input uses the <input type="datetime-local" /> input type (as specified in HTML5), the .value property is not a Date object, but rather a string in ISO 8601 format.
If you can't avoid a Date object, then create a local ISO string, like this:
function dateToLocalISOString(date) {
var offset = date.getTimezoneOffset();
var shifted = new Date(date - offset * 60 * 1000);
return shifted.toISOString().slice(0, -1);
}
OR, using Moment.js:
moment(yourDateObject).format("YYYY-MM-DD[T]HH:mm:ss.SSS")
Lastly, you will probably read advice from others about storing these as UTC. Don't listen. The advice "always use UTC" is shortsighted. Many scenarios require local time. Scheduling appointments is a primary use case for local time. However, if you need to act on that appointment, you'll use the current UTC time, and you'll also need some information about the time zone for the appointment so you can convert from UTC to the appointment's time zone. For example, if this is something like an in-person doctor's office appointment, then it's safe to assume the time zone of the doctor's office. But if it's an appointment for an online meeting, then you'll have to capture the user's time zone separately and apply it on the back end where appropriate.
The problem is with your current timezone.
What your application does is get current timezone (+3) in this case.
Now it got your timezone but it will convert to utc time. So what will happen, your current time will be -3 hours.
If you not adjust to summer and winter time then you can simply add 3 hours to the datetime. Otherwise you have to get the offset of your timezone and add that to the current datetime value.
Take care if you use this application in different timezones. For example You life in +3 and some else life in +2 timezone.

Get timezone from users browser using moment(timezone).js

What is the best way to get client's timezone and convert it to some other timezone when using moment.js and moment-timezone.js
I want to find out what is clients timezone and later convert his date and time into some other timezone.
Does anybody has experience with this?
When using moment.js, use:
var tz = moment.tz.guess();
It will return an IANA time zone identifier, such as America/Los_Angeles for the US Pacific time zone.
It is documented here.
Internally, it first tries to get the time zone from the browser using the following call:
Intl.DateTimeFormat().resolvedOptions().timeZone
If you are targeting only modern browsers that support this function, and you don't need Moment-Timezone for anything else, then you can just call that directly.
If Moment-Timezone doesn't get a valid result from that function, or if that function doesn't exist, then it will "guess" the time zone by testing several different dates and times against the Date object to see how it behaves. The guess is usually a good enough approximation, but not guaranteed to exactly match the time zone setting of the computer.
var timedifference = new Date().getTimezoneOffset();
This returns the difference from the clients timezone from UTC time.
You can then play around with it as you like.
All current answers provide the offset differece at current time, not at a given date.
moment(date).utcOffset() returns the time difference in minutes between browser time and UTC at the date passed as argument (or today, if no date passed).
Here's a function to parse correct offset at the picked date:
function getUtcOffset(date) {
return moment(date)
.subtract(
moment(date).utcOffset(),
'minutes')
.utc()
}
Using Moment library, see their website -> https://momentjs.com/timezone/docs/#/using-timezones/converting-to-zone/
i notice they also user their own library in their website, so you can have a try using the browser console before installing it
moment().tz(String);
The moment#tz mutator will change the time zone and update the offset.
moment("2013-11-18").tz("America/Toronto").format('Z'); // -05:00
moment("2013-11-18").tz("Europe/Berlin").format('Z'); // +01:00
This information is used consistently in other operations, like calculating the start of the day.
var m = moment.tz("2013-11-18 11:55", "America/Toronto");
m.format(); // 2013-11-18T11:55:00-05:00
m.startOf("day").format(); // 2013-11-18T00:00:00-05:00
m.tz("Europe/Berlin").format(); // 2013-11-18T06:00:00+01:00
m.startOf("day").format(); // 2013-11-18T00:00:00+01:00
Without an argument, moment#tz returns:
the time zone name assigned to the moment instance or
undefined if a time zone has not been set.
var m = moment.tz("2013-11-18 11:55", "America/Toronto");
m.tz(); // America/Toronto
var m = moment.tz("2013-11-18 11:55");
m.tz() === undefined; // true
You can also get your wanted time using the following JS code:
new Date(`${post.data.created_at} GMT+0200`)
In this example, my received dates were in GMT+0200 timezone. Instead of it can be every single timezone. And the returned data will be the date in your timezone. Hope this will help anyone to save time
if the user's timezone is all you wanted then
const localtz = moment.tz.guess() // returns user's timezone
Additionally if you wanted to use it then the best way to convert a timestamp to user's timezone is
const time = moment.tz(response.timestamp)
const localtz = moment.tz.guess() // user's timezone
const date = time.clone().tz(localtz) // convert time to user's timezone
here localtz is the user's timezone and using it we can convert the timestamp to user's local time
First, you can find out the clients time zone using the following
let zoneVal = moment().tz(Intl.DateTimeFormat().resolvedOptions().timeZone).format('Z')
it will return you the GMT zone format for example +5:30 (colombo/srilanka & Delhi/India) or +6:00(Dhaka Bangladesh) depending on the region you are in.
secondly,
if you want to find out the time of a particular time zone , then do the following
moment.tz("Asia/Dhaka").format()
which will return you the time zone value in ISO format of Dhaka.
Using moment timezone you can get easily your local date-time
moment().utcOffset(0, true).format()

CouchDB filtering UTC dates by month and year

I save a UTC timestamp for every document in one of my Couch databases.
I want to query and filter those documents based on a specific month of a year. For this purpose I created a view with the following map function:
function(doc) {
var date = new Date(doc.activity.date);
emit([doc.user, date.getUTCMonth(), date.getUTCFullYear()], doc.activity.distance);
}
I query this view with for example: ?key=["1edd367d08770ea34586dbe6dc03ea2c",3,2013], which works perfectly fine as long as the UTC and local time is in the same month.
What I am asking is how can I ensure that a query like this returns all documents of a month, where the month is defined by the clients local time and not UTC?
The client is a JavaScript based Web-Client, but the query is handled through a NodeJS API in the back end.
What I am asking is how can I ensure that a query like this returns all documents of a month, where the month is defined by the clients local time and not UTC?
Find the start of the client's month and the start of the client's next month. These will form the range to query. For example:
var now = new Date();
var start = new Date(now.getFullYear(), now.getMonth(), 1);
var end = new Date(now.getFullYear(), now.getMonth() + 1, 1);
Then you will need to decide the best way to get this as UTC to pass to the database. For example:
var startUTC = start.toISOString();
var endUTC = end.toISOString();
(I am not sure of the specifics for CouchDB, so adjust if necessary.)
Then you need to do a range query in your database. You can't just match on year and month because your data is in UTC and the edges will not necessarily line up with your client's time zone.
I'm not familiar with Couch's syntax, but in SQL it would be something like this:
... WHERE activityDate >= startUTC AND activityDate < endUTC
Since we passed the start of the next month as the end date, make sure that perform an exclusive < and not an inclusive <= operation. Only the start date should be inclusive.
What I am asking is how can I ensure that a query like this returns
all documents of a month, where the month is defined by the clients
local time and not UTC?
If the client sends his local time, he must do so with a reference as to which timezone he is located in.
Otherwise, he must use UTC or GMT.
Without including the timezone, you would basically have to detect what timezone the client is located in and make assumptions which would not work out (e.g. a laptop located on a italian IP may be set with american time).
See: http://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators
The ISO 8601 format has time zone designator that would allow clients to send their localtime but with the attached time offset.
Couchdb supports this ISO 8601 format:
http://comments.gmane.org/gmane.comp.db.couchdb.user/14485
http://wiki.apache.org/couchdb/IsoFormattedDateAsDocId .
Remaining would be for you to do your conversions/comparisons etc to UTC/Unix time/ etc... whatever format works for you in order to be able to compare the KEY with the DATA.
The main point is that your comparing datetimes that are converted to the same timeoffset or UTC (no time offset).

Categories