一個 ICS 檔是一個用純文字寫出來的行事曆事件。它以電子郵件附件的形式抵達、坐在每一個「加入行事曆」按鈕後面,而且它正是「寄件人用的軟體和你完全不同,但那個會議邀請還是進得了你的行事曆」的原因。
把一個改名成 .txt 打開——內容是讀得懂的,而且人們碰到的幾乎每一個行事曆問題,在裡面都看得見。
BEGIN:VCALENDAR
METHOD:REQUEST
BEGIN:VEVENT
UID:[email protected]
DTSTART;TZID=Asia/Taipei:20261110T140000
DTEND;TZID=Asia/Taipei:20261110T150000
SUMMARY:專案檢討
LOCATION:3 號會議室
END:VEVENT
END:VCALENDAR
一個容器、一個事件、一個開始、一個結束、一個標題。上面有四行做的事比它們看起來多,而每一行都解釋了一個熟悉的失敗。
每個事件帶著一個 UID——一個在那個事件的一生中都不變的唯一識別碼。
當一個會議移動,主辦人的軟體會寄出一個新的 ICS 檔,帶著相同的 UID 和一個更後面的序號。你的行事曆認出它是同一個事件,於是更新既有的那一筆,而不是再加一筆。
這正是重複事件出現的原因。如果一個工具每次匯出都產生一個全新的 UID——有些預約系統和電子報的「加入行事曆」連結就是這樣——那每一次更新都會以一個全新事件的身分抵達,於是你手上有同一個會議的三個副本。那些重複不是你行事曆的 bug;它被告知它們是不同的事件。
METHOD 那一行告訴你的行事曆這是哪一種檔案:
所以當一個邀請以純附件的形式抵達、沒有任何回覆按鈕,它通常是一個 PUBLISH 檔,或者是一個在轉寄過程中失去邀請身分的 REQUEST。接受它會把事件加進你的行事曆,但完全沒告訴主辦人任何事——這就是為什麼有人會在一個他打算出席的會議上,被標記成「尚未回覆」。
ICS 檔裡的時間可以用三種不同的方式寫,而那個差別極為要緊。
DTSTART;TZID=Asia/Taipei:20261110T140000——台北的兩點,對其他所有人都正確換算。這是你要的。DTSTART:20261110T060000Z——結尾那個 Z 代表 UTC,每個行事曆都會正確換算成當地時間。也沒問題。DTSTART:20261110T140000——不管讀的人在哪裡的兩點。對一個個人提醒來說那是對的。對一個跨城市的會議來說那是災難:每個人都看到自己時區的下午兩點,然後沒有人碰得到面。當一個會議落在行事曆上差了好幾小時,幾乎永遠是因為這個。第二種常見變體是從一個自身時區設定就錯了的系統匯出的事件,那種情況下檔案內部是一致的,而且一致地錯誤。
全天事件有它自己的版本。一個正規的全天事件是寫成一個完全不帶時間的日期(DTSTART;VALUE=DATE:20261110)。如果改寫成一個時間——某個時區的午夜——它對任何位置夠東或夠西的人來說,就會移到前一天或後一天。這就是為什麼一個生日有時候會落在錯的日期上。
一個每週會議不是存成五十二個事件。它是一個事件加上一條規則:
RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=52
行事曆從那條規則產生那些場次。另外兩行處理真實行程一定會產生的例外:EXDATE 刪掉其中一次,而一個帶著 RECURRENCE-ID 的獨立事件覆蓋其中一次——那就是「這週的會議改到星期二」而不擾動其餘系列的機制。
各家行事曆程式對這些例外的支援程度不一,這就是為什麼一個被修改過的場次在系列跨系統搬移時,有時候會變回去、或者出現兩次。
這是關於行事曆檔案最有用的一件事,而這兩個選項在你選的當下看起來幾乎一模一樣。
匯入一個 ICS 檔,是把那些事件複製進你的行事曆一次。它們變成你的、你可以編輯它們,而且它們是凍結的。如果來源改變了,什麼都不會發生——你的副本還是原來的樣子。
訂閱一個 ICS 網址則保留一條連結。你的行事曆會定期重新抓那個檔案,來源的改動會出現。你不能編輯那些事件,因為它們其實不是你的。
幾乎每個人都至少中過一次:一份課表、一份賽程表或一份共用的團隊行程被匯入了,然後那個組織改了好幾個日期,而行事曆安安靜靜地繼續顯示舊的。沒有任何東西警告你,因為從行事曆的角度看,什麼問題都沒發生。
規則很簡單。如果來源會持續改變,就訂閱。如果是一次性的事件,就匯入。訂閱連結通常長得像一個以 .ics 結尾的網址,有時候開頭是 webcal://,那是同一個東西,只是指名要給行事曆 App。
一個值得知道的但書:訂閱的行事曆是照那個應用程式自己的排程更新,可能是好幾小時甚至一天。今天早上做的改動,可能明天才會出現。