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

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.

Related

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

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.

Is there a way to override `new Date()` in Javascript so it always returns the date considering one specific hardcoded timezone?

I'm working on a React.js project that handles lots of comparisons related to DateTime (comparing the hour, the month, the year and so forth to dates retrieved from an API). For this specific React.js application, I would like to always consider DateTimes (from new Date()) as if the user was in the server timezone. Assuming that the server is in "Europe/Berlin" timezone, I would like that at any point in the application, calling new Date('2019-01-01') would retrieve me a DateTime that refers to this time in the "Europe/Berlin" timezone, that would be Tue Jan 01 2019 01:00:00 GMT+0100 (Central European Standard Time). If I set my computer Date and Time as if I was in Brazil, for instance, I get Mon Dec 31 2018 22:00:00 GMT-0200 (Brasilia Summer Time), but would like to get the same as before. The main reason why this is a problem is that we extract data from these DateTimes such as .getHours(), .getDate(), etc.
This would fit this specific application because it's really important for this project that we only support the server Timezone, no matter where the user is. So to keep consistent with the server time, the call new Date('2019-01-01').getDate() should return 1, since it will be 1st January in Berlin. However, if the user is in Brazil, this same call will return 31, as Brazil is some hours before GMT, when it's midnight in GMT time, it will be still the previous day in Brazil.
I tried first to use the date-fns along with date-fns-timezone to set the DateTime to display the dates and times to the client considering the server timezone. That worked fine to display the right data but didn't solve some issues that are caused due to these attributes extraction from the date and that will vary depending on where the user is.
So that's why what I'm trying to do now is override the new Date() method in a way that it will always retrieve the date as if the user was in the server time. I haven't managed to get how it can be done. Whenever I change the Date and Time from my computer, the output for the new Date() already takes into account this new date and time settings.
So how can I force the new Date() to always give back the DateTime with a hardcoded timezone? Also, it would be really good if I could do it without using external libs (like moment.js, for instance), and do it only with plain Javascript.
I was taking a look into the window.navigator variable to see if I could set this one to force the Date to the server timezone, but it doesn't look like that will be the way to solve my issue.
I looked a lot for this answer and didn't find any question that was really close to this one. I'll just list here some of the questions I looked before and why my case differs from them.
1- new Date() for a specific timezone in JavaScript: In this case, the moment is used, and there was no way to accomplish this overriding for the new Date() itself.
2- Convert string to date without considering timezone: In this one, the answer gives back a string with the date formatted to the desired timezone, but not a Date object itself.
3- javascript date considering Request.UserLanguages[0]: also in this one, the question/answer is about formatting the date output rather than retrieving the Date object itself with the new timezone.
4- How do I combine moment.js timezone with toDate to build a new date object?: in this one the answer also is about using moment.js, what I would like to avoid since what I really want to achieve is overriding the new Date() method.
A few things:
Generally speaking, one should try to design their applications such that the server's time zone is not relevant at all. This means only relying on the server's UTC functionality, and working with specific named time zones. Asking for the local time of a server tends to become problematic, especially when dealing with environments where you may not have full control of the server.
Keep in mind that the Date object in JavaScript is misnamed. It is really a timestamp. Essentially it is just an object wrapper around the value you get with .valueOf() or .getTime() or when you coerce it to a number. Everything function on the Date object simply reads this value, applies some logic (which may or may not use the local time zone depending on the function), and emits some result. Similarly, the constructors of the Date object interpret your input and then assign this number in the resulting object. In other words, the Date object does not keep track of a time zone at all. It simply applies it when it is called for. Thus, one cannot change it.
When you pass a string in yyyy-MM-dd format to the Date constructor, per the ECMAScript specification it is interpreted as midnight UTC, not as midnight local time. This is a deviation from ISO 8601, and such often confuses people.
The strings you showed as output like Tue Jan 01 2019 01:00:00 GMT+0100 (Central European Standard Time) are emitted in local time, and are the result of either calling the .toString() function, or by passing a Date object to console.log in some environments. Other environments show the result of .toISOString(), which is emitted in UTC.
There's no global in window.navigator or elsewhere that can change the local time zone. In Node.js apps, if you really need to set the server's time zone globally, you can set the TZ environment variable before launching Node. However, this doesn't work in Windows environments, and doesn't help for browsers.
You're on track with using a library such as date-fns with date-fns-timezone. There are other libraries as well, which are listed in this answer. Alternatively, you could call .toLocaleString with the timeZone option, such as:
new Date().toLocaleString("en-US", {timeZone: "America/New_York"})
This API provides functionality for emitting a string in a particular time zone, but it does not do anything for applying a time zone to an input string.
So ultimately, to answer your question:
So how can I force the new Date() to always give back the DateTime with a hardcoded timezone?
Sorry, you can't. You can either use a different object to track the time zone statefully, or you can use functions that take the time zone as a parameter. Both are things offered by existing library, but only the one function I showed above is currently built in to JavaScript.
In case someone else has this same problem, here is the approach that I followed to solve the issue:
As in this specific case I really need to compare and group some entities based on date, among other operations (always considering the server timezone), I ended up choosing moment.js along with moment-timezone. The native Date class would not solve it since it has no way to handle different timezones. Then, I tried to use first date-fns and date-fsn-timezone, but as these libs always use the standard Date class, it gets back to the problem that a Date in javascript is only a timestamp and has no clue about other timezones other than the one in the client (the application is a React.js one).
Just to show one example about the issue, consider that the server is placed in Europe/Berlin timezone, and one stamp is retrieved as 2019-04-30T02:00:00+02:00. On the React.js application, I want to group this stamp to all the stamps that are also from 2019-04-30. However, if the browser is in Brazil/Brasilia time, the call new Date('2019-04-30T02:00:00+02:00').getDate() will give me 29 as return, since in the client timezone, this same timestamp will represent 2019-04-29T09:00:00-03:00.
Using moment and moment-timezone it could be fixed by defining in which timezone I want to retrieve the values, such as:
moment('2019-04-30T02:00:00+02:00').tz('Europe/Berlin').date()
// retrieves 30 independent on the client timezone.
Thank you very much to all who contributed with answers and suggestions. You all helped me to find the path to solve this issue.

Formatting date with JavaScript in a customer fronting e-commerce application

We have a situation where in we need to format a json response attribute which contains a date string (say "2017-01-29"). To format the date we are currently using jquery UI function like:
dayVar = $.datepicker.formatDate('M dd, yy', new Date("2017-01-29"));
But if we print dayVar, it is displayed as Jan 28, 17 as against the expected Jan 29, 17.
What is the best solution to fix this so that it can fit into any time zone?
It is single page application built with Marionette framework.
When instantiating your new Date object, you are leaving the Time up to interpretation. By default most environments will interpret this to 00:00:00 ... however, as this is javascript you are at the mercy of the user's local machine to interpret this value.
I would append the 00:00:00 in the string to the Date() function. to enforce the expected outcome, or even attempt to play with the time to see what kind of outputs it creates (Maybe set it for 1AM or 22:00:00) this should provide a more in depth understanding of what is causing the issue, and hopefully the solution.

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.

Time Zone Sensitive Date and Time Display in Web Applications?

I am looking for recommendations on displaying times in a web application in a time zone other than the user's current time zone.
We store our dates/times in UTC/GMT in the database, so it is not an issue to format the time for UTC/GMT or the user's current time zone. However, in other situations we need to display the time from the point of view of an arbitrary time zone (i.e. every date/time on this page is in Eastern, regardless of whether or not the user is in West Coast, Central, Eastern, etc.).
In the past we have stored offsets or time zone info, then done the calculations in server code in .Net or else we have done some client-side manipulations in javascript that I would prefer to avoid, since it all becomes very dependent on javascript and the user's browser. I'd like to know the best way to do this in a more client-side/MVC type application.
Here is an example:
Date stored in db: 1302790667 (Thu, 14 Apr 2011 14:17:47 GMT)
Converted date displayed for a client in Central time zone: Thu Apr 14 09:17:47 2011
Date I actually want to display, always in Eastern time zone: Thu Apr 14 10:17:47 2011
In the above example, it's easy to get the time in UTC (#1) or the user's current time zone (#2) but it is more difficult to get #3. My options seem to be:
Store offsets or time zones in the db and do calculations on the client - this is what we've done in the past with .Net but it seems even messier in client side code is the path we are currently trying to avoid.
Do the conversion on the server and send down a full date for display to the client - client receives a string ("Thu Apr 14 10:17:47 2011"). This works but it's not very flexible.
Do the conversion on the server, break it into parts and send those down to the client, then put them back together. ("{DayOfWeek:Thu, Month:Apr, Day:14, Hour:10, Minute:17}"). This gives us the correct data and gives us more flexibility in formatting the date but it feels a little wrong for this scenario.
Any other options ideas? How do others handle similar situations? Thanks.
Our results:
I tried out a few libraries like Datejs, MS Ajax, etc. and I was never very happy with them. Datejs didn't work at all in a few of my test cases, is not actively maintained, and seemed to focus a lot on syntactic sugar that we don't need (date.today().first().thursday(), etc.)
We do use jQuery for some basic date/time parsing.
I came across a lot of "roll-your-own" client-side date conversion "hacks", most of which only addressed the conversion to UTC, started off working fine, and then eventually fell apart on some edge case. This one was the 90% solution for a lot of standard UTC conversion but didn't solve our "arbitrary timezone" issue.
Between the code complexity the conversion routines added and the bugs they seemed to cause, we decided to avoid client side date processing most of the time. We do the date conversions on the server with our existing date handling routines and pass the formatted dates or info down as properties to be used by the view. If we need a separate date, we just add another property. There are usually only a few properties that we need at a time (i.e. EventDateUTC, EventDateLocal, EventDateAlwaysAustralia, and EventDayOfWeek).
I offer the suggestion that you look into the Datejs library. It offers a bunch of extensions to basic JavaScript date manipulation, including a "setTimezone()" method and flexible ways to convert a date into a formatted string for display.
I usually hesitate to suggest libraries when their use is not explicitly allowed for in questions, but Datejs isn't very large and it's pretty solid (even though it's called an "alpha" release). If you'd prefer not to rely on something like that, you might want to look at it anyway just to see the basics of how its extensions were implemented.

Categories

Resources