在 Google 日曆排定循環性事件為每月最後一個日曆日或工作日

在 Google 日曆上排定一些事件,並在未來時間提醒自己,讓我在生活上的確輕鬆不少,可以少記一些東西 XD。尤其是那種數週後,甚至是數個月後的事件。

我很經常用到「重複排定」功能,設定一些週期性的事件,非常方便。

Google 日曆內建了數種週期,如果建內建的週期還不夠彈性,也可以自訂適合的週期。要應付日常生活中的事件,絕多數是足夠,但總是有例外的情況。

在此之前,先看一下哪些週期在設定上,是沒有什麼難度的

  1. 每日(星期一到日)或每個工作日(星期一到五)
  2. 每隔 N 周或每 N 月或每隔 N 年的特定日
  3. 每月的第一天(即 1 號)
  4. 每月的第 N 個星期 N
  5. 其他

但有些情況,在日常生活中也可能會遇到,但我們卻只能手動建立,而無法做週期性設定。例如:每月最後一日(可能是 28~31 號)或是每月最後一個工作日(可能是星期一到星期五,同時可能是 26~31 號)

如果這樣的週期,只會持續兩三個月,那麼使用手動建立的方式,不會有什麼問題。但如果把時間拉長到一年或以上,一想到要手動建立 12 個或以上的的週期性事件,真的會讓人很厭世,也難保不會有漏建的情況,而且這些看似具週期性的事件,實際上均屬獨立事件。

類似的情況,匯入 CSV 或是 ICS 檔,我認為會是一個很棒的選擇,這也是 Google 日曆允許匯入的日曆格式1

採用 CSV 檔,相對於在日曆上直接編輯會更來得容易,且理論上會更不容易漏建事件的情況。但缺點就是內容仍然要手動建立(用 Excel 會快一點),而且這些事件各屬獨立事件。

週期性事件,我個人則認為單一事件,而不是各自發生的獨立事件。若是獨立事件的情況,要修/刪未來的事件,就必須逐筆進行。如果週期性事件沒有「截止日」,在將來也就會造成更大的麻煩。

採用 ICS 檔,我認為是更棒的選擇,因為它是標準的日曆資料交換格式(RFC 5545)2,但缺點就是理解它的難度會比理解 CSV 再來得高,編輯的難度也會比較高,主因是要查較多的資料,而不是需要特定的編輯軟體。

但它的優點就是只要新增一筆,就可以產生出 N 個具週期性的單一事件。這對於未來事件的修/刪,會帶來很大的幫助與便利性。

先直接給上一段 ICS 的完整事件內容,它會在 2023 年每月的最後一個日曆日建立一個全天事件。

BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
DTSTART;TZID=Asia/Taipei:20230131
DTEND;TZID=Asia/Taipei:20230131
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=20231231;BYDAY=SU,MO,TU,WE,TH,FR,SA;BYSETPOS=-1
SUMMARY:每月的最後一天
DESCRIPTION:每月的最後一天
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

在 Google 日曆中搜尋關鍵字後的結果,果然都是每月的最後一天

可以達到每月最後一天的週期性事件,最主要的關鍵在於以下這行

RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=20231231;BYDAY=SU,MO,TU,WE,TH,FR,SA;BYSETPOS=-1

RRULE 是用來設定週期性事件的條件,其中 BYSETPOS=-1 是一個靈魂關鍵。我們先逐項看看 RRULE 裡的意義,再回過頭來看 BYSETPOS。

FREQ 是指頻率也可以說是週期的基本單位,它可以是 SECONDLY、MINUTELY、HOURLY、DAILY、WEEKLY、MONTHLY 或 YEARLY。範例就是以月為一個基本單位

INTERVAL 是指頻率間距,要和 FREQ 一起看。範例是 1,就是指每月。若是設為 3 就是指每 3 個月。

UNTIL 是指週期性事件的結束時間。沒有指定,就是沒有結束的時間,會持續不間斷。像範例設定 20231231,就代表 2023 年 12 月 31 就結束了,從 2024 年開始,不會再有相關事件。

BYDAY 指的就是星期一到日這 7 天,也是事件發生的日子。要注意的是星期 N 的英文縮寫是採前 3 個字母,但這裡只採前 2 個字母。因為範例是填上了星期一到星期日,也因此就代表星期一到星期日都會發生。BYDAY 同時也和 FREQ 搭著看。

綜觀至此,我們可以這樣解讀:這個週期性事件,每 1 個月都會發生 1 次。而每 1 次發生的日子會在每 1 週的星期一到星期日,換言之也就是每天都發生。

BYSETPOS 是一個以逗號做分隔且數個日子所組成的集合清單中,去選取指定順序的日子。它可以接受的值是 +1 ~ +366 或者 -366 ~ -1。舉一個簡單的例子:若有一個集合清單是

A,B,C,D,E,F,G

如果 BYSETPOS 是 1,那麼這個值就會是 A,因為 A 在這個集合清單中,是排在第 1 個,若是 4,就會是 D。

而若是負數型態,其實就是從後面數的意思,也因此若是 -1,那麼我們就會得到 G,-3 則是 E

我用一個表格來表達它們,會更容易理解

ABCDEFG
+1+2+3+4+5+6+7
-7-6-5-4-3-2-1

藉由上述例子,若再回過頭來看原本的例子,我們是以每 1 個月為一個週期,且發生於每 1 個月的每 1 天,若以 7 月來講,這個集合清單裡的項,就會是 1 號到 31 號,共 31 個值。所以若是指定為 -1,就會是 7 月 31 號。而若是 2 月的話,則可能會是 2 月 28 日或者 2 月 29 日。

搞懂了 BYSETPOS 的意義,同時也明白如何指定每月最後一個日曆日時,我們就可以用相同的方式達成只選定每月的最後一個工作日。

一般來說工作日泛指星期一到星期五。所以在 RRULE 的部份,我們只要把 BYDAY 的部份,改成星期一到星期五就可以了,而 BYSETPOS 則不變,一樣是 -1,那麼這樣子就會每月最後一個工作日了。

RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=20231231;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1

直接上圖比較最後一個日曆日和工作,可以明顯看出 2023 年有三個月份的工作日和日曆是在不同一天

在 Google 日曆裡,如果週期性事件,固定發生日是不變的,在設定上就非常容易。而若固定發生日,本質具有變動性,例如:固定發生日是在每月最後一個日曆日,但它實際會是在 28~31 號的其中一天,這樣的情況就只能透過匯入 ICS 檔案的方式,來達成目的。

雖然透過 ICS 可以達成我們的目的,但它也不是完美的。目前會發現這些事件在 Google 日曆上的週期性,是不受到支援的,這個問題端看個人如何去看待它。而在我個人而言,它並不算是一個問題,單純只會顯示不支,但在實際上並不影響到我什麼(或我還沒有發現)。

參考資料與其他備註

  1. 將活動匯入 Google 日曆:https://support.google.com/calendar/answer/37118 []
  2. RFC 5545:https://www.rfc-editor.org/rfc/rfc5545 []

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。

這個網站採用 Google reCAPTCHA 保護機制,這項服務遵循 Google 隱私權政策服務條款