Javascript Send Schedule Date From Local Timezone to Server Time - javascript

I am in Hawaii. From Angular, I want to send Pacific Standard Time (PST) to C# Net Core API. It will save it in database and save in Pacific Standard Time. (I didn't create the architecture, just noting).
The whole Angular Application date/times are written in reference of PST.
Example goal is to Schedule Date at 4:30PM PST
We learned a bug today, if we send a date "2021-03-17T16:30:00" , our application creates new Date("2021-03-17T16:30:00") in Hawaii Timezone misinterprets. Then C# probably does the right thing, and converts any time to Pacific Local time (TimeZoneInfo.ConvertTime(dateTime, TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles")). Thus we incorrectly convert to 7:30PM.
Our API Request Data type is in Date. Would this solution work? however, I feel last 4th line will not account for Daylight savings, or can create issue.
const inputDate = "2021-03-17T16:30:00";
const pstMoment = moment.tz(inputDate, 'America/Los_Angeles');
const clientTimeZoneName = Intl.DateTimeFormat().resolvedOptions().timeZone;
const clientMoment = pstMoment.clone().tz(clientTimeZoneName);
console.log(new Date(clientMoment.format())); // this will send to API
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.33/moment-timezone-with-data-10-year-range.js"></script>
Note: Code resolution should not only work in Hawaii, but other global countries which may Not have Daylight Savings Time. Our schedule datepicker utilizes a string storage type (inputDate).
Typescript class
export interface ScheduleDto {
scheduleDate?: Date;
scheduleDate: "2021-03-18T02:30:00.000Z"
C# class
public class ScheduleDto
{
public DateTime? ScheduleDate { get; set; }
....
return TimeZoneInfo.ConvertTime(ScheduleDate , TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles"));

If I understand correctly, the problem is that your current approach on the client-side is just creating a Date object like new Date("2021-03-17T16:30:00"), which is going to use the local time zone to determine the point-in time you're talking about. You want it to always be interpreted as Pacific time, but you don't know which offset to use because of DST.
So then, the ultimate answer to your question is - YES, the code you wrote using Moment-timezone, would indeed apply the correct offset. However, it's a bit verbose. You don't actually need to do anything with regard to detecting the current local time zone. You can instead just build the Date object with:
moment.tz('2021-03-17T16:30:00', 'America/Los_Angeles').toDate()
Given Moment's project status, it would be preferred to use Luxon instead (unless you have already extensively used Moment in your project). In Luxon, that would be:
luxon.DateTime.fromISO('2021-03-17T16:30:00', {zone: 'America/Los_Angeles'}).toJSDate()
Or you could use the zonedTimeToUtc function from date-fns-tz.
The point is, since you're constructing a Date object, you're always sending some string representation of it over the wire. You appear to be serializing those Date objects with .toISOString(), which will send the UTC equivalent "2021-03-18T02:30:00.000Z". Getting that UTC point in time correct is what matters most.
In your .NET code, if you are receiving that value into a DateTime object, then its Kind property will be set to Utc because of the Z at the end of the string. TimeZoneInfo.ConvertTime will use the Kind property to determine the source time zone for the conversion, and you've supplied the destination time zone as Pacific time.
A much simpler approach would be to not use a Date object in your client-side code, but rather you would send the intended date and time over the wire instead, without the offset "2021-03-17T16:30:00". In your .NET code, the DateTime.Kind would be Unspecified. Then you wouldn't call TimeZoneInfo.ConvertTime at all, because you already have the value in the desired time zone.
Alternatives:
For the TypeScript data type, You could use a string.
In the future, when Temporal is finished and fully integrated into ECMAScript, you could use a PlainDateTime.

Related

(Sanity Check) Do I need a timezone library for this simple use case?

This will be my first time working with time zones, I hear this is a major pain point for a lot of developers so I'm asking this question as a sanity check to make sure I'm not missing anything.
My use case is rather "simple", I want to have a date time picker where the user can choose their date and time in their local timezone (in other words what they see in the picker matches what their computer's date and time is set to).
Then I want to take this chosen date and time, convert it to UTC and send it to the server to be saved.
When the user goes to certain pages I take the UTC date/time coming back from the server and convert it to the user's local date/time and display it to them in a user friendly way.
Do I need a library like moment timezone for this or will the browser's native date methods like Intl.DateTimeFormat, new Date().getTimezoneOffset(), etc be enough? (I only need to support the latest modern browsers so I'm asking this from a "does it do what I need" ​point of view not a browser support POV).
It seems all I need are 2 things:
A way to get the user's timezone offset from UTC (so I can convert their local time to UTC to send to the server and also to convert UTC back to their local time to display it to them on certain pages)
A way get their timezone abbreviation (EST, PDT, CDT, etc) to show in the UI
Do I need a library for these? And if not why do people use such large libraries for working with timezones anyway?
You don't need a time zone library for the functionality you mentioned.
// Get a Date object from your date picker, or construct one
const d1 = new Date("2020-08-02T10:00");
// Convert it to a UTC-based ISO 8601 string for your back-end
const utcString = d1.toISOString();
// Later, you can create a new Date object from the UTC string
const d2 = new Date(utcString);
// And you can display it in local time
const s1 = d2.toString();
// Or you can be a bit more precise with output formatting if you like
const s2 = d2.toLocaleString(undefined, {timeZoneName: 'short'});
console.log("UTC: ", utcString);
console.log("Local Time (default string): ", s1);
console.log("Local Time (intl-based string): ", s2);
Keep in mind that not all time zones will have an abbreviation, so those ones will give output like "GMT+3".

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.

Persisting DateTimeOffset to server

I'm trying my best to figure out how to persist a datetimeoffset string to the server so that it correctly binds to a DateTimeOffset property.
I've tried using something like this:
moment.utc().format() + ' +' + new Date().getTimezoneOffset() / 60 + ':00';
This was showing as 6/12/2017 22:27 +4:00 and I was sending that to the server as a string but it seems to fail being parsed to a DateTimeOffset object every time.
I'm using Nancy for my web api framework at the moment, but I wonder the same thing for Web API as well.
Currently I'm just sending back the offset and setting the DateTimeOffset property like this:
dto.CommentDate = new DateTimeOffset(DateTime.UtcNow).ToOffset(new TimeSpan(dto.Offset, 0, 0));
Is there a better way to do this? Can I send the whole date instead of just the offset?
A few things:
+04:00 is an offset used in parts of Russia, and a few places in the Middle-East. From the location mentioned on your blog, I think you meant -04:00, which is used for Eastern Daylight Time in the US (among others). (The JavaScript getTimezoneOffset method is inverted with this regard.)
+ ':00' assumes that all time zone offsets are in terms of whole hours. In reality, many are :30 and a few are :45.
In a DateTimeOffset, the DateTime portion is always the local time, not the UTC time. In other words, the offset is already applied.
For example, if the local time is 2017-06-16 12:00:00 in the US Eastern time zone, then we'd represent that as 2017-06-16T12:00:00-04:00 (in ISO 8601 format). The corresponding UTC time would be represented as 2017-06-16T16:00:00Z.
Sending the UTC time plus the the local offset is not allowed in ISO 8601 format. The only format I'm aware of that uses that convention is the older "ASP.NET JSON Date Format", that came out of things like JavaScriptSerializer class in .NET. The corresponding value to the previous example would be /Date(1497628800000-0400)/. As you can see, the timestamp portion is a millisecond-resolution version of Unix Time, which is UTC based. The offset has not been applied, and is thus extraneous. This format considered deprecated now, so please don't do this.
You appear to be using moment.js on the client side. This can easily generate the string you need to send.
// get the current time, as an ISO 8601 string, with whole-second resolution
moment().format() // "2017-06-16T12:00:00-04:00"
// or, with millisecond resolution
moment().format('YYYY-MM-DD[T]HH:mm:ss.SSSZ') // "2017-06-16T12:00:00.000-04:00"
On the server side, just expose your property as a DateTimeOffset. Don't try to construct it yourself.

Converting moment UTC to Local date time

I put a UTC .NET/JSON date from .net on client side. When I run the following command:
moment(value.Planet.when).utc()
The returned date from webservice:
"/Date(1469271646000)/"
I get a date in the _d parameter showing the current accurate UTC date with GMT+0300 on right side.
I want to convert this time to local time on the user machine and what ever I do, I always get the time 3 hours back.
I do this:
moment(value.Planet.when).local().format('YYYY-MM-DD HH:mm:ss')
and I get the same date time as the UTC. I don't understand how can I get momentjs to show the UTC time relative to the local time. I checked that the momentjs object is indeed UTC.
I thought that if I pass the moment.utc() function the UTC date that I've got from the webservice (originally from the database), I can just run the local() function and I'll get the accurate hour relative to my area, but it didn't work.
You can use moment(date).format('YYYY-MM-DDTHH:mm:ss');
Eg:- if you date "/Date(1469271646000)/"
ip-> moment(1469271646000).format('YYYY-MM-DDTHH:mm:ss');
op-> "2016-07-23T16:30:46"
Do not use the _d property. It is for internal use only. See this answer, the user guide, or Maggie's blog post on the subject.
As far as you question of how to convert to local time, you don't actually need to convert at all. You're already parsing the input value in local mode, so you can just use it directly:
var m = moment("/Date(1469271646000)/"); // gives you a moment object in local mode.
var s = m.format(); // lets you format it as a string. Pass parameters if you like.
var d = m.toDate(); // gives you a Date object if you really need one
Try to avoid using Date objects unless they're required by some other controls or libraries you're using. Most operations can be done strictly on moment objects.

How to work with datetimes betwixt javascript, JSON, and python, and mongodb?

Colleague and I have scabbed together a little app that uses a bunch of JS in the browser, and communicates with a Tornado (Python3) server via JSON, the server uses mongodb as a backing store for persistent data. This is a sort of first for both of us.
What we're finding difficult is how to interchange datetime information between the JS and Python. We do believe that we should use UTC times for everything. JSON doesn't have a datetime literal, so we have to encode it somehow. We naively (?) used the JS notion of milliseconds from 1970 and have been sharing big integers back and forth. So the JS code might get the current utc now time with something like:
var newTime = new Date().getTime();
On the Python3/mongo side, we'd like to use real datetime objects, so we convert that with something like:
datetime.datetime.utcfromtimestamp(jsMilliseconds / 1000)
But then when we have to send date back, said Python3 object only has a timestamp() method. And round tripping that doesn't seem to create the same time. So we've been frustrated with this.
What we're looking for is for someone with experience to give us a good set of idioms to use here. Should we be using strings, instead of the ms integers when passing back and forth with JSON? What are the recommended methods to use on both sides to go between that format? Or should we stick with the integers and which methods should we be using then?
There are obviously a lot of requirements to consider when dealing with time. However, in the case you want to maintain a Date/Time to be displayed to user in their time zone and use mongo/python/java/javascript, I've used ISO 8601 date formats and always store UTC (Zulu) time. Also, if you truly want to maintain the actual time that something occured, not only do you need to store the "date/time" you need to also store the "timezone" (for example IANA timezone string) where the event occurred.
For a lifetime a reading, you can search for "date time best practices". This answer has a good start on discussion: Daylight saving time and time zone best practices
Alright, now to the code (all of this can be found readily on the internet if you search for
" parse||output ISO 8601 parsing" (e.g. "python parse ISO 8601 date string"):
JSON - on the wire between JavaScript and Python backend send a complex (can be simple if you have no need to preserve time zone) object with ISO-8601 formatted string and string to store time zone string:
{
"dateTime": "1970-07-10T12:00:00.047Z",
"timeZone": "America/New_York"
}
Java Script
a. read datetime string JavaScript: Which browsers support parsing of ISO-8601 Date String with Date.parse
var d = Date.parse("2011-04-26T13:16:50Z");
b. write datetime string How do I output an ISO 8601 formatted string in JavaScript?
var date = new Date();
date.toISOString(); //"2011-12-19T15:28:46.493Z"
Python
a. read datetime string How do I translate a ISO 8601 datetime string into a Python datetime object?
import dateutil.parser
yourdate = dateutil.parser.parse(datestring)
b. write datetime string Python - Convert date to ISO 8601
import dateutil.parser as parser
date.isoformat()
Mongo - store datetime as native datetime field http://docs.mongodb.org/manual/reference/mongodb-extended-json/#date
a. store datetime string (notice "$date" and that datetime is in ZULU/UTC (denoted by 'Z')):
"dateTime" : { "$date" : "1970-07-10T13:00:00.047Z"}
Reference:
1. IANA TimeZone Databasehttp://en.wikipedia.org/wiki/Tz_database
2. The google calendar API (event resource) can be used as an example for RFC 3339, as well (see start/end): https://developers.google.com/google-apps/calendar/v3/reference/events

Categories