Cloud FunctionsでFirestoreのTimestamp取得すると時間がずれる
Cloud Functions内でFirestoreに登録した日時データを取得したら、どういうわけか日本標準時ではなく、Fri Mar 05 2021 15:00:00 GMT+0000 (Coordinated Universal Time)
のように協定世界時の標準時となってしまう現象に遭遇しました。その解決に1日時間を潰してしまったのでこちらで供養したいと思います。
事象
現在、非常食管理アプリを作成しています。
その中で登録したデータに「通知日」をもたせておき、今日が通知日であるデータをfunctionsでユーザーに通知するという処理をつくっていました。
ところが、Firestoreに登録したデータをCloud Functionsで取得してログに表示すると、通知日が日本標準時(JST)ではなくグリニッジ標準時(GMT)で9時間前の時間になってしまって、うまくデータを取得できない事象が発生しました。
Firestoreに登録された値(UTC+9で日本標準時になっている)
Cloud Functionsで上記データを取得してログに出したとき(GMT+0000で協定世界時の標準時になっている)
最終的に有効だった解決策
- Cloud FunctionsのGCP側のコンソールでランタイム環境変数に
TZ=Asia/Tokyo
を設定してあげる
調べたこと
実は他にもいろいろDateの使い方が間違ってないかとか、Firestoreからの取得が間違ってないかとか、GMTとかUTCってなんなん?等も調べたんですが、結果的に解決につながったのは以下の3つを調べていったのが大きかったです。
時間のズレが問題なので、Firebase側のリージョンやタイムゾーン設定、もしくはFunctionsの関数の書き方あたりに原因がありそうだと検討をつけて調査を進めました。
- Cloud Functionsのソースでタイムゾーンを
Asia/Tokyo
に設定されているか? - Firestore, Cloud Functionsのロケーションが
asia-northeast1
になっているか? - Cloud Functionsのタイムゾーンが
Asia/Tokyo
になっているか?
Asia/Tokyo
に設定されているか?
Cloud Functionsのソースでタイムゾーンを最初に「Firestore functions 時間」でググったときにでてきた解決策で、functionsの関数を書くときにリージョンやタイムゾーンを指定する必要があるとのことでした。
当初、Cloud Functionsの関数処理にちゃんとリージョンやタイムゾーンをうっかり指定していなかった
ので、公式ドキュメントを参考に以下のように修正を加えました。(一部省略)
[...]
exports.dailyNotificationSender = functions
// リージョンを指定
.region('asia-northeast1')
.pubsub.schedule('0 10 * * *')
// タイムゾーンを指定
.timeZone('Asia/Tokyo')
.onRun(async (context) => {
const jst = new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
const todayStart = new Date(jst);
[...]
ただ、修正したあと現在デプロイされている関数を削除したうえで、再デプロイし実行したのですがうまくいきませんでした。
asia-northeast1
になっているか?
Firestore, Cloud Functionsのロケーションが次にあんまり初期設定等でミスをしていてロケーションがasia-northeast1
になっていないのでは?と思いコンソールを確認しましたが、以下の通りちゃんと東京リージョンになっていました。
Asia/Tokyo
になっているか?
Cloud Functionsのタイムゾーンがで、最終的に有効だったのがCloud Functionsのタイムゾーンの設定をすることでした。ただ、これGCP側のコンソールから設定する必要があります。
この方法でちゃんと日本標準時で取得できるようになったのですが、一つ問題点があってどうもローカルからfirebase deploy
するとTZ=Asia/Tokyo
が消えてしまうようです。
その解決策としては、関数の中でprocess.env.TZ = 'Asia/Tokyo'
を設定してあげればいけました。
import * as sgMail from '@sendgrid/mail';
import { format } from 'date-fns';
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
const db = admin.initializeApp().firestore();
sgMail.setApiKey(functions.config().sendgrid.key);
// ソース内で環境変数としてセットすればOK
const timezone = 'Asia/Tokyo';
process.env.TZ = timezone;
exports.dailyNotificationSender = functions
.region('asia-northeast1')
.pubsub.schedule('0 10 * * *')
.timeZone(timezone)
.onRun(async (context) => {
const jst = new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
const todayStart = new Date(jst);
[...]
参考
Cloud Functions のロケーション | Firebase
協定世界時 - Wikipedia
グリニッジ標準時 - Wikipedia
GMT/UTC +0 hours - Time Now | GMT
firebase functionsでDate()使ったときに9時間ずれてしまうのを直す的なお話 ~ 適当な感じでプログラミングとか!
https://www.yuulinux.tokyo/18671/
Discussion
firestoreのTimestampにはタイムゾーンの情報は無くて、ブラウザで見るときはブラウザのタイムゾーンで表示してるだけみたいです(ブラウザのタイムゾーンを変えたら表示が変わった)
参考 https://stackoverflow.com/questions/55714631/firestore-timestamp-fromdate-not-utc
コメントありがとうございます!
返信気づかず大変失礼しました・・・
これは知りませんでした。ありがとうございます。