codebude / QRCoder

A pure C# Open Source QR Code implementation
MIT License
4.46k stars 1.09k forks source link

Time Zone in Calendar events #458

Closed 5fq closed 3 months ago

5fq commented 1 year ago

Type of issue: Request for new feature/improvement

It will be good if can have an option to define the time zone as well while creating the calender event.

5fq commented 1 year ago

https://github.com/codebude/QRCoder/wiki/Advanced-usage---Payload-generators#34-calendar-events-icalvevent

codebude commented 3 months ago

Does anybody know what to do here to add timezones to an iCal event? Is it still true that one has to define a complete VTIMEZONE block with all it's parameters or does iCal meanwhile support passing just timezone ids? I can't quite make sense out of the documentation: https://icalendar.org/iCalendar-RFC-5545/3-6-5-time-zone-component.html

But if we really have to define the VTIMEZONE component, I guess it will be a bigger task. (At least if we implement it without any 3rd party libraries, which is what I prefer.)

@Shane32 Are you by any chance familiar with iCal (events) and time zones?

Shane32 commented 3 months ago

I’ve looked at iCal briefly in the past but not much; something I always wanted to do but never did. Perhaps I can poke around with it this weekend and see if I can get anywhere.

Shane32 commented 3 months ago

Looks like the easiest answer is to store the date-time value in UTC vs local time by simply converting date-time values to UTC and adding Z as a suffix. This doesn't really make the iCal event time-zone aware, but at least it will be converted to the local user's time zone upon reading the event.

https://icalendar.org/iCalendar-RFC-5545/3-3-5-date-time.html

Alternatively the time zone could be specified, but I'm not sure what time zone values are allowed. Linux machines could likely use the TimeZoneInfo.Id property but Windows machines' Id property returns a completely different set of values. I don't think it's in the interest of this library to contain or maintain a bunch of time zone information.

https://icalendar.org/iCalendar-RFC-5545/3-8-3-1-time-zone-identifier.html

Sample provided at the first webpage above:

 DTSTART:19970714T133000                             ; Local time
 DTSTART:19970714T173000Z                            ; UTC time
 DTSTART;TZID=America/New_York:19970714T133000       ; Local time and time zone reference
codebude commented 3 months ago

Thanks for your investigation @Shane32 !

Looks like the easiest answer is to store the date-time value in UTC [...] at least it will be converted to the local user's time zone upon reading the event.

But only for users who run QRCoder locally. Wherever QRCoder is used on the server side, confusion can arise if the local time zone of the server does not match that of the user. Nor does it solve the actual time zone problem. If you create an appointment with a date in winter time during summer time, it would still be imported incorrectly into the calendar because the time changeover is not taken into account. I think for these two reasons UTC is not a significant improvement.

[...] but I'm not sure what time zone values are allowed. Linux machines could likely use the TimeZoneInfo.Id property but Windows machines' Id property returns a completely different set of values [...]

That's the point where I'm also unsure. But I've just looked through the documentary again and I think it's a bit misleading. It mentions timezone identifiers such as America/New_York, but I think I have understood that these have no reference to timezone objects in operating systems/software/real world, but refer exclusively to timezones defined in the calendar. Here only a tzid name was chosen, which looks like generally known time zones.

I believe this is the case, as the section on TZID states the following:

This property specifies the text value that uniquely identifies the "VTIMEZONE" calendar component in the scope of an iCalendar object.

For me, this means that the TZID id is only a reference to a VTIMEZONE component, which must also be defined in iCal.

I don't think it's in the interest of this library to contain or maintain a bunch of time zone information.

Yes and no. I agree that I too would find it unattractive to lug around a huge list of timezone info in the code (to avoid external dependencies), but if it can be implemented with a manageable amount of code, I would still try it. (We also have other payload generators that contain generator-specific logic and sometimes static data). For example, if it is possible to generate a VTIMEZONE component from a TimeZoneInfo (standard .NET) without having to store static information in time zones but solely retrieving all information from the TimeZoneInfo, then I would consider that acceptable.

Shane32 commented 3 months ago

But only for users who run QRCoder locally. Wherever QRCoder is used on the server side, confusion can arise if the local time zone of the server does not match that of the user.

Sure, but you could argue that the burden of this responsibility (client-server communication of time zone information) is on the developer, not this library. In other words, if you always transmit dates and times from the server to the client in the client's local time without time zone information, then creating an iCal in "local time" makes the most sense because the server has no knowledge of what is the correct time zone. On the other hand, if UTC is used, then the iCal should be in UTC format. The only question arises when the client sends the server a DateTime value in the client's local time, separately transmitting or specifying the time zone.

Let's take the easy cases first. If there's no time zone information, the server should create a DateTime instance with the DateTime.Kind property set to DateTimeKind.Unspecified and the iCal event should be generated with no time zone information.

Secondly, if the value is transmitted in UTC, the server should create a DateTime instance with the DateTime.Kind property set to DateTimeKind.Utc and the iCal event should be generated in UTC.

Third, if the value is transmitted with an offset, the server should create a DateTimeOffset instance. However, this does not store what specific time zone, but merely the local time and offset. Attempting to determine the user's time zone would be a guess, so I would assume that an iCal should be created based on the UTC equivalent of the DateTimeOffset instance.

The only difficult question is what to do when passed a DateTime value in the client's local time (which should have DateTime.Kind equal to DateTimeKind.Unspecified) along with a separately stored time zone value. Although this could be stored in various ways, let's assume the user has a TimeZoneInfo instance representing the client's time zone. We have two options:

  1. We can use the .NET libraries to accurately convert the date/time value into UTC time based on the provided time and time zone info, generating a iCal event in UTC time. (Note: .NET will properly account for the date when computing the offset to UTC for time zones with daylight saving time.)

  2. We can write the time zone id into the iCal file (somehow).

I'd opt for option 1 above. It's easy and always accurate. But the second option seems to me to only have limited benefits. Sure if the developer did not have the selected time zone coerced into a TimeZoneInfo instance. But how likely is that?

In other words, if the developer had their client pick "Eastern Time Zone" from a dropdown, wouldn't the rest of their application need to know how to translate times into that time zone? Surely the only reason they would provide a time zone selection option is to convert times throughout their application between the server and client's time zones. As such, it would be easy, no matter the mechanism, to do the same for the purposes of generating a QR code in an iCal format from the server.

The only other benefit is that the device reading the QR code could display the original time and time zone of the event, while also knowing how to translate it to the device's local time.

For example, if it is possible to generate a VTIMEZONE component from a TimeZoneInfo ... then I would consider that acceptable.

Sure, I agree. But just storing the UTC time of the event seems to be an acceptable alternative to me if there is a lack of expertise to write such an implementation.

Shane32 commented 3 months ago

I would propose two immediate changes and one future change:

  1. Any DateTime value passed to the iCal payload generator that has a DateTime.Kind value of DateTimeKind.Utc will write in UTC format (with the 'Z' suffix)

  2. An overload is provided that accepts DateTimeOffset values, which call the existing overload using the .UtcDateTime property of the DateTimeOffset values (which will write in UTC format).

For backwards compatibility, I would not convert DateTimeKind.Local values to UTC.

Proposed future feature: add overload with DateTime and TimeZoneInfo which will write the TZID / VTIMEZONE / etc properly.