◉RORK LABEN
●EXPO — EAS Observe がネイティブクラッシュも記録(10/07)。SDK 57 は 57.0.21 以降で確認できます●SDK 58 — SDK 58 Beta は 9/15 公開。stable の日付はまだ未確認です●RN 0.88 — React Native 0.88.x の正式リリース予定は 10/12、残り3日●Q&A — expo-widgets が本番ビルドだけ真っ黒になる、という問いが出ています●RORK — 09-29 に GPT-6.1 Sol を追加。Pro・Max プランで利用できます●NEW — クライアントのアプリを作る前に決める、公開アカウントの持ち主●EXPO — EAS Observe がネイティブクラッシュも記録(10/07)。SDK 57 は 57.0.21 以降で確認できます●SDK 58 — SDK 58 Beta は 9/15 公開。stable の日付はまだ未確認です●RN 0.88 — React Native 0.88.x の正式リリース予定は 10/12、残り3日●Q&A — expo-widgets が本番ビルドだけ真っ黒になる、という問いが出ています●RORK — 09-29 に GPT-6.1 Sol を追加。Pro・Max プランで利用できます●NEW — クライアントのアプリを作る前に決める、公開アカウントの持ち主
記事一覧/開発ツール
⬡ 開発ツール/2026-06-25上級

海外に行くと毎朝の通知が9時に来ない — Expoの定時ローカル通知をタイムゾーンと夏時間に強くする設計

Rork(Expo)で作った定時リマインダーが、出張やサマータイムで現地の9時に届かなくなる問題。timeIntervalトリガーの落とし穴と、ローカル時刻基準で再スケジュールする設計を実装コード付きで整理します。

Rork577expo-notifications7タイムゾーンローカル通知3React Native238DST

✦ プレミアム記事

毎朝「今日の一枚」を届ける壁紙アプリで、ある利用者から「ヨーロッパに来たら通知が夕方に届く」という報告をもらったことがあります。日本では問題なく朝9時に出ていたので、最初はサーバー側の話かと疑いました。けれども原因はもっと手前、ローカル通知のトリガーの組み方にありました。

個人開発で癒し・引き寄せ系のアプリを6本ほど並行運用していると、こうした「自分の端末では再現しないバグ」が一番厄介です。定時通知の時刻ずれは、まさにその典型でした。ここでは、Expo のローカル通知がタイムゾーンと夏時間(DST)でずれる仕組みを切り分け、現地時刻に追従させる再スケジュール設計を、私自身が実際に直した手順に沿って実装コードとともに整理します。

なぜ「現地の9時」に届かなくなるのか

ずれの正体は、トリガーが「絶対的な瞬間」を指しているか「現地のカレンダー上の時刻」を指しているかの違いです。expo-notifications のトリガーは大きく3系統あります。

トリガー種別指す対象時差・DSTで
timeInterval(n秒後)絶対的な瞬間(スケジュール時点から固定秒数)ずれる
date(固定のDate)絶対的な瞬間(UTC上の一点)ずれる
daily / calendar(hour, minute)端末の現地カレンダー上の時刻追従する

問題が起きやすいのは、timeInterval で「次の朝9時までの秒数」を計算して渡すパターンです。これは一見正しく見えますが、スケジュールした瞬間に「○○秒後」という絶対的な約束に変換されます。利用者が9時間進んだタイムゾーンへ移動しても、約束された瞬間は動きません。結果、現地では夕方に鳴る、というわけです。

固定の Date を渡すパターンも同じ理屈でずれます。new Date(2026, 5, 26, 9, 0) は端末の現在オフセットでUTCの一点に焼き付けられるため、移動後にそのまま発火すると現地の時計とは合いません。

私が踏んだのはこの両方でした。日替わりで通知文を変えたかったので、あえてカレンダートリガーではなく日付を1件ずつ計算して積んでいたのです。文面の出し分けと引き換えに、時差耐性を失っていました。

まず基本:現地時刻に追従させたいだけなら DAILY を使う

毎日同じ文面・同じ現地時刻でよいなら、答えはシンプルです。SchedulableTriggerInputTypes.DAILY を使えば、OS が現地カレンダーで再評価してくれるため、時差移動も夏時間の切り替えも自動で吸収されます。

import * as Notifications from 'expo-notifications';
 
export async function scheduleDailyReminder(hour: number, minute: number) {
  // 既存の定時枠を一旦消してから積み直す(重複防止)
  await Notifications.cancelAllScheduledNotificationsAsync();
 
  await Notifications.scheduleNotificationAsync({
    content: {
      title: '今日の一枚が届きました',
      body: 'ホーム画面の気分を、静かに変えてみませんか。',
    },
    trigger: {
      type: Notifications.SchedulableTriggerInputTypes.DAILY,
      hour,   // 端末の現地時刻で解釈される
      minute,
    },
  });
}

iOS では内部的に UNCalendarNotificationTrigger、Android では繰り返しアラームに対応づきます。どちらも「現地の hour:minute」という意味なので、東京で設定して9時間進んだ地域へ移っても、現地の9時に鳴ります。timeInterval から DAILY に置き換えるだけで直る場合が、実は一番多いです。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦timeInterval/Date トリガーが時差・夏時間でずれる理由と、DAILYカレンダートリガーが現地時刻に追従する仕組みの切り分け
✦意図した「現地9:00」を保持し、フォアグラウンド復帰時にタイムゾーン変化を検知して全件キャンセル&再スケジュールする実装
✦春の時刻飛び(2:30が存在しない日)と64件上限の回転枠を両立させる、次回発火時刻の安全な計算ロジック
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ 開発ツール2026-09-04
EAS の secret は「アプリに入れない」設定ではありません — 接頭辞と可視性を別々に決める
EXPO_PUBLIC_ の接頭辞は成果物に入るかを決め、EAS の可視性は誰が読めるかを決めます。二つを重ねたときに OTA 更新でだけ値が消える筋道と、出荷前に自分の手で確かめる手順をまとめました。
⬡ 開発ツール2026-08-22
一括置換は全ファイル成功しました。壊れたのは、消さなかった行のほうです
生成コードに一括置換をかけると、一致した行ではなく隣の行が壊れます。運用中のプロジェクトで実際に起きた破損と、置換の不変条件を検査する20行のガードスクリプトをまとめました。
⬡ 開発ツール2026-08-14
Expo SDK 57 に上げる前に、prebuild で消えるネイティブ変更を洗い出す
Expo SDK 57 では expo prebuild が既定で ios と android を消してから再生成します。手編集を上げる前に洗い出す方法、config plugin への移し替え、57.0.9 で解消したメモリ回帰までを実務の順序でまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます