Skip to content

Latest commit

 

History

History
91 lines (70 loc) · 4.64 KB

README.md

File metadata and controls

91 lines (70 loc) · 4.64 KB

s.containers/ms-ics-fix

Why?

In the vast sea of tools from the company that brought us VSCode and GitHub, there lurks Microsoft 365/Outlook—a product seemingly riddled with peculiarities. Among its quirks is the notoriously broken calendar export feature, an issue that has persisted for years, frustrating users and developers alike.

Understanding the Problem

Microsoft Outlook exports calendar events in ICS format, which, quite ironically, often fail to adhere to the very standards intended to ensure compatibility and functionality across calendar applications. Specifically, the exports often mishandle timezones in a way that violates RFC 5545

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Microsoft Corporation//Outlook 16.0 MIMEDIR//EN
METHOD:PUBLISH
BEGIN:VTIMEZONE
TZID:Romance Standard Time
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:UTC
# ...
END:VTIMEZONE
BEGIN:VEVENT
# Event uses a different timezone, not properly linked to any defined VTIMEZONE
DTSTART;TZID=Central Europe Standard Time:20240425T113000 # Uses TZID without proper global reference,
DTEND;TZID=Central Europe Standard Time:20240425T120000   # likely to fail in non-Microsoft apps
# ...
END:VEVENT
END:VCALENDAR

And this is not a one time issue, see:

To fix this, this tool will work like an ICS proxy, which will re- parse the ICS file.

Environment Variables

Name Type Default Value Description
ICS_ON_DEMAND boolean false Whether to enable the on demand mode.
ICS_{counter}_URL string null The URL of the ICS file to serve.
ICS_{counter}_PATH string? null The path to serve the ICS file.
REPLACE_{counter}_FROM string null The string to replace in the ICS file.
REPLACE_{counter}_TO string null The string to replace the string from with.

On Demand Mode

If ICS_ON_DEMAND is set to true, you can make a GET request to / with the query parameter url.

/?url=https%3A%2F%2Fexample.com%2Fcalendar.ics

This will return the ICS file from https://example.com/calendar.ics.

Don't forget to URL encode the query parameter.

Example

version: "3"
services:
  app:
    image: ghcr.io/scolastico-dev/s.containers/ms-ics-fix:latest
    ports:
      - "3000:3000"
    environment:
      ICS_0_URL: "https://example.com/calendar1.ics"
      ICS_0_PATH: "your-new-filename-which-will-be-served.ics"
      ICS_1_URL: "https://example.com/calendar2.ics"
      # ...
      REPLACE_0_FROM: "Romance Standard Time"
      REPLACE_0_TO: "Europe/Paris"
      # ...

This will result in to paths being published:

  • GET /your-new-filename-which-will-be-served.ics -> https://example.com/calendar1.ics
  • GET /0.ics -> https://example.com/calendar2.ics

If no path is specified, the tool will try to use {counter}.ics, starting with 0 and incrementing by 1 for each new ICS file. If another file blocks the counter, the tool will skip the counter and try the next one.