日付、時刻表示(firebase,timestamp,moment.js)


日付、時刻表示について

チャットアプリ作成時、苦戦したのでまとめておく。
メッセージを送信した際に、Cloud Firestore(firebase)にtimestampを保存し、そのtimestampを取り出してフォーマットした後、表示させる。

大前提として、、、

○firestoreにtimestampを作る

firebase.firestore().collection("messages").add({
      content: value,
      timestamp: firebase.firestore.FieldValue.serverTimestamp(), //<-new Date()だとそのパソコンの時刻が使われるのでズレる場合を考慮してサーバーサイドの時刻を追加する
      // timestamp: new Date(),
    });

new Date() だとクライアント側 (ブラウザ) の時刻になるが、端末の時刻が狂ってたら (または故意にずらしてたら) 問題が起こる可能性がある。
serverTimestamp() は Firebase のサーバー側で時刻を設定する

※serverTimestamp()について

firebase.firestore.FieldValue.serverTimestamp()でfirebaseにtimestampを作っているが、こちらで少しハマってしまった。
ただページを開いたり更新したりするだけだと日付と時間を表示できるが、メッセージを送信した際に表示ができず、エラーになった。

今回のエラーはtimestampがnullだからtoDate()が使えないよっていうエラーだった。
つまりtimestampがnullになっているのだが、これは取得時に書き換えをすることで回避することができた。
firebaseのserverTimestampはデータをaddしたタイミングに作り始める。
だから、onSnapshotでデータをとろうとすると、
データをadd ⬅serverTimestamp作成開始
データを所得 ⬅serverTimestamp作成途中
serverTimestampがnullですよ(エラー) ⬅作成完了
のような流れになっている様です。

☆firebase.firestore.FieldValue.serverTimestamp() を使うと、すぐにtimestampが入るわけではなく、タイムスタンプが設定されるまでに、少しタイムラグがある様です。

これを回避するには、useEffect内のデータ取得時に

timestamp: doc.data({serverTimestamps:"estimate"}).timestamp.toDate(), //この部分でfirebaseより取得(このように書くことで、serverTimestampが作成途中の時は見積もり時間を返してくれる)
//エラーが起きていたときにコード➜timestamp: doc.data().timestamp.toDate(),

とすることで回避できた。
doc.dataの引数にはSnapshotOptionsというオプションを指定することができ、今回はserverTimestampが作成途中の時は見積もり時間を返してくれるよというものになっている。
つまり、timestampがnullのときは見積もり時間を返し、timestampが作られたらその時間を表示してくれる様になります。

○firestoreから取り出す

まずfirebaseからデータをとってくる際にDate型に変えるtoDate()を実行する。

timestamp : doc.data().timestamp.toDate() // firebaseのtimestampの型からDate型にかえる

として取得します。
その後取得したデータはDate型なのでそのまま画面に表示させることができません。
一度string型、要は文字列にする必要があります。
そのやり方は↓でまとめる。(string型にフォーマットされた日付を作る方法と、moment.jsを使用する方法)

実際に表示させる

[今回、2通りの方法で実現した。]

①フォーマットしつつ、string型に変換

const formatTime = `${message.timestamp.getFullYear()}/${message.timestamp.getMonth()+1}/${message.timestamp.getDate()} ${message.timestamp.getHours()}:${message.timestamp.getMinutes()}:${message.timestamp.getSeconds()}`

としてstring型にフォーマットされた日付を作ります.それを

<span>{formatTime}</span>

として表示してやる。

②moment.jsを使用

※moment.js以外にも使えそうなライブラリは多数あり。
import dayjs from 'dayjs';

// 略
            <span>{dayjs(message.timestamp).format('YYYY/MM/DD HH:mm')}</span>