Why is input="date" changing values between browser and server? - javascript

I have a <input type="date"> field on my webpage and cannot understand why the value of this input changes when I send it via a HTTP POST request from the browser (using AngularJS) to the server (Java). Here is an example of what happens:
Date selected in browser via <input type="date">: Wed Jan 24 2018 00:00:00 GMT+1100 (AUS Eastern Daylight Time)
Date that appears on server when sent via POST request: 2018-01-23T13:00:00.000Z
It appears that, on the server-side, the value changes from the browser's timezone to UTC (in this case from Australia/Sydney to UTC).
How can I use <input type="date"> to just send the date portion of the value without having the timezones converted and dates messed up?

Failure
The Z on the end is short for Zulu and means UTC.
So 2018-01-24T00:00:00.000Z and 2018-01-23T13:00:00.000Z do not represent the same moment. They represent two different moments in history, two different points on the timeline, eleven hours apart.
If you are certain that pair of values were intended to be the same moment, then something went wrong. This is not an adjustment in time zone.
We cannot further diagnose the source of the error as we lack information.

What probably happens is your server converts it to it's local timezone.
Why it happens? To normalize your time.
Imagine situation where someone from UK sends date (for example 12am 2018.01.22), and then someone from US receives it, and it will show him exact same time, even if it's different time zone (different time for him).
What you want to do is to calculate timezones(webpage side), or if it's not something you want to achieve, try changing your servers time zone.

Related

Javascript - Storing Date as another timezone and parsing it back

The context
I've got an application that lets an admin configure a timezone for their company. Other users, then, can modify the schedule of the company, but it should be always saved with the company's timezone.
This data needs to be stored in UTC in the server, and whenever someone gets it back it needs to be displayed with the company's timezone time.
For example:
Admin sets timezone to "Europe/Madrid" (GMT +01:00).
User A, in timezone "America/Sao Paulo" (GMT -03:00), sets the Schedule to 09:00 - 15:00 (two dates).
The schedule needs to be sent to service in UTC as if the user was in 'Europe/Madrid'.
Service stores the date in UTC
After that, someone in timezone "Europe/Athens" (GMT +02:00) visits the page and they have to see the time as if they were in "Europe/Madrid". So, in this case, 09:00 selected in "America/Sao Paulo" should be stored as "08:00 UTC" (Madrid timezone).
What I need
Basically, I need the time, no matter what is the timezone, to be displayed as if the user was always in the company's timezone, and whenever someone saves the schedule time, I need it to be saved as if they also were in "Europe/Madrid".
Also, the displayed time shouldn't change in DST, as the schedule will be always the same for all the year. So, the user should see all year the "09:00 - 15:00" schedule.
Keep in mind that the server uses SSR, and this means that in the first render we do not know the local timezone of the user who's going to see the data.
What I've tried
I've tried libraries such as "date-fns-tz" and "moment-tz" to convert the dates, but they do not work for the intended, as whenever I parse it to UTC they do it ok but when I try to parse it again to the desired timezone they do not work as expected.
I've tried "countries-and-timezones" library to get the timezone offset and try to manually adjust the dates, but I keep getting messed up by the server's local timezone (which I suspect is the reason why the other libraries didn't work as expected either).
Is there some way I can achieve that?
Thank you.
Basically you'll want to forget about the user's local timezone. America/Sao Paulo should play absolutely zero role in any of this. When the user enters "9:00" in the browser, you just want that to be "9:00" and nothing else. Since the company's timezone is Madrid, you want the input "9:00" to be interpreted as "9:00 Madrid". You can do that either client-side or server-side. But at no point do you want any conversion going on from Sao Paulo time to Madrid time.
Secondarily, you'll want to be very clear what you're storing in the database and what you'll do with this information later. To be clear, what does not work and what you need to avoid is this:
user enters "9:00"
you store 2022-12-01T08:00:00Z in the database
you automatically generate future dates like 2023-05-01T08:00:00Z from it
That generated date in May will be 10:00 Madrid, not 9:00.
You pretty much have two choices:
store just "9:00 Madrid" in the database, i.e. a description of a rule that you can later use to generate times
generate concrete timestamps in the Madrid timezone, convert them to UTC, then store them
Which one is more appropriate depends on how you intent to use them. If you want concrete timestamps in your database to query by, then you need to follow these steps:
get input "9:00 Madrid"
create a concrete date 2022-12-01T09:00:00+01:00
convert it to UTC 2022-12-01T08:00:00Z to store it
generate a future date by adding days/weeks/months to it, keeping the time value the same, e.g. 2023-05-01T09:00:00+02:00
convert it to UTC 2023-05-01T07:00:00Z to store it
for display, convert the UTC date to the company's timezone, and display it in that timezone; i.e. the user's local timezone plays absolutely no role (right?)
To do any time arithmetic, you basically want to construct new dates by combining parts of the existing date, in order to avoid changes to the time-of-day. The details will depend on the exact library being used, but in pseudocode it's something like:
var a = new Date(2022, 12, 1, 9, 0, 0, 'Europe/Madrid')
var b = Date.combine(a.date.add(6, 'months'), a.time, a.timezone)
// 2023-05-01 9:00:00 Europe/Madrid
Hope this helps to clarify things a bit.

How to handle BST and GMT timezones on client side? Storing them in a DB as UTC

I've got a Go app and I store all datetimes as UTC:
dateTime, err := time.Parse("2006-01-02T15:04:05.000Z", myDateTime)
This becomes something like: 2022-09-29T19:40:36.150Z.
Now I want to show this datetime client side (on a website), but showing the user the time in their timezone.
Since it's all UTC I could have the user pick their timezone from a list, like this one https://gist.github.com/valo/c07f8db33d223f57a4cc9c670e1b6050.
Then it's just a matter of adding/subtracting some time from the UTC and showing it.
But the problem is with daylight savings time, the client side timezone would have to change when it's summer, and change again when it's winter.
How are people doing this?
Usually, this happens on the client side. Information about the client's timezone is stored on the browser and is available to you if you need it. However, there are functions that sort this out automatically like toLocaleDateString.
Step 1.
Parse the time string 2022-09-29T19:40:36.150Z to a Date object in javascript
Step 2.
Use toLocaleDateString on the date. This will display the Date in the client's timezone setting stored in the browser
MDN docs for toLocaleDateString()

How do I get consistent data across different timezones if I am saving all the dates in UTC +0

I have a system where users from Washington DC can create a post. This post is saved in my system in UTC +0 time. Then, I can use a reporting system which will give me info about every created post in a certain date range. Lets say I select a date range from March 21st 00:00:00 to March 28th 23:59:59 but in my system someone created a post on March 28th 22:30:00 Washington DC time. Washington DC is several hours behind UTC, so this post would be saved at around March 29th 02:30:00, and so when I generate the report for March 21st to March 28th, I will not get the correct result because there is 1 post that has been created on March 28th Washington time, but that is March 29th UTC +0 time.
I first solved this by obtaining the UTC offset of the client and sending it to the server, and so adding that offset to my date range:
// JavaScript
"offsetHours" : parseInt(new Date().getTimezoneOffset() / -60)
"offsetMinutes" : (new Date().getTimezoneOffset() / -60) % 1 * 60
// Python
_range["from"] = strToDate(_range["from"]) - datetime.timedelta(hours = int(request.headers["offsetHours"]), minutes=int(request.headers["offsetMinutes"]))
This solved the issue, but it raised another. Now if I generate a report for the same time range (from 21st to 28th March) from 2 different timezones, I will get different results. This is due to the fact that the 2 users have different offsets and so they affect the from range in different intervals.
Is there any solution to this problem?
You're not necessarily describing a problem, but rather a side effect of how local times around the world work.
At any given time, there is usually more than one "date" in effect somewhere in the world. If you are saving the timestamp of an event that took place, and you have customers around the world, you're not necessarily saving it with the same date that the user thought it was in their own time zone. This is true whether you align the timestamps to UTC or to a specific time zone.
Therefore, you must make a business decision about how your application is intended to work. Do you want your daily reports to reflect posts that were made within a UTC day, or within the day according to the time zone of your business's headquarters? Then store the timestamp in UTC and (optionally) adjust to your business's time zone before or during reporting.
If however you want the daily reports to reflect the date in the user's time zone, then you might want to also store the user's time zone ID (such as America/New_York - not a numeric offset) so that you could convert to that. Keep in mind that if user's are in different time zones, your reports might look strange when examined from a single time zone's perspective.
Another technique that is often used (primarily for performance reasons, but also for clarity of logic), is to keep both a UTC-based timestamp and a separate field for the "business date" that applies. Usually such a field is just a date field, storing a value such as 2021-03-29 without any time or time zone. This field can be pre-converted to a time zone according to whatever rules you decide are applicable for your business. It then becomes a great candidate for an index and works well for range queries for daily reports.
In the end - there is no one "right" way to do it. You have to decide what works best for your use case. If you are working for a larger company and unsure of the business requirements, then ask someone who might already perform a similar activity manually. (Often this is an accounting or sales person in a larger organization.)

How to convert timespan using offset

I have MVC web application. I am storing UTC time in database. (Not datetime but just a time). In C# When I retrieve this time from the database I get timespan object back. I also have offset available in minutes. For example.
double offset = 600;
How do I use this offset to convert timespan to local datetime.
Note I don't want to use DateTime.ToLocalTime () method because that will use server's timezone.
UPDATE1
I am using the Javascript new Date().getTimezoneOffset() method to get the client's offset, and i have offset value stored on the server. Then I also have drop down list that show times as 12:00 AM, 12.30 AM, 1:00 AM etc etc. The dropdownlist is bound to model property SelectedDateTime of type DateTime. Idea is to convert user selected time to UTC and then UTC to localtime based on the offset. So lets say i have offset 300 minitues that would be 300/60 = 5 hours
double offset = 5.00; // this is available on the server
When the user selects time in a drop down list, I am getting a datetime object on the server, ignoring the date part i want to store UTC time into database. This is how I'm converting to UTC time.
TimeSpan utcTime = SelectedDateTime.AddHours(offset).TimeOfday;
I store this utcTime into the database. Now I want to convert UTC timespan into the client's datetime.
I am assuming i have Subtract offset now
var newLocalTimeSpan = utcTime.Subtract(TimeSpan.FromHours(offset));
var newLocalDateTime = new DateTime(newLocalTimeSpan.Ticks, DateTimeKind.Local);
However this throws the error:
Ticks must be between DateTime.MinValue.Ticks and
DateTime.MaxValue.Ticks.\r\nParameter name: ticks
For example with offest 5 hours, If user selects 8:00 PM then it will be converted to UTC and will be stored as 01:00:00.0000000 in database. When I retrieve the UTC value from database its '1:00:00 AM'. Then I subtract 5 hours form this TimeSpan which equals to `-4' now and if I pass Ticks to DateTime..i get above error.
NOTES: If you are curious why model property is DateTime instead of TimeSpan thats because i am using Kendo TimePicker which needs DateTime type.
UPDATE 2
I really appreciate all for your help. I have gone through all the articles #Matt Johnson has posted and it looks like I should not be using offset for calculating the UTC time. Mainly because of the day light time saving. But instead I should be using timezone. So I have 3 options here to find client’s time zone:
1> Use JavaScript to detect time zone
In JavaScript I can do new Date().toString() which returns date time as Sun May 22 2016 02:12:36 GMT-0500 (Central Daylight Time) I can then parse the string to get “Central Daylight Time” and post it to the server. However on server, for .net “Central Daylight Time” is not a valid windows time zone ID.
Question
Is this correct approach? Is JavaScript returning IANA zone id? Will it always return IANA zone id?
If JavaScript is returning IANA Id then I can use Matt’s article here to get windows time zone id
2> Use http://momentjs.com/ to detect client’s time zone
Question
Is momentjs returns IANA zone id?
If momentjs return IANA zone id then I can use Matt’s article above to get windows zone id. One of the reason I don’t like this approach is because I have to use 2 third party libraries momentjs and Noda Time
3> Provide user a drop down list using TimeZoneInfo.GetSystemTimeZones() and let the user selects the timezone.
User will select a time and timezone, then on server I will convert it to UTC using selected timezone and save it DB. However I have to show that time on some other pages, So I again need timezone. That means I have to put the drop down list in such a place on UI where it will be available all the time. Like top menu.
(I can certainly save timezone into DB along with the time, however if user travel to other place he will still see time in initially selected time zone. Which I don’t want)
Are these correct approaches? Am i missing something?
Question
Assume that I implement timezone selection using one of the approach above and i have correct client's time zone with windows timezone id on server in some variable.
Now lets say user selects 6:00 PM (Central Daylight Time , UTC -5) which will convert to UTC as 23:00:00. As long we are in Central Daylight Time the conversion from UTC to local will show 6:00 PM. Once we go into Central Standard Time which is UTC -6 Will the conversion still show 6:00 PM or 5:00 PM?
I am planning to use TimeZoneInfo.ConvertFromUtc(datetimevalue, timezone) method for converting UTC to Local
In general, there are only two viable approaches:
Pass only UTC dates and times to the client, and do all conversions to local time in the browser using JavaScript.
Use this approach when you don't care what the time zone actually is, but you just want it to match the browser's local time.
The Date object can do this, but you may find it easier to use a library such as moment.js, which gives you better control of output format, among other things.
Apply a time zone (not just an offset) to the UTC date and time on the server side, to produce the correct local time value.
Use this approach when the time zone affects an entire application, and needs to be known in server-side business logic.
You can try to guess the user's time zone using jsTimeZoneDetect or moment.tz.guess() in moment-timezone. However, it's just a guess, and it is always an IANA time zone ID (such as America/Los_Angeles).
Asking the user for their time zone from a list is a good idea. Usually one would place this on a user settings or profile page. You can use the guess made earlier to pick a default value from the list.
You will indeed need to use Noda Time on the server if you are using IANA time zones on the client.
Some applications choose to list Windows time zones instead, which is a much simpler approach as you can get everything from the TimeZoneInfo class. However, recognize that there are limitations with this approach including:
Localization issues, as you cannot easily get at display name strings other than the ones matching the operating system's default language, not .NET's globalization and localization features.
Maintainability issues, as you yield control to the operating system for keeping the time zone data updated. This may seem more convenient, but you may find that your hands are tied when keeping up with short-notice time zone changes. This is especially problematic when you don't have control over how or when updates are applied to the OS, such as with Microsoft Azure App Service.
Compatibility issues, as Windows time zones aren't generally recognized outside of Windows. If you ever expose the user's time zone setting in an API, you'll likely have translation issues for callers from other platforms.
Now, getting to your specific points:
I am using javascript new Date().getTimezoneOffset() method to get the client's offset...
That gives you the client's current offset. You have no guarantees that it is the correct time zone to apply for an arbitrary date and time.
If wanted to apply a fixed offset to a UTC DateTime in C#, the best way is with a DateTimeOffset.
DateTime utc = new DateTime(2016, 12, 31, 0, 0, 0, DateTimeKind.Utc);
DateTimeOffset dto = new DateTimeOffset(utc); // DateTimeKind matters here
TimeSpan offset = TimeSpan.FromMinutes(-300); // The offset is inverse of JavaScript's
DateTimeOffset result = dto.ToOffset(offset);
But do note this is only for a fixed time zone offset. For a true time zone, you would use the TimeZoneInfo class if you're using Windows time zones, or you would use NodaTime's DateTimeZone class for IANA time zones.
In JavaScript I can do new Date().toString() which returns date time as Sun May 22 2016 02:12:36 GMT-0500 (Central Daylight Time) I can then parse the string to get "Central Daylight Time" and post it to the server.
No, this approach is not recommended, for several reasons:
There's no guarantee you will get output in any particular format from JavaScript's toString function. The results are implementation specific, and will vary across browsers and platforms.
They are generally intended for display purposes. When DST is in effect, they'll show a daylight name, and when standard time is in effect they'll show a standard name.
They are often localized for the user's language, English, French, Chinese, etc.
The only native API that can return the user's time zone is:
Intl.DateTimeFormat().resolvedOptions().timeZone
This is part of the ECMAScript Internationalization API. Unfortunately, it currently only works in a handful of browsers. Both jsTimeZoneDetect and moment.tz.guess() will use this API if it's available, then will fall back to their own guessing logic if not.
Assume that i implement timezone selection using one of the approach above and i have correct client's time zone with windows timezone id on server in some variable. Now lets say user selects 6:00 PM (Central Daylight Time , UTC -5) which will convert to UTC as 23:00:00. As long we are in Central Daylight Time the conversion from UTC to local will show 6:00 PM. Once we go into Central Standard Time which is UTC -6 Will the conversion still show 6:00 PM or 5:00 PM?
I am planning to use TimeZoneInfo.ConvertFromUtc(datetimevalue, timezone) method for converting UTC to Local
As you said earlier, "Central Daylight Time" is not a valid Windows time zone identifier. Your user wouldn't pick that. You'd display a list generated from TimeZoneInfo.GetSystemTimeZones(), showing the DisplayName to the user, and using the Id for the value. The Id would be "Central Standard Time", which indeed is the correct identifier for US Central Time, inclusive of both CST and CDT - despite having the word "Standard" in the string.
You need to convert the TimeSpan to a DateTime, using the current Year, Month and Day. If you subtract from a TimeSpan without doing so, it can result in an unobtainable date.
Also, I noticed in your update that you left the results in a DateTime, so I did the same.
This code is showing you the time if the UTC time was 1:00 AM, as your problem states.
double offset = 5.00;
TimeSpan utcTime = new TimeSpan(1,0,0); //setting manually to your representation of 1 am.
DateTime newLocalDateTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, utcTime.Hours, utcTime.Minutes, utcTime.Seconds);
newLocalDateTime = newLocalDateTime.Subtract(TimeSpan.FromHours(offset));

How to find the client machine time zone using ASP.NET

I need to find the time zone of the client machine using ASP.NET (C#) or JavaScript. What are the different time zones that are available all over the world and how to convert the date and time based on the users time zone.
Please provide some suggestions or sample coding to change the time based on the time zone.
You cannot find client time zone settings from ASP.NET.
You can use JavaScript to tell the current time, but there are several time zones that can be synchronized at any given time.
In Chrome, you can get the time zone from the JavaScript date object. There's no specific function for it, from what I've found, but the code
(new Date()).toString()
will yield something like
Mon Apr 18 2011 08:58:59 GMT+0200 (W. Europe Daylight Time)
In websites, the best approach I've found has been to have a setting for each user to specify the time zone to display all times in. If the JavaScript getUtcOffset gives a different offset than what is expected for the user's time zone, I'll show a notice for the user to review their settings. If time zone can be guessed from the date (which I've only found to be the case in chrome), I'll suggest that time zone, but I still resort to a select box for the user to manually pick the time zone.
Even so, it is possible to have the wrong time zone setting, without the script noticing it, because for a great part of the year, the two time zones may be perfectly synchronized.
Once you have a time zone (you can enumerate them all with System.TimeZoneInfo.GetSystemTimeZones()), you can convert UTC dates with System.TimeZoneInfo.ConvertTimeFromUtc and System.TimeZoneInfo.ConverTimeToUtc, respectively.

Categories