What is this about
I'm developing a web application in NodeJS to display information about the agenda of the users which are synchronized with the central server of my University. For that, I wrote a script that downloads an ICS file every hour to update my local agenda (stored in an SQLite database).
What seems to be the problem
Someone reported to me that some events in their agenda aren't synchronized properly with the University's version, the start time differs. Here's the problem. I have an Event A and an Event B which are both displayed as starting at 6am UTC on the university web page. But, when I retrieve them from the server in iCal format, they are represented as such (I've removed the properties unrelated to my issue) :
DTSTART:20211108T070000Z <-- starts at 7am UTC
DTEND:20211108T083000Z
SUMMARY:Event A
LOCATION:A room
DESCRIPTION:A description
DTSTART:20211025T060000Z <-- starts at 6am UTC
DTEND:20211025T073000Z
SUMMARY:Event B
LOCATION:A room
DESCRIPTION:A description
What I already tried
I've already tried using an open source application (named OpenWebCalendar) that displays iCal events to a calendar from a URL and it displays just like the University agenda. Both *Event A and B start at 6am UTC.
Unfortunately, I couldn't find how the app did it be looking into its source code.
So this problem doesn't come from the University ical file I retrieve.
I've tried looking into the iCalendar specification document to no avail. I also couldn't find any evidence that this was related to a timezone issue as all datetime values set in the ical file are in UTC format.
So, no problem with the Timezones and I can't find a property or specification that would explain this difference and how to fix it.
I've also written a test script in NodeJS in order to retrieve info about the iCal to no avail using node-ical. The start time displayed when retrieving the events are the same as in the file which doesn't help at all.
Here's the redacted code if you are interested :
const ical = require('node-ical')
let body = `
BEGIN:VCALENDAR
METHOD:REQUEST
PRODID:-//SomeAppName/version 1.0
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
DTSTAMP:20211010T100331Z
DTSTART:20211108T070000Z
DTEND:20211108T083000Z
SUMMARY:Event A
LOCATION:A room
DESCRIPTION:A description
UID:UniqueCodeA
CREATED:19700101T000000Z
LAST-MODIFIED:20211010T100331Z
SEQUENCE:2128343411
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20211010T100331Z
DTSTART:20211025T060000Z
DTEND:20211025T073000Z
SUMMARY:Event B
DESCRIPTION:A description
UID:UniqueCodeB
CREATED:19700101T000000Z
LAST-MODIFIED:20211010T100331Z
SEQUENCE:2128343411
END:VEVENT
END:VCALENDAR
`
ical.async.parseICS(body, function(err, events){
console.log(events["UniqueCodeA"])
console.log(events["UniqueCodeB"])
})
So, my question is
Is there any iCal specification, properties or any explanation as to why both iCal start times diverge and how to correct them just like OpenWebCalendar does it ?
Welcome to the joys of daylight saving changes and timezones. Somewhere there someone is viewing dates with a timezone set as most people do, this is expected. There are 2 dates there, and it appears one is one side of a daylight saving change and the other is on the other side.
Unlike UTC some timezones have daylight saving and of course timezones don't change from/to daylight saving at the same time, so lots potential for multiple time differences to appear to be inconsistent across events.
If we take London as an example, on the 8 of November 2021, london time is same as UTC time:
https://www.timeanddate.com/worldclock/meetingtime.html?day=8&month=11&year=2021&p1=195&p2=136&iv=0
However on 25 October 2021, at 6am UTC time, London is 7am.
https://www.timeanddate.com/worldclock/meetingtime.html?iso=20211025&p2=195&p3=136
Always check what timezones are set on every device and calendar app and be alert around daylight saving changes.
Related
I have a list of deadlines for various projects. Once every 3 months, I download the next 3 months' worth of deadlines as ICS files and add them to my calendar. Because I only download them once every 3 months, I am experiencing an issue with regards to daylight saving time (DST). Here is an example of the problem:
In January, I download the upcoming deadlines for February, March, and April, setting the time as 9:00 AM on their respective dates.
Come March 13th (the start of DST), the deadlines start to appear as 8:00 AM because when I downloaded them, it was not DST, so when we "sprung forward," what used to be 9:00 AM is now 8:00 AM, and the files are not compensating for that.
I have had trouble determining where the best way to solve this problem would be:
In the configuration of the ICS file itself. I imagine that if there was a place to do this, it would be here. I have scoured the documentation of ICS files and have yet to find something that would auto-compensate for DST in the future.
In JavaScript on the front-end. I know that JavaScript has the option of setting the timezone to a region ("America/New_York") instead of a time zone, which helps it auto-compensate for DST, but I have not been able to get that feature to work on a date in the future, as it only references the current time.
In PostgreSQL on the back-end. I have tried various versions of AT TIME ZONE to no avail. I encounter the same problem I did in JavaScript: It sets the time in the specified time zone without taking into account whether or not the date would be affected by DST in the future.
Update to Post to Include More Information
The deadline information (PROJECT_NAME and DUE_DATE) is extracted from a PostgreSQL database. The due date is stored as a DATE value like this: 05-MAR-22.
To add the time, I have taken two approaches:
Adding the time in PostgreSQL like this:
select ('06-JUN-2022'::date + time '9:00') at time zone 'America/New_York'
Adding the time in JavaScript like this:
deadline.DUE_DATE = deadline.DUE_DATE.split("T"[0] += " 09:00:00";
But neither method has helped me solve the timezone issue.
The JavaScript microservice sets the following fields:
BEGIN:VEVENT
UID:[ UID ]
SUMMARY:[ PROJECT_NAME ]
DTSTAMP:[ DTSTAMP ]
DTSTART:[ DTSTART ]
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER:[ TRIGGER ]
END:VALARM
DURATION:PT
END:VEVENT
which seems to be missing a few important details for configuring the timezone.
You have the option of embedding time zone information in the ICS file by utilizing the TZID parameter and VTIMEZONE component of ICS files. The VTIMEZONE component requires adding information about DST rules to the file, and each unique TZID must have a corresponding VTIMEZONE component. You can use tzurl.org which has generated VTIMEZONE definitions for every time zone. For more information about how to do this, you can refer to these related questions:
How to Specify Timezone in ics File which will work efficiently with google,outlook and apple?
What's VTIMEZONE used for in icalendar? Why not just UTC time?
I am lacking some knowledge of the specific implementation details of Microsoft Outlook and Apple calendar, but I'm pretty sure that they would take such relevant information from the ICS file into account.
Look at how you are generating the ICS files with Javascript. It sounds like the timezone is being set to the pre-daylight-savings timezone, which will cause problems when viewing the event after the daylight savings shift. According to the ics specification:
The use of local time in a DATE-TIME or TIME value without the "TZID" property parameter is to be interpreted as floating time, regardless of the existence of "VTIMEZONE" calendar components in the iCalendar object.
This may also point to the possibility that the timezone is being set incorrectly, because if the "TZID"/"VTIMEZONE" components were not defined, the events should be regarded as "floating", or "the same hour, minute, and second value regardless of which time zone is currently being observed."
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.)
I don't know of a simple way to describe this, so this description will be kind of long. Apologies beforehand.
Okay, imagine you live Chicago and you own a business. You write a web app such that you can make an online schedule for your employees that they can check to see when they work each week. Ie, you select a date (in this instance with Angular-UI Bootstrap Datepicker), then you pick to pick a time (using Timepicker) and set an employee to work at that time.
Now, one week you need to go out to California for a business trip. However, you still need to make the schedule out for the next week while you're out of town. Because javascript dates always adjust to local time, if you put "Jake works at 8 pm" on the schedule while you're in California when Jake logs in to look at it, he will see that he works at 10 pm because the 8 pm local California time is 10 pm local CST time.
So, you want all dates and times chosen in your schedule maker to always been in your "home" time zone. IE, no matter where you are, if you set "Jake works at 8 pm", it will always show up as 8 pm CST.
Okay, that's the basic setup. I have all of that working using a little bit of a hacky workaround server side after the "Jake works at 8 pm" message gets sent over. I pull the month, date, year, hours, and minutes out of the sent time, use moment-timezone.js to make a new moment with that date/time but in my "home" time zone, then I convert that new moment into UTC and save it to the DB.
So, back to the client side. I have 3 pages. "New Schedule", which sends the schedule off to the server where it does all the transformation stuff. Then a "View Schedule" page, where I get all of those UTC times back and display them in my 'home' timezone using
Date.toLocalString('en-US', {set of locale options including timeZone: 'America\Chicago'})
So, my "New Schedule" and "View Schedule" pages are working fine. All of the times get remade as a CST time using the exact input time, then converted to UTC. Then the display view outputs them as the correct timezone.
Now, my problem is the last page. My "Edit Schedule" page for making changes to a schedule that's already been saved.
I can't figure out a way to re-adjust my times after they come back from the server to the edit page. So, in this instance, I originally put in "Jake works at 8 pm (and I was in PST when I made it)", which gets converted to "Jake works at 8 pm CST", which gets converted to "Jake works at 15:00 UTC" and saved. Now, my 'View Schedule' page pulls that information, when I load that into the model for my Timepicker, it takes that 15:00 UTC and loads the time picker with 6 pm PST because I'm still in Cali while trying to edit the schedule.
I can't figure out a good way to deal with this. Or maybe there's a better time picker widget I could use that lets you set a default timezone from which it parses the ng-model Date object? I don't know, any ideas would be appreciated!
So to clarify why I want to do this: I need to send push notifications to users when it's 7pm in their timezone.
For each registered device, I have a timezone string, like "Europe/Paris".
I'm creating a background job which will run every hour. It should fetch the list of users for which it's 7pm, and send them a notification.
So the question I'd like to answer is:
"Where in the world is it 7pm now"
Edit The important thing is to get the timezone, even if it's not formatted like "Europe/Paris", I can do that conversion manually with an array.
There's no built-in support in Javascript for converting the "standard" timezone names (e.g. Europe/London) to timezone offsets.
You mention push notifications so that suggests you're not running in a browser. If you're using Node.js there's a good library I've used called timezone which uses a local set of timezone spec files to handle conversion between timezones.
Note that timezone specs do sometimes change, for example when a national government decides with little notice that they're not doing daylight savings this year. It's vital that your local mappings are kept up to date accordingly.
There may be an easier way to do this, but have a database of users, who have a timezone. Then have a hour difference between your notification server, and that time zone. So your server is in pacific time, and the timezone is eastern, it will have a difference of 3.
Use the difference of the time you want, and what it is on your server, to determine the timezone. You want 16:xx it is 13:xx you get +3 hour difference, use that to look up the time zone, then push to all users associated with that time zone.
There may be an easier way, but that way is real simple solution with a db and a little sql knowledge.
EDIT: Also if you dont mind the time delay of using a web api, you should check this out: https://developers.google.com/maps/documentation/timezone/
Whoops numbers were off with the time-zones, Thanks Trent
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.