4/12/2019

MS Graph, Events, and Time Zones in SPFx

Working with time zones is always a mess, whatever language, technology stack or environment you're in. There are always different issues on how to convert dates, get correct time zone and so on.
Currently I'm working on SPFx solution that should create an event in Outlook calendar.
User should be able to select date, time, and time zone of the event.
Time zone selection dropdown should display friendly names like that:

And the dropdown initial selection should be based on browser's (computer) time zone.
Below I'm providing some thoughts on the implementation, unfortunately without final solution for now. All the thoughts are based on the SPFx stack.

Implementation Steps

So, what should be done?
  • Create date time picker to select the date and time of the event.
    This step is simple, and we can use PnP Reusable Controls for that
  • Create time zones dropdown and display friendly time zones names.
    Not an issue as well. We can use /me/outlook/supportedTimezones MS Graph endpoint to get all user's time zones:
    GET https://graph.microsoft.com/v1.0/me/outlook/supportedTimezones
    
    The result will look like that:
  • Set time zones dropdown's selected item to current time zone.
    This is a step with problems, and I will describe them below.
  • Create new event using data entered by a user.
    Complicated step as well. But if we have current browser's time zone (and/or it's offset - which is always available), and new selected time zone with its offset - we can convert the date and time to needed time zone to send to the server using Create Event method of MS Graph:
    POST /me/events
    {
      "subject": "Let's go for lunch",
      "body": {
        "contentType": "HTML",
        "content": "Does late morning work for you?"
      },
      "start": {
          "dateTime": "2017-04-15T12:00:00",
          "timeZone": "Pacific Standard Time"
      },
      "end": {
          "dateTime": "2017-04-15T14:00:00",
          "timeZone": "Pacific Standard Time"
      },
      "location":{
          "displayName":"Harry's Bar"
      },
      "attendees": [
        {
          "emailAddress": {
            "address":"samanthab@contoso.onmicrosoft.com",
            "name": "Samantha Booth"
          },
          "type": "required"
        }
      ]
    }
    
Below I want to describe all the problems that I experienced while working on the project. Once again, I don't have "good" final solution as of now.

Problem #1 - IE 11

Browsers contain Intl APIs that allow you to get current time zone:
const timeZone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
It will return a string like America/Los_Angeles, which is an Internet Assigned Numbers Authority time zone database code, or IANA code (source, Wikipedia).
It allows you to correctly define user's time zone and even location (more or less). The problem with it is you need to map it with user-friendly display name that is added to time zones dropdown. See Problem #2 - IANA-Windows Time Zones Mapping.
Everything is good, but in IE 11 Intl.DateTimeFormat().resolvedOptions().timeZone is undefined...
OK, in that case we can get time zone offset from a Date object:
new Date().getTimezoneOffset()
It will give us the offset in minutes (for example, my current time zone is Pacific Standard with daylight offset and I will get 420 as a result). The problem with that approach is there is no information about location, and, as a result, we'll lose information if it's really a Pacific Daylight, or Arizona time zone (Arizona is UTC-7 without daylight saving).
Another option here is to get time zone setting from user's Outlook Calendar. It might differ from current time zone while traveling, but in most cases will be accurate.
We can do that using user's mailboxSettings resource from MS Graph:
GET https://graph.microsoft.com/v1.0/me/mailboxSettings
There response will contain timeZone property with a string like "Pacific Standard Time" that can be used to initialize selected item of time zones dropdown. No need for additional mapping.

Problem #2 - IANA-Windows Time Zones Mapping

This problem is related to the fact that in time zones dropdown we want to show user-friendly time zones (called Windows time zones), and browser's time zone is stored as IANA time zone code. And we need to map these two to select correct item in the dropdown based on browser's time zone.
Unfortunately, there is no one-to-one mapping between Windows and IANA time zones: one Windows time zone contains multiple IANA time zones.
Another problem here is that time zones and daylight saving rules are changed relevantly often, so, if you don't want to support your code forever, you don't want to hardcode the mapping.
First source I was looking into to solve the mapping issue was MS Graph. Mentioned earlier /me/outlook/supportedTimezones endpoint allows us to get time zones in Windows format, as well as in IANA format, if we add TimeZoneStandard=microsoft.graph.timeZoneStandard'Iana' parameter:
POST https://graph.microsoft.com/v1.0/me/outlook/supportedTimezones(TimeZoneStandard=microsoft.graph.timeZoneStandard'Iana')
The problem here is that both responses that they return a list of timeZoneInformation entities. And these entities contain alias and displayName properties only. No offset or some other parameter that could be used to map items from these 2 lists.
For example, for Pacific Standard Time in Windows list I'll get
{
  "alias": "Pacific Standard Time",
  "displayName": "(UTC-08:00) Pacific Time (US & Canada)"
}
And for IANA list I'll get
{
  "alias": "America/Los_Angeles",
  "displayName": "America/Los_Angeles"
},
{
 "alias": "PST8PDT",
 "displayName": "PST8PDT"
},
{
 "alias": "Canada/Pacific",
 "displayName": "Canada/Pacific"
}
... and some others.
As you can see there is no way to map these values one to another.

NOTE: I've posted a MS Graph UserVoice idea to provide some kind of mapping in the results. So, if you feel like this will solve your pain, please, go and vote for it.

So, after failure with MS Graph I was looking for some existing free APIs/endpoints to get this mapping. And, unfortunately, I was unable to find any... Remark here is that I was looking for commercial usage, there are some free resources for personal usage, but not for commercial.
The only more or less interesting resource I found is windowsZones.xml hosted by Unicode.org. They update it more or less regularly, so it can be used to get the mapping.
The problem here is that while implementing logic to get that file (with caching of course) from the url above, parse it and return in needed format, I experienced 503s multiple times...
So, I decided, that I can't rely on that.

As of now, the only solution here from my point of view is
  • Get either this XML, or full IANA database, and copy it to your custom location
  • Implement REST API (or any other API) to get mapping in needed format
  • Worst part: do not forget to update the file/database if a new version was released
It's not so bad... But if we could have this mapping in MS Graph... :)

Conclusion

Time zones conversion itself is difficult in implementation. But this part is mostly solved with such libraries as moment-timezones.
Unfortunately, there are other problems that we can face while working on projects with dates.
Two of them are listed above: IE 11 doesn't show current time zone info, and there is no reliable easy-to-support solution for Windows-IANA time zones mapping.
Please, vote for MS Graph UserVoice idea to solve at least the second problem with out-of-the-box Office 365 implementation.

That's all for today!
Have fun!

2 comments:

  1. Get more than $916.25 worth of authority advancement materials including two complimentary months of that one of a kind framework today as a feature of Kevin Eikenberry's Most Remarkable Free Leadership Gift Ever at Cerys Matthews

    ReplyDelete