LINE予約「LUCA」の機能と料金はこちら

n8nからAirtableに入れた日時が9時間ずれる原因と書き方

実務ノート 実務ノート 公開 更新

n8nからAirtableに日時を書き込んだら、画面では9時間あとの時刻になっていた。そんな場面のための記事です。予定の候補日や予約の時刻のように、人が画面で見て動く値だと、ずれたまま気づかずに使われます。

この記事の結論

  • Airtableの日時の列(dateTime)は、ISO 8601の文字列を「UTCの時刻」として受け取ります。末尾の Z はUTCという意味です。日本時間の正午のつもりで 2026-05-15T12:00:00.000Z と書くと、日本時間の画面では21時になります。
  • Codeノードでよく使う new Date(...).toISOString() は、いつも Z 付きのUTCで文字列を作ります。日本時間の数字に Z を付けてしまう形が、ずれの典型です。
  • 直し方は2つです。日本時間の時刻に +09:00 を付けるか、UTCに直してから Z を付けます。
  • n8nのAirtableノードで列を1つずつ指定している場合、+09:00 も Z も無い文字列は、ワークフローのタイムゾーンの時刻として読まれます。その設定が無いと既定は America/New_York です。時差を書かない文字列は使わないのが安全です。

実際に起きたこと

弊社では、予定を管理する表をAirtableに置いています。日時の列のタイムゾーンは Asia/Tokyo に設定してありました。

2026年4月30日、この表に予定の候補日を3件登録しました。書き込んだ文字列は T10:00:00.000Z の形です。

ところが、画面には「5/14(木) 19:00」と出ました。3件とも同じように9時間あとの時刻になっていました。画面の写しを見た代表の指摘で気づきました。

このときの書き込みは、n8nではなく、AIエージェントからMCP(AIが外部の道具を呼ぶための仕組み)経由でAirtableへ直接行ったものです。ただ、同じ書き方はn8nのCodeノードでもそのまま起きます。そこで弊社では、n8nのワークフローを作る前・直す前に通す点検の手順(preflight)に、この形を見つける検査を足しました。

(r"new\s+Date\([^)]+\)\.toISOString\(\)",
  "ℹ️ Airtable dateTime に渡す場合は +09:00 形式推奨(toISOString は UTC 'Z' 終端)",

点検の手順の「知っている落とし穴」の一覧にも、同じことを1行で書いています。

- Airtable dateTime は `+09:00` 終端推奨(`Z` だと UTC 解釈で 9 時間ズレる)

原因

Airtableは日時をUTCとして受け取る

Airtableの公式ドキュメント(Field model)では、日時の列の値は「ISO 8601形式の文字列」で、中身は「UTCの日時」と説明されています。例として 2022-09-05T07:00:00.000Z が載っています。

弊社の件では、列のタイムゾーンを Asia/Tokyo にしていても、Z 付きの値はUTCの時刻として保存されました。そして画面には日本時間に直して表示されました。日本時間はUTCより9時間進んでいます。だから 10:00:00.000Z は日本時間の19時と表示されたわけです。

つまり、Airtableは間違っていません。「日本時間の数字」に「UTCの印」を付けて渡したことが原因です。

CodeノードのtoISOStringはいつもUTCになる

JavaScriptの Date の toISOString() は、UTCに直した時刻に Z を付けて返します。手元で試すと次のとおりでした。

new Date('2026-05-15T12:00:00+09:00').toISOString()
// => "2026-05-15T03:00:00.000Z"

この結果そのものは正しい値です。日本時間の12時はUTCの3時だからです。危ないのは、日本時間の「年月日と時分」を文字列で組み立てて、最後に .000Z を足すような書き方です。数字は日本時間なのに、印はUTCになります。

時差を書かない文字列は、n8nのタイムゾーンで読まれる

n8nのAirtableノード(v2)では、Airtableの dateTime と date の列を、n8nの中の日時の型として扱います。n8n本体のソースでは次のように対応づけています。

dateTime: ['dateTime', 'date'],

列を1つずつ指定するモード(columns.mappingMode が defineBelow)では、入力した値が送信の前に日時として読み直されます。作成(create)と更新(update)のどちらの処理でも同じです。読み直す関数 tryToParseDateTime の中心はこうです。

const isoDate = luxon_1.DateTime.fromISO(dateString, { zone: defaultZone, setZone: true });

ここで使う Luxon(n8nに入っている日時のライブラリ)は、文字列に時差が書いてあればその時差をそのまま使います。書いていなければ、Luxonの既定のタイムゾーンで読みます。その既定は、n8nがワークフローごとに次のように決めています。

this.timezone = workflow.settings?.timezone ?? (0, global_state_1.getGlobalState)().defaultTimezone;
luxon_1.Settings.defaultZone = this.timezone;

ワークフローの設定にタイムゾーンが無ければ、n8n全体のタイムゾーンを使います。n8n全体のタイムゾーンは環境変数 GENERIC_TIMEZONE で決まり、ソースの既定値は次のとおりです。

this.timezone = 'America/New_York';

つまり 2026-05-15T12:00:00 のような時差の無い文字列は、どのn8nで動かすかで意味が変わります。日本時間に設定したn8nなら日本時間の12時です。設定していないn8nなら、ニューヨークの12時として送られます。

なお、入力をそのまま流すモード(autoMapInputData)と、オプションの typecast をオンにしたときは、この読み直しを通りません。文字列はそのままAirtableへ届き、Airtable側の解釈になります。

直し方

手順1:日本時間の時刻には +09:00 を付ける

いちばん確実なのは、送る文字列に時差を書くことです。日本時間の正午なら次のどちらかで送ります。

"2026-05-15T12:00:00+09:00"   // 日本時間の12時(おすすめ)
"2026-05-15T03:00:00.000Z"    // UTCの3時=日本時間の12時(同じ時刻)

次の2つは使いません。

"2026-05-15T12:00:00.000Z"    // 日本時間の画面では 21:00 になる
"2026-05-15T12:00:00"         // 時差が無い=どのタイムゾーンで読まれるか環境しだい

手順2:Codeノードでは Luxon で日本時間を作る

Codeノードの中では、Luxonの DateTime がそのまま使えます。setZone で日本時間にしてから toISO() を呼ぶと、末尾に +09:00 が付きます。

// 今の時刻を日本時間で
const nowJst = DateTime.now().setZone('Asia/Tokyo').toISO();
// => "2026-05-15T12:00:00.000+09:00" のような形

// 「2026-05-15 の 12:00(日本時間)」を組み立てる
const slot = DateTime.fromISO('2026-05-15T12:00:00', { zone: 'Asia/Tokyo' }).toISO();
// => "2026-05-15T12:00:00.000+09:00"

return [{ json: { scheduled_at: slot } }];

new DateTime() と書くとエラーになる点は、別の記事「n8n「DateTime is not a constructor」の原因と直し方(Luxon)」にまとめています。

Luxonを使わずに書くなら、点検の手順に載せている書き方もあります。日本時間の日時の文字列を作り、最後に +09:00 を足します。

new Date().toLocaleString('sv',{timeZone:'Asia/Tokyo'}).replace(' ','T')+'+09:00'

手順3:今ある toISOString の使い方を探す

すでに動いているワークフローの中から、日時の列へ toISOString() の結果を渡している箇所を探します。弊社の点検で使っている正規表現はこれです。

new\s+Date\([^)]+\)\.toISOString\(\)

見つかった箇所が「UTCの正しい時刻」を作っているなら、直す必要はありません。日本時間の数字に Z を付けているなら、手順1か手順2の形に直します。

手順4:n8nのタイムゾーンを日本時間にしておく

時差の無い文字列が混ざったときの保険として、n8n全体のタイムゾーンを日本時間にしておきます。セルフホストなら環境変数で設定します。

export GENERIC_TIMEZONE="Asia/Tokyo"

この設定はスケジュールの時刻にも効きます。未設定でスケジュールがずれる件は「n8nのスケジュールがずれる原因と直し方(タイムゾーン設定)」に書きました。ワークフローの設定でタイムゾーンを指定している場合は、そちらが優先されます。

直ったかを確かめる

確かめ方は3つです。

  1. Airtableの画面で見る。書き込んだ行を開き、日本時間で意図した時刻になっているかを見ます。
  2. APIで読み戻す。読み戻した値はUTCの Z 付きで返ってきます。日本時間の12時を入れたなら 03:00:00.000Z になっていれば正しい値です。12:00:00.000Z なら、まだ9時間ずれています。
  3. 送る前の文字列を手元で確かめる。n8nに入っているLuxonで、時差の有無による違いを試せます。弊社の手元のn8nは 2.19.2、中のLuxonは 3.7.2 で、結果は次のとおりでした。
const { DateTime, Settings } = require('luxon');

Settings.defaultZone = 'Asia/Tokyo';
DateTime.fromISO('2026-05-15T12:00:00', { setZone: true }).toJSON();
// => "2026-05-15T12:00:00.000+09:00"

DateTime.fromISO('2026-05-15T12:00:00.000Z', { setZone: true }).toJSON();
// => "2026-05-15T12:00:00.000Z"   ← Z はそのまま UTC

Settings.defaultZone = 'America/New_York';
DateTime.fromISO('2026-05-15T12:00:00', { setZone: true }).toJSON();
// => "2026-05-15T12:00:00.000-04:00"   ← 時差なしはニューヨーク時間になる

3つ目の結果が、時差を書かない文字列を避ける理由です。同じ文字列でも、n8nのタイムゾーンしだいで送られる時刻が変わります。

それでも直らないとき

  • 読み出した値を、日本時間と思って使っていないか。Airtableから読み戻した値は Z 付きのUTCです。その文字列から時刻の部分だけを切り出して画面やメッセージに出すと、今度は読む側で9時間ずれます。表示する前に日本時間に直します。
  • ワークフローの設定のタイムゾーンを見る。n8n全体を日本時間にしても、ワークフローの設定で別のタイムゾーンを選んでいれば、そちらが使われます。
  • まだ時差の無い文字列を送っていないか。実行結果のAirtableノードの入力を開き、日時の値の末尾に +09:00 か Z が付いているかを見ます。どちらも無ければ手順1に戻ります。

まとめ

  • Airtableの日時の列はUTCで受け取ります。末尾の Z はUTCの印です。
  • 日本時間の数字に Z を付けると、日本時間の画面では9時間あとになります。
  • 日本時間なら +09:00 を付けて送ります。Codeノードでは DateTime.now().setZone('Asia/Tokyo').toISO() が手軽です。
  • 時差を書かない文字列は、n8nのタイムゾーン(既定は America/New_York)で読まれます。使わないのが安全です。
  • 直ったかは、画面の表示とAPIの読み戻しの両方で確かめます。

参考にした公式ドキュメント

n8nの点検と構築のご相談

「Airtableに入れた日時が画面でずれている」といったn8nとAirtableの連携の不具合の点検や、業務の自動化の仕組みづくりをお引き受けしています。

お問い合わせはこちら

この記事を書いた人

貫名 孝夫(マルタマーケティング株式会社 代表取締役)。n8nで140本を超えるワークフローを本番で動かしながら、実際に起きた不具合と直し方を記録しています。会社概要