I am trying to create a scheduling application. The front end\UI is developed using JavaScript. The back end is a ASP.NET Web Api application which uses MSSQL server as the database. From the UI, user will schedule a job which can run daily/weekly/monthly. Each job can run for maximum of 3 months. The job will run on the server side at the specified time.
Assume user come and selects a job which will run for a week (From 23-Nov to 29-Nov) at 10 AM local time. In this case, I will make seven entries in the database starting from 23 nov (One for each day). Each row will have Start time, Start Date and some status related columns.
I have following querstions:
How do I store time information (10 AM in this case)on SQL server?
Should I get the time using JavaScript on client machine and then convert the same to UTC?
Should I get the time using JavaScript and also save the user time zone information?
What happens when DST related changes take effect?
Will library like momemnt.js will help in this scenario?
I am thinking of saving user timezone information and the saving his local time on the server.
Warning - Scheduling properly is hard. There's a lot more to consider. Please read this and this. Most of your questions are addressed there (though from the perspective of other languages, the challenges are the same).
You might also take a look at Quartz.net, which is sufficient for many scenarios.
To answer your specific questions:
How do I store time information (10 AM in this case) on SQL server?
For the recurrence rule, store the local time of the event. SQL Server has a time type, which works well for storing the time of day. You will need other fields for tracking the time zone, the start date, days of the week, and other pattern information.
For the specific instance that is scheduled to run, you calculate the UTC datetime based on all the information in the recurrence rule. At minimum, you schedule the next occurrence, and recalculate after each run. In some cases, you may decide to project the next N occurrences, depending on what you need to show to the users. (You could also use a datetimeoffset for this purpose. See datetime vs datetimeoffset.)
Should I get the time using JavaScript on client machine and then convert the same to UTC?
Should I get the time using JavaScript and also save the user time zone information?
To answer both questions: For scheduling, you should not discard the original input, which will be in the local time zone of the event being scheduled. That may or may not match the time zone of the user. You will need to ask the user to select the time zone of the event.
What happens when DST related changes take effect?
That's up to you. You will need to test this thoroughly. In general, there is a period of local time that is skipped, and a period of local time that is repeated.
When it is skipped, you have to decide when to run the event. Options include: 1) before the skipped time, 2) after the skipped time, and 3) not at all. In most cases, the preferred option is to run after the skipped time, by advancing the local time by the DST bias (usually 1 hour). For example, a daily event scheduled to run at 2:30 every day in Pacific time would run at 3:30 on the day of the spring-forward transition.
When it is repeated, you have to decide when to run the event. Options include: 1) at the first occurrence, 2) at the second occurrence, and 3) at both occurrences. In most cases, the preferred option is to run at the first occurrence only. For example, a daily event scheduled to run at 1:30 every day in Pacific time would run at 1:30 PDT, and not at 1:30 PST.
Exceptions to this include dealing with businesses that are open late into the evening and choose to stay open for the repeated hour. For example, a bar, restaurant, or movie theater. It is highly dependent on the specific use case and the choices made by the specific business.
Will library like moment.js will help in this scenario?
Not from a scheduling perspective, no. It can help with parsing, formatting, and validating input though. You might also use moment-timezone to help with selecting the event's time zone. If you were running this with node.js on the back end, then perhaps there would be more benefit.
The biggest challenge is actually one you have not talked about, which is maintaining the time zone data on your server. In your C# code, I recommend using Noda Time for this, instead of TimeZoneInfo. You can then update the tzdb data yourself as needed. You also need to think about the workflow of rescheduling the UTC instants of each occurrence, in the case that a time zone has changed its offset or daylight saving time dates.
Related
I have a .NET web site that captures a C# DateTime value and stores it in database with UTC value. Upon showing that datetime value on web page, what I want is that date time UTC value to be converted to web user's local time zone properly. So web users in different time zones will see the displayed date time value with different date time results.
I have 2 following approaches. I do not know which is the best and correct one.
Approach 1: Let C# server side codes to convert the UTC DateTime value to local date time using .NET library method DateTime.ToLocalTime() and pass that converted value to client side to display without any further client side codes.
Approach 2: I do not do any time conversion using C# server side codes. Instead, I simply pass the UTC datetime value to client side and I will use some client side (javaScript or some JS library) codes to do the conversion before displaying it to web user.
Question 1: Approach 1 or Approach 2 is the best and correct one?
Question 2: The .NET library DateTime.ToLocalTime() method will convert a datetime value to local time of web user; web server's local time; OR web user's computer OS time?
Question 3: How can I test my web page with different time zones to see the best/correct approach working?
Thank you for your help.
I suggest that all date/times be stored in UTC on the server and that, in your case, you do the web client timezone conversion on the client side. I suggest the Moment library for client-side operations. If you use Moment, look at the guess() method for determining the client's current timezone.
By the way, you might want to determine the client's current timezone periodically in case 1) the user in on a mobile device and physically moves to another timezone or, 2) the user's session lasts long enough to transition from Daylight Savings Time (DST) to Standard Time (STD) or vice versa.
Now, I said "in you case" because, based on your question you don't seem to need to worry about calculating time intervals between 2 date/times. If you need to do that you also have to consider the complication of changes in a timezone from 3 sources:
Changing from STD to DST (and vice versa) that occurs twice a year in many timezones.
Changes to the UTC offset of a timezone that sometimes happens when a government implements a change.
Changes to the date/time when a location converts from STD to DST or vice versa.
Also, you might need to consider the complication of dealing with the potential for a local time corresponding to two UTC times! This can happen when a local time for a location falls within the 1 hour preceeding the date/time when that location converts from DST to STD. That 1 hour period is repeated when a 'fallback' from DST to STD time occurs and so you need to resolve which of 2 possible UTC times you want to store in your database.
If any of the above timezone complications might exist for you, consider storing a location along with the date/time since this context is required for timezone calculations. The location can be lat/lon which you would need to map to a timezone or you could keep it simple and store just the timezone. Lat/lon is sometimes more desirable since sometimes governments decide to move a location into a new timezone (but this is pretty rare).
I am setting up a webapp for a business which takes future bookings which can also be recurring. At this moment, only the business can make the bookings.
Business rules:
The app must save a timezone against the user based on the business address provided at account registration. All events will be taking place at the business address. UPDATE: following comments, this rule will be changed to: Timezone will be recommended to the user user using a geocoding service (in the instance that a match is found), but user must confirm their timezone.
When logging in from another timezone, the webapp app booking system should still appear to the user to be set to the business timezone. (Potential scenario for this is the user is at a meeting abroad, but still wants to check up on their bookings). This should mean that they can still use the system as before eg. by adding new bookings. However, I believe this would mean the system has to ignore the user's local timezone, and apply the business timezone to any booking events.
I have the booking system working for a user that is within the same timezone. But when they change timezones the future bookings calendar breaks.
My app is nodeJS with postgresql. What I am confused about is what I should be persisting with regards to datetimes, and what relevant conversions to perform.
I think it would be prudent to save the client locale datetime always (even if not used as requirements may change), and also the UTC time. But I think I will have to also construct a date (in javascript) that takes the datetime selected and applies the business timezone to this (using eg. moment.tz.setDefault("America/New_York")?). I am unsure what to do about receiving this date on the client when viewing the scheduling information. How do I ensure that it displays with the business timezone?
You should always store the time zone that is relevant to the event. Since you said "All events will be taking place at the business address", then the time zone of the business is relevant.
If these are physical in-person events, then the user's current local time zone isn't relevant at all. For example, I would expect that if I made a dentist appointment in Los Angeles, that I would specify the time in Los Angeles even if I was making the appointment from New York.
However, if the events are virtual online events, then you may also want to add some logic in the UX to show both the time in the target time zone and the equivalent local time. For example, if I'm scheduling a video call next week with a person in Los Angeles, and I happen to be in New York today, you have no idea whether I will still be in New York at the time of the call or if I'll have travelled to Los Angeles by then. You should allow me to choose which time zone I'm creating the appointment in, and show both the time there and my equivalent local time.
The time zone itself should be stored as a varchar(14). IANA 2017c established a 14 character limit for individual segments of zone or link identifiers, removing the only link name that exceeded it, Canada/East-Saskatchewan. (If you have older data that includes that, replace it with America/Regina.)
For a single appointment, you should store the date and time of the appointment, in the time zone of the appointment. You should store this in a data type that holds the date and time without any time zone or offset. timestamp without time zone in postgres, datetime2 in MS Sql Server, etc.
You might think you should use a timestamp with timezone (or datetimeoffset type in SQL Server), but that would pin the appointment to the equivalent universal time, using the rules applicable at the time the appointment was scheduled. If those rules change before the appointment comes into effect, then the appointment will shift to a different local time. This is usually undesired. The point is to capture the intent of the user. In other words, if I say "8am on December 1st", then I mean that regardless of what my government does to my time zone between now and my appointment time.
You might also store the UTC time equivalent in a separate field, but you'd need to recompute it any time the server's time zone data was updated. (Such a field can be convenient to quickly query for upcoming events.)
For a recurring appointment, all the above still applies except that you'll need to store some form of recurrence rule. Some people like to store many fields for various components of the rule (such as one field for "daily", one field for "on Wednesdays", one field for the time of day, etc...). You can use the date and time types in your database if you like. Other people like to store a string with a chron expression. It really just depends on your needs.
It's especially important with recurring appointments to consider that the UTC equivalent for each occurrence will not necessarily be the same. Consider that many time zones alternate their UTC offsets based on whether daylight saving time is in effect. For example, if I'm creating a daily appointment in Los Angeles at 10 am, that would be 6 pm UTC during Pacific Standard Time, and 5 pm UTC during Pacific Daylight Time. Thus, you cannot just store the UTC time. Again, capturing the intent of the user is the most important part.
See the title: for the solution I'm working on, I need to get the current timezone offset (from the client, running javascript/jQuery) and use it in backend C# code.
The question is rather similar to the one asked here, but there are a few differences - the main one being that I am pretty sure that the time on the client computer won't be tampered with. So new Date().getTimezoneOffset() will do just fine.
I cannot read the value upon submitting a form since the user is not working in a form: after the user has logged in, among the items that are visible on the screen is a table with data entered by the user or by other users. This data contains UTC datetimes that have to be adjusted according to the client's timezone. C# code is responsible for retrieving and formatting the data - hence my question.
What would suffice, is storing the value somewhere so that C# can read it when necessary. But I don't think that can be done as well. What would be the approach here?
Thanks in advance!
Your suggested approach is flawed in that the current offset from the client's browser is only going to apply to the current date and time. In reality, time zone offsets change over time within a given time zone. You cannot just take a singular offset from one point in time and expect to use it to convert other dates and times to the same time zone. Instead, you need to use the string that identifies the time zone, not an offset from that zone.
As an example, consider the Eastern time zone in the United States. For part of the year, it uses UTC-5, and we call it Eastern Standard Time (EST). In another other part of the year, it uses UTC-4, and we call it Eastern Daylight Time (EDT). This time zone is identified by either the IANA time zone ID "America/New_York", or the Windows time zone ID "Eastern Standard Time" (which covers the entire zone, both EST and EDT despite its wording).
So, break this problem apart into a few steps:
In JavaScript, identify the users's IANA time zone (America/New_York):
If you are targeting modern web browsers, you can call this function:
Intl.DateTimeFormat().resolvedOptions().timeZone
If you need to support older web browsers, you can use jsTimeZoneDetect, or moment.tz.guess() from Moment-Timezone.
Send that string to your web server through whatever mechinsm you like (form post, XHR, fetch, etc.)
In your .NET code, receive that string and use it to reference the time zone and do the conversion. You have two options:
You can use Noda Time, passing the IANA time zone ID to DateTimeZoneProviders.Tzdb as shown in the example on the home page.
You can use .NET's built-in TimeZoneInfo object. If you're running .NET Core on non-Windows systems (Linux, OSX, etc.) you can just pass the IANA time zone ID to TimeZoneInfo.FindSystemTimeZoneById. If you are on Windows, you'll need to first convert it to a Windows time zone ID ("Eastern Standard Time"). You can use TZConvert.GetTimeZoneInfo from my TimeZoneConverter library.
Once you have either a DateTimeZone from Noda Time, or a TimeZoneInfo object, you can use the methods on it to convert UTC values to local time values for that time zone. Each of these will apply the correct offset for the point in time being converted.
I'll also say, many applications simply ask the user to choose their time zone from a dropdown list and save it in a user profile. As long as you're storing a time zone identifier string and not just a numeric offset, this approach is perfectly acceptable to replace steps 1 and 2 above.
Let's say I have a user of my application set a recurring event. His timezone is America/Denver, so I store that along with an rrule that determines when the recurrences happen.
This works assuming that both my server and all of my users are in the same time zone. However, let's say I have other users, in America/Pheonix and maybe America/New_York who wants to get the occurances of this event the user has defined. I need to be able to create the events using the America/Denver time, but then return them to the user in UTC. Conversely, I also need to calculate recurring events that users in America/New_York defined and return those as UTC to the user.
Is there a library that exists that I can give it a timezone and an rrule and have it generate the recurring events based on that timezone's rules (like respecting DST)? Or maybe a third party API?
EDIT: Here is some clarity to my problem.
Let's say I have a recurring event that occurs every Friday at 9am. If I set this event in Colorado, the times that this event occurs are going to be slightly different than times in Arizona, which doesn't have DST for the 5 months of the year that Colorado does. So in my database, I need to store the time zone, and I need some way to generate the events based on that time zone's rules. This is the part I am stuck on, finding a way to generate the events based on the time zone's rules.
I need some way to generate the events based on that time zone's rules
No, you don't. The "time zone's rules" are irrelevant to the generation of occurrences. An event at "every Friday at 9am" is going to be at 9am all year round, whether DST is active or not. In other words, you can (and should) completely ignore DST for the rrule part.
The only thing you need to do is keep the original time zone along with each occurrence - a date and a time is not enough - then simply convert the date/time to another time zone on the fly, when you need to display it to a different user.
I have used Moment.js and Moment Timezone to work with dates before, but you probably have other options out there.
I know that new Date().getTimezoneOffset() accounts for the local user's timezone & DST, but how to calculate the current local time in say Timbuktu for an arbitrary time in the future?
JavaScript does not have a timezone database built in to the language (as can be seen by perusing the spec). Instead it only has the concept of "local time" and "UTC time," with there being only one local timezone.
It is possible there is a browser API for getting a timezone database, but I have never heard of it. There are some third-party projects that interface with such databases; a quick search turns up timezone-js.
I doubt there is any algorithm that could give you what you want. The nations of the world often choose not to follow the standard time zone rules. Worse the changeover dates for DST are different.
There are 3 sources that I can think of to check:
Jeppesen, a subsidiary of Boeing, is the primarily civilian supplier for the entire world for aeronautical navigation information. I just checked my old Jeppesen manual that I ceased getting weekly updates for when I retired 13 years ago. It gives the time zone for all major airports in the world and the dates of their DST change. Go here for contact info. I would suspect that by now it's conveniently available electronically, but Jeppesen doesn't give anything away for free.
ICAO, the International Civil Aviation Organization (part of the UN now I think) would I think maintain that information, but I'm not so sure about the DST data. Their data, I believe, is available either free or for a nominal charge.
IATA, the International Air Transport Association, might also maintain that data.
I would think that the last two organizations might respond favorably to a simple request for a URL with that data if they know of one.
Also, if can get ahold of any airline dispatcher working international flights, they would have to have that information available.
Also, check this and others you'll see if you Google "airline time zone and dst data."
The easy part is accounting for timezones. You can find the default (+0) timezone by just subtracting timezone*60*60 seconds from the date, then adding the new timezone*60*60 to the date.
Then it's just a matter of making the time you want, using:
d = new Date(year, month, day, hours, minutes, seconds, milliseconds)