


複数のユーザがサーバに接続して、チャットをするアプリなどを作る場合に、あるユーザが発言した時間を記録したいと思うかもしれません。
この場合に、各ユーザの端末の時間を送信して記録するのは、端末の時計が狂っている可能性があるので、よくないです。
Cloud Firestore と Realtime Database には、データベースにデータを記録する際に、サーバ時間を格納してくれる仕組みがあります。
Realtime Database では以下のようにします。
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("info");
ref.setValue(ServerValue.TIMESTAMP);
このようにして、特定の参照にデータを書き込みます。
ServerValue.TIMESTAMP は具体的な時刻値ではなく、この場所にサーバ時間を入れてくれるように指示するための値です。
そして、この参照からデータを取得することで、サーバ時間が得られます。
この際に注意点があります。書き込みをした直後に取得を行うと、データベースに格納された時刻とは微妙に異なった値が得られることがあります。
正確な値が必要であれば、しばらく待ってから取得する必要があります。
Realtime Database から、上記とは別の方法で、サーバ時間を取得することもできます。
DatabaseReference ref = FirebaseDatabase.getInstance().getReference(".info/serverTimeOffset");
ref.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
double offset = snapshot.getValue(Double.class);
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
この方法では、端末の時計とサーバ時間のずれを取得できます。
ServerValue.TIMESTAMP を書き込む必要がないので、こちらの方が簡単です。
ただし、この値を取得した後、端末の時間を変更し、再度取得した場合に、得られる値が変わらないようなので注意が必要です。
Cloud Firestore では、以下のようにします。
Map<String, Object> data = new HashMap<>();
data.put("timestamp", FieldValue.serverTimestamp());
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference doc = db.collection("Hoge").document("Fuga");
doc.set(data);
このようにすることによって、Realtime Database の時と同じように、サーバ時間を格納させることができます。
この格納されたサーバ時間を取得する際に注意点があります。
書き込んだ直後に、時刻を取得しようとした場合、データベースにまだ値が書き込まれておらず、null が返ることがあります。
時刻を取得する際に ServerTimestampBehavior 列挙型の値を渡すことで、null が返る際の挙動を変更することができます。
doc.get().addOnCompleteListener(this, new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot snapshot = task.getResult();
if (snapshot.exists()) {
Log.d(TAG, "1:" + snapshot.get("timestamp"));
Log.d(TAG, "2:" + snapshot.get("timestamp", ServerTimestampBehavior.NONE));
Log.d(TAG, "3:" + snapshot.get("timestamp", ServerTimestampBehavior.PREVIOUS));
Log.d(TAG, "4:" + snapshot.get("timestamp", ServerTimestampBehavior.ESTIMATE));
}
}
}
});
デフォルトは NONE となっており、null が返るタイミングでは null が返ります。
PREVIOUS を指定した場合は、null の代わりに、前回取得した値が返ります。(ただし、初めて取得した時は null)
ESTIMATE を指定した場合は、null の代わりに、推定値が返ります。