かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

今更の Firebase Cloud Firestore 。タイムスタンプ周りのメモ

今更ながら Firebase を使う機会が増えてきて Cloud Firestore のタイムスタンプ周りで何度か同じようなことを調べてしまっているのでメモ。

Cloud Firestore のタイムスタンプ型は Date ではなく firebase.firestore.Timestamp

firebase Cloud Firestore
↑これ。

Cloud Firestore にタイムスタンプ型のデータを保存する方法

🙅 input type="date"input type="datetime-local" の値をそのまま送ってもダメ。

import firebase from 'firebase/app';
import 'firebase/firestore';
const firebaseApp = firebase.initializeApp(FIREBASE_CONFIG);
const db = firebaseApp.firestore();
// ...

const [datetime, setDatetime] = useState('');

const postDataToFirebase = async () => {
  const addedDocRef = await db.collection(COLLECTION_NAME).add({
    datetime: datetime, 
  });
  return addedDocRef; 
};

return (
  <input
    type="datetime-local"
    value="datetime"
    onChange={(evt) => setDatetime(evt.currentTarget.value)}
  />
);

=> String 型のまま Cloud Firestore に登録 されてしまう

🙆 Timestamp 型に変換して送る

firebase.firestore.Timestamp.fromDate メソッドで Timestamp 型に変換できる

import firebase from 'firebase/app';
import 'firebase/firestore';
const firebaseApp = firebase.initializeApp(FIREBASE_CONFIG);
const db = firebaseApp.firestore();
// ...

// Firestore の Timestamp に変換して返す
const timestamp = (datetimeStr) => {
  return firebase.firestore.Timestamp.fromDate(new Date(datetimeStr));
};

const [datetime, setDatetime] = useState('');

const postDataToFirebase = async () => {
  const addedDocRef = await db.collection(COLLECTION_NAME).add({
    datetime: timestamp(datetime), 
  });
  return addedDocRef; 
};

return (
  <input
    type="datetime-local"
    value="datetime"
    onChange={(evt) => setDatetime(evt.currentTarget.value)}
  />
);

₍ ᐢ. ̫ .ᐢ ₎👌

cf. #データ型 Cloud Firestore にデータを追加する | Firebase

Date オブジェクトをそのまま送信しても大丈夫だった

Date オブジェクトをそのまま送信すると Firebase 側でよしなに Timestamp 型に変換してくれるみたいです

  const postDataToFirebase = async () => {
    const addedDocRef = await db.collection(COLLECTION_NAME).add({
-     datetime: timestamp(datetime), 
+     datetime: new Date(datetime),
    });
    return addedDocRef; 
  };

サーバーで処理した時間のタイムスタンプを使う (created_at, updated_at)

firebase.firestore.FieldValue.serverTimestamp() を使えばOK

  // ... 略
  const postDataToFirebase = async () => {
    const addedDocRef = await db.collection(COLLECTION_NAME).add({
      datetime: timestamp(datetime),
+     createdAt: firebase.firestore.FieldValue.serverTimestamp(),
    });
    return addedDocRef; 
  };

cf. #サーバーのタイムスタンプ Cloud Firestore にデータを追加する | Firebase


Cloud Firestore から取得した Timestamp を日付表示する

Cloud Firestore から取得した DocumentData にある Timestamp は seconds というプロパティを持っているが、これを new Date() に渡しても日付がズレてしまう。

new Date(seconds) だと別の日付になってしまう問題

Cloud Firestore 上のデータ

createdAt: 2021年4月16日 16:18:17 UTC+9
dueDate: 2021年4月18日 16:18:00 UTC+9
isDone: false
todo: "HPBtoMe"

Cloud Firestore から取得したオブジェクト

{
  createdAt: t {seconds: 1618557497, nanoseconds: 367000000},
  dueDate: t {seconds: 1618730280, nanoseconds: 0},
  isDone: false,
  title: "HPBtoMe",
}

new Date(seconds) した場合

data.dueDate.seconds; // => 1618730280
new Date(data.dueDate.seconds);
// => Tue Jan 20 1970 02:38:50 GMT+0900

Cloud Firestore 上に登録されている dueDate2021年4月18日 16:18:00 UTC+9 なのですが new Date(seconds) で変換すると 1970年1月20日 という全然異なる日付になってしまいました。なんもわからん…

toDate() メソッドを使う

Timestamp class
toDate(): Converts a Timestamp to a JavaScript Date object. This conversion causes a loss of precision since Date objects only support millisecond precision.
Timestamp class | Firebase

import dayjs from 'dayjs';
import firebase from 'firebase/app';
import 'firebase/firestore';
const firebaseApp = firebase.initializeApp(FIREBASE_CONFIG);
const db = firebaseApp.firestore();
// … 

const [todoList, setTodoList] = useState([]);

const getTodoFromFirebase = async () => {
  const items = await db
    .collection(COLLECTION_NAME)
    .orderBy('dueDate')
    .get();
  setTodoList(items.items.map((item) => {id: item.id, ...item.data()}));
};

//...

return (
  <ul>
    {todoList?.map((todo) => {
      const dueDate = dayjs(todo.dueDate.toDate()); // ← ココ
      return (
        <li key={todo.is}>
          <span>{todo.title}</span>
          <time datetime={dueDate.format('YYYY-MM-DDTHH:mm')}>
            {dueDate.format('YYYY-MM-DD')}
          </time>
        </li>
      );
    }}
  </ul>
)

₍ ᐢ. ̫ .ᐢ ₎👌 ヨシ!

所管

Firebase ドキュメント と ドキュメンテーションが別になってるのまじ探しにくいから何とかしておくれ… クラスとかメソッドとか書いてるドキュメンテーションは自動的によしなに日本語翻訳してくれるんだけど、メソッド名まで日本語に翻訳されちゃうから困っちゃうんよね…

とはいえ、だいぶ Firebase と仲良くなれてきた気がします!
そう。4/18 は僕の誕生日なのでみなさん祝ってください!!
欲しいものは時間と知識と不労所得です。ヨロシクオネガイシマス!!!!

干し芋


[参考]

Firestore はこれが良いと聞きました!

アマノ タイムスタンプ PIX-200

アマノ タイムスタンプ PIX-200

  • 発売日: 2008/01/09
  • メディア: オフィス用品