Obsidian Full Calendar Plugin で Google Calendar を ICS 形式で取り込んだときの Timezone を JST に修正する

# はじめに
普段、Obsidianを使ってタスク管理をしています。
これまでは、Daily Note（Periodic Notes）+ Day Planner を使っていましたが、予定を月表示できる点などに惹かれ、Full Calendar プラグインを試しています。

ここで、Google CalendarをICS形式で取り込んだときに、時刻同期がうまくいかない（JSTに合わない）問題がありました。
GitHubのIssuesに解決方法がまとめてありましたが、少し躓いたのでJST用にまとめておきます。

# 環境
- Obsidian v1.7.6
- Full Calendar 0.10.7

# リンク
- [GitHub - obsidian-community/obsidian-full-calendar: Keep events and manage your calendar alongside all your other notes in your Obsidian Vault.](https://github.com/obsidian-community/obsidian-full-calendar)
- [ICS events appear in the wrong timezone · Issue #311 · obsidian-community/obsidian-full-calendar · GitHub](https://github.com/obsidian-community/obsidian-full-calendar/issues/311)

# やり方
1. ~/.obsidian/plugins/obsidian-full-calendar/main.js　を開く
1. function getDate2 を検索する
1. function getDate2, function getTime3 は書き換えない
1.  function getTime3 をコピーしてその下などにペーストする
1. ペーストしたものを以下のようなfunction getTime4に変更する
    ```diff_javascript
    function getTime3(t3) {
      if (t3.isDate) {
        return "00:00";
      }
      return DateTime2.fromSeconds(t3.toUnixTime(), { zone: "UTC" }).toISOTime({
        includeOffset: false,
        includePrefix: false,
        suppressMilliseconds: true,
        suppressSeconds: true
      });
    }
    + // setting for JST(UTC+9)
    + // https://github.com/obsidian-community/obsidian-full-calendar/issues/311
    + function getTime4(t3) {
    +   if (t3.isDate) {
    +     return "00:00";
    +   }
    +   return DateTime2.fromSeconds(t3.toUnixTime(), { zone: "UTC+9" }).toISOTime({
    +     includeOffset: false,
    +     includePrefix: false,
    +     suppressMilliseconds: true,
    +     suppressSeconds: true
    +   });
    + }
    ```
1. その下にある、function icsToOFC を書き換える
    ```diff_javascript
    function icsToOFC(input) {
      if (input.isRecurring()) {
        const rrule = rrulestr(input.component.getFirstProperty("rrule").getFirstValue().toString());
        const allDay = input.startDate.isDate;
        const exdates = input.component.getAllProperties("exdate").map((exdateProp) => {
          const exdate = exdateProp.getFirstValue();
          return getDate2(exdate);
        });
        return __spreadValues({
          type: "rrule",
          title: input.summary,
          id: `ics::${input.uid}::${getDate2(input.startDate)}::recurring`,
          rrule: rrule.toString(),
          skipDates: exdates,
          startDate: getDate2(input.startDate.convertToZone(import_ical.default.Timezone.utcTimezone))
        }, allDay ? { allDay: true } : {
          allDay: false,
          startTime: getTime3(input.startDate.convertToZone(import_ical.default.Timezone.utcTimezone)),
          endTime: getTime3(input.endDate.convertToZone(import_ical.default.Timezone.utcTimezone))
        });
      } else {
        const date = getDate2(input.startDate);
        const endDate = specifiesEnd2(input) && input.endDate ? getDate2(input.endDate) : void 0;
        const allDay = input.startDate.isDate;
        return __spreadValues({
          type: "single",
          id: `ics::${input.uid}::${date}::single`,
          title: input.summary,
          date,
          endDate: date !== endDate ? endDate : void 0
        }, allDay ? { allDay: true } : {
          allDay: false,
    +     // startTime: getTime3(input.startDate),
    +     // endTime: getTime3(input.endDate)
    +     startTime: getTime4(input.startDate),
    +     endTime: getTime4(input.endDate)
        });
      }
    }
    ```
1. Obsidianを再起動すると反映されているはずです。

# まとめ
他に良い方法等を知っている方がいれば教えてください。
変更した部分をコピーできるように下に貼っておきます。

<details>

```javascript
// src/calendars/parsing/ics.ts
var import_ical = __toModule(require_ical());
function getDate2(t3) {
  return DateTime2.fromSeconds(t3.toUnixTime(), { zone: "UTC" }).toISODate();
}
function getTime3(t3) {
  if (t3.isDate) {
    return "00:00";
  }
  return DateTime2.fromSeconds(t3.toUnixTime(), { zone: "UTC" }).toISOTime({
    includeOffset: false,
    includePrefix: false,
    suppressMilliseconds: true,
    suppressSeconds: true
  });
}
//setting for JST(UTC+9)
//https://github.com/obsidian-community/obsidian-full-calendar/issues/311
function getTime4(t3) {
  if (t3.isDate) {
    return "00:00";
  }
  return DateTime2.fromSeconds(t3.toUnixTime(), { zone: "UTC+9" }).toISOTime({
    includeOffset: false,
    includePrefix: false,
    suppressMilliseconds: true,
    suppressSeconds: true
  });
}
function specifiesEnd2(iCalEvent) {
  return Boolean(iCalEvent.component.getFirstProperty("dtend")) || Boolean(iCalEvent.component.getFirstProperty("duration"));
}
function icsToOFC(input) {
  if (input.isRecurring()) {
    const rrule = rrulestr(input.component.getFirstProperty("rrule").getFirstValue().toString());
    const allDay = input.startDate.isDate;
    const exdates = input.component.getAllProperties("exdate").map((exdateProp) => {
      const exdate = exdateProp.getFirstValue();
      return getDate2(exdate);
    });
    return __spreadValues({
      type: "rrule",
      title: input.summary,
      id: `ics::${input.uid}::${getDate2(input.startDate)}::recurring`,
      rrule: rrule.toString(),
      skipDates: exdates,
      startDate: getDate2(input.startDate.convertToZone(import_ical.default.Timezone.utcTimezone))
    }, allDay ? { allDay: true } : {
      allDay: false,
      startTime: getTime3(input.startDate.convertToZone(import_ical.default.Timezone.utcTimezone)),
      endTime: getTime3(input.endDate.convertToZone(import_ical.default.Timezone.utcTimezone))
    });
  } else {
    const date = getDate2(input.startDate);
    const endDate = specifiesEnd2(input) && input.endDate ? getDate2(input.endDate) : void 0;
    const allDay = input.startDate.isDate;
    return __spreadValues({
      type: "single",
      id: `ics::${input.uid}::${date}::single`,
      title: input.summary,
      date,
      endDate: date !== endDate ? endDate : void 0
    }, allDay ? { allDay: true } : {
      allDay: false,
      // startTime: getTime3(input.startDate),
      // endTime: getTime3(input.endDate)
      startTime: getTime4(input.startDate),
      endTime: getTime4(input.endDate)
    });
  }
}
```
</details>