How to reject non UTC/GMT dates from client in C#? - javascript

I want to force clients (web, android, ios) to send the API only time in UTC/GMT.
This means that they get user's local time (using any method), convert it to UTC/GMT, and then send it to the API.
And I want to reject any datetime parameter that is not in UTC/GMT.
In JavaScript, I can get UTC this way:
new Date().toUTCString() which gives this result:
'Mon, 15 Nov 2021 04:26:38 GMT'
And I send this string to the API:
[HttpGet]
public object Parse(string clientDateTime)
{
var date = DateTime.Parse(clientDateTime);
return new
{
ParsedDate = date,
Kind = date.Kind.ToString()
};
}
However, I see that .NET parses this date time as Local and not as Utc. This is in spite of the string containing GMT.
How can I check the incoming datetime and make sure it's UTC/GMT?

You can greatly simplify your problem by using a conventional format for datetime serialization. A common choice for this problem is using the ISO 8601 datetime format.
Here you can find an in depth explanation of this format, but as an example this is a datetime in the ISO 8601 format: 2021-11-15T06:40:48.204Z (the final Z indicates that the datetime represented by this string is UTC)
The main advantage in fixing a format for date and times is that you will know in advance the format and you will be in a much better position to parse the datetime strings on the server.
Using the ISO 8601 format is a good choice, because it is a well known format and it is the standard de facto for the datetime serialization: this basically means that anyone writing a client for your application will be able to comply with the required format. Of course, you are required to clearly document this convention so that your clients (or your fellow developers) will be aware of it.
Another tip is using the DateTimeOffset struct instead of DateTime. DateTimeOffset is basically used to represent a specific point in time and that's exactly what you want: your clients will send you ISO 8601 strings representing a point in time and you want to know that point in time in your application.
Your clients will be able to use any time zone to express the point in time they want to send to your application. Doing this using an UTC datetime or any other time zone is just an implementation detail. Once you have parsed the datetime string to a DateTimeOffset instance, if you really want to, you can check whether it is an UTC time by checking the Offset property: it will be a zero TimeSpan value if the DateTimeOffset instance represents an UTC date and time.
In order to manipulate date in a Javascript client application I strongly suggest to use the Moment.js library. Check this docs to see how to get an ISO 8601 string with Moment.js
You can use this helper method to parse an ISO 8601 string to a DateTimeOffset instance. This implementation allows the client to send you a broad range of ISO 8601 compliant string formats, if you want you can be stricter by reducing the number of allowed formats (see the Iso8601Formats static field in the code).
To summarize:
ask your clients to only send you datetime strings in a format compliant with the ISO8601 specification. Clearly document this choice
for a Javascript client use a library like Moment.js to manipulate date and times. This will be much simpler than using plain old javascript Date objects.
if you are manipulating date time strings representing a specific point in time, use the DateTimeOffset struct instead of the DateTime struct. DateTimeOffset represents a specific point in time expressed in a certain time zone. The Offset property represents the difference between the point in time represented by the DateTimeOffset instance and UTC: its value will be a zero TimeSpan if the DateTimeOffset instance represents an UTC datetime. Notice that the point in time will always be the same regardless the time zone it is referring to, so using UTC doesn't make any real difference (it's just an implementation detail at this point).
use code like this one to parse a DateTimeOffset instance from a string. This code tries as many ISO 8601 compliant formats as possible (this is done in order to accept as many valid formats as possible). If you want, you can decide to be stricter: to do that, just reduce the number of formats in the Iso8601Formats array.
A final note on your code. The behaviour you are observing from DateTime.Parse is exactly the expected one. Check the documentation for DateTime.Parse:
Converts the string representation of a date and time to its DateTime
equivalent by using the conventions of the current thread culture.
DateTime.Parse is basically designed to use the locale settings of the machine running the code.
If you want to learn more on the difference between DateTime and DateTimeOffset you can check this stackoverflow question.

Related

How to stop conversion of the timezone on new date object in javascript lwc?

I receive a datetime string with different timezones (for example '2020-10-28T08:00:00+10:00, 2020-10-28T11:00:00+11:00'). When I create new date object, it converts the string to the current User timezone, displaying different date and time on UI rather than showing what is being received. How to stop timezone conversion?
If LWC uses the JavaScript Date object, then sorry - but there's nothing you can do. The behavior you described is how the Date object works.
The Date object internally tracks only a timestamp which is the number of milliseconds since the Unix epoch: 1970-01-01T00:00:00.000Z (UTC). If you parse a string with an offset, that offset is considered when determining the timestamp. If you create a string with toString(), it uses the system local time zone to convert the timestamp to a string. The original string you parsed, or its offset, is not retained in the Date object.
I don't know much about LWC, but in general you can only solve this by not using a Date object, and using something else instead. There are many good JavaScript date libraries to pick from, such as Luxon.

Handling date objects javascript vs java

I have an date input field. The user inputs a date then submits and this date is saved in the database. I have two options on how to do things and I know how to implement both but I'm not too sure what the best practice is as I'm a noob.
Option 1: Take the value read from the date input which is a string and send that to my Spring back-end and then create a date object.
Option 2: Take the value read from the date input and create a Date object in JavaScript and send a date object to my spring controller.
I'm leaning towards option 1 because I'm thinking I will have better error handling in my controller?
Your Question is moot as you will be sending a string of a date back to the server either way.
ISO 8601
Generally best to use the standard ISO 8601 formats when exchanging date-time values as text. For a date that would be YYYY-MM-DD.
java.time
As for performing date-time related work on that date value, I recommend using Java for that. Java offers the industry-leading date-time framework found in the java.time classes, defined by JSR 310. See Oracle Tutorial.
The java.time classes conveniently use the ISO 8601 formats by default when parsing/generating date-time strings.
LocalDate ld = LocalDate.parse( "2019-01-23" ) ; // Parses standard ISO 8601 strings.
String output = ld.toString() ; // Generates "2019-01-23".

Javascript convert string to date, but ignore the timezone that is assumed it's in

A really long title, I know, but I had to highlight the fact, that I'm confronting a situation that's a little different than all the usual javascript date conversions.
I am getting the following datetime in a string from the server:
2017-05-18T08:00:00
When I put this string into the following statement:
var newDate = new Date("2017-05-18T08:00:00");
It assumes it's in the UTC timezone, so it automatically adjusts, and converts it into local time, which in Sidney would become 2017/05/18 18:00:00.
Any way that I can stop the date constructor to assume that the string is UTC time (make it assume that it's local time)?
use getTimezoneOffset() function to adjust timezone. By default Date converts it to local timezone :(
If you're applying your code in serious applications, consider a tool like Moment.js

new Date('yyyy-MM-ddTHH:mm:ss') without locale

I am based in the UK so when I attempt to parse a new date in JavaScript as follows:
new Date('2016-06-03T09:05:15');
Results in the following date:
Fri Jun 03 2016 10:05:15 GMT+0100 (BST)
I want the date to be parsed as is, and for no locale adjustments (in this instance, BST) to occur. Is this achievable without writing my own date/time parser?
I want the date to be parsed as is, and for no locale adjustments (in this instance, BST) to occur
That is exactly what should occur, however it doesn't in all browsers. You should not parse strings using the Date constructor or Date.parse (they are equivalent for parsing). Always manually parse strings, a library can help but usually isn't necessary.
According to EMCAScript 2015, '2016-06-03T09:05:15' should be parsed as a "local" date (i.e. based on the host system settings for date, time and time zone on the date and time supplied). A Date object's time value is UTC, so when creating the time value, the host settings are taken into consideration. The same settings are also used for output, so if the OP string is correctly parsed and then written to output, you should get back exactly the same date and time (though probably in a different format).
If you're seeing a different time from the input string, then the string isn't being correctly parsed (hence advice to manually parse strings).
Is this achievable without writing my own date/time parser?
Yes, use a library that someone else wrote. However, if you also want host settings to be ignored for output, you'll also need to write your own formatter, or use a library.
The good news is that there are many to choose from.

MomentJs output Date of toDate() is incorrect

I've started using momentJs in an Angular/Typescript project. (Included incase it's relevant in any way although I very much doubt it)
In the run method of my module I call
moment.locale(window.navigator.language);
which correctly sets the locale to en-GB in my instance. Further down the line I use moment to parse a GB time.
when doing the following:
var mom = moment("24/11/2015 00:00:00");
for example. This populates a new moment object using the defaults set on the moment global (If i understand how it should work correctly). moms date is set to 2016-12-11T00:00:00.000Z. This clearly means it's parsed the given string in en-US instead of en-GB which was set via Locale in a default setting prior to this call. Is there anything I've missed in configuration/setup of moment which would make this not work?
I've also inspected the _locale property of my variable. mom._locale is set to en-gb and I can see the L,LL,LLL etc etc formats are all en-GB formatted values (as they should be).
running mom.toDate(); unsurprizingly returns the 2016 date stored internally by the moment object.
Some misc information I forgot to include:
I am using the latest release of momentjs from NuGet (Version 2.10.6 at time of writing) and I've included moment-with-locales.js in my HTML
Using any recent version of MomentJS, you should see why in the console:
Deprecation warning: moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.
Unless you specify a format string, MomentJS relies on the Date object's parsing, and unfortunately, regardless of locale the Date object will, with a string using /, assume U.S. format. One of the many, many things that aren't quite right with Date.
You'll need to use a format string, or supply the string in the simplified ISO-8601 format used by Date. From Parse > String:
When creating a moment from a string, we first check if the string matches known ISO 8601 formats, then fall back to new Date(string) if a known format is not found.
var day = moment("1995-12-25");
Warning: Browser support for parsing strings is inconsistent. Because there is no specification on which formats should be supported, what works in some browsers will not work in other browsers.
For consistent results parsing anything other than ISO 8601 strings, you should use String + Format.
So I got around this by fetching the locale data from moment and just passing it into the format parameter. Considering the example input of "24/11/2015 00:00:00" I would structure my format as below:
var format = moment.localeData().longDateFormat('L') + " " + moment.localeData().longDateFormat("LTS");
this generates the format mask of "DD/MM/YYYY HH:mm:ss".
You can mix and match whatever formats you want and this will be locale specific to whatever you set moment.locale("") to be (presuming you have the locale information setup in moment already)
This is a crazy workaround and I'm surprised that moment doesn't presume locale information as default when parsing. TJCrowder has raised an issue on Github with the moment guys which I suggest anyone who cares should comment on. https://github.com/moment/moment/issues/2770
You're probably better off passing the format to moment directly and validating the string before hand. This will ultimately reduce the amount of debugging you'll need to do and get you up and running straight away.
var mom = moment("24/11/2015 00:00:00", "DD/MM/YYYY HH:mm:ss");
You could try the new(ish) Intl API but browser support is limited (IE11+), so I would recommend having a user select the month in a dropdown or something to force them to input a certain way.

Categories

Resources