Как использовать временную метку сервера Firebase для создания даты?

В настоящее время версия Google ServerValue.TIMESTAMP возвращает {".sv":"timestamp"}, которая используется в качестве директивы для Firebase для заполнения этого поля меткой времени сервера после сохранения данных на сервере Firebase.

Однако, когда вы создаете свои данные на стороне клиента, у вас нет фактической метки времени для воспроизведения (т.е. использовать в качестве даты создания). У вас будет только доступ к отметке времени после первоначального сохранения и последующего поиска, что, я думаю, иногда слишком поздно и не очень изящно.


Перед Google:

Обновление: игнорировать этот раздел, поскольку он неправильный - я неправильно понял примеры. ServerValue.TIMESTAMP всегда возвращал {".sv":"timestamp"}.

Насколько я понимаю, в предустановленной Firebase появилась доступная на сервере отметка времени, которая позволила вам получить фактическую метку времени:

import com.firebase.client.ServerValue;
ServerValue.TIMESTAMP // eg. 1466094046

(ref 1, ref 2)


Вопросы:

  • Является ли такое сохранение/извлечение единственным способом получить дату создания сервера в экземплярах моей модели?
  • Если да, вы можете предложить метод реализации такого шаблона?
  • Я правильно понимаю ServerValue.TIMESTAMP изменился с приобретением Google Firebase? Обновление: Нет, @FrankvanPuffelen ответил, что во время приобретения ничего не изменилось.

Примечание:

Я не рассматриваю возможность использования new Date() на стороне клиента, поскольку я читал его небезопасно, хотя, пожалуйста, поделитесь своими мыслями, если вы считаете, что разные.

Ответ 1

Когда вы используете константу ServerValue.TIMESTAMP в операции записи, вы говорите, что сервер базы данных Firebase должен определить правильную метку времени, когда она выполняет операцию записи.

Скажем, мы запускаем этот код:

ref.addValueEventListener(new ValueEventListener() {
    public void onDataChange(DataSnapshot dataSnapshot) {
        System.out.println(dataSnapshot.getValue()); 
    }

    public void onCancelled(DatabaseError databaseError) { }
});
ref.setValue(ServerValue.TIMESTAMP);

Это будет выполняться следующим образом:

  • вы присоединяете слушателя
  • вы пишете значение с помощью ServerValue.TIMESTAMP
  • Клиент Firebase немедленно запускает событие значения с приближением метки времени, которую он будет писать на сервере.
  • ваш код печатает это значение
  • операция записи отправляется серверам Firebase
  • серверы Firebase определяют фактическую метку времени и записывают значение в базу данных (при условии, что правила безопасности не сработают)
  • сервер Firebase отправляет фактическую метку времени клиенту
  • Клиент Firebase вызывает событие значения для фактического значения
  • ваш код печатает это значение

Если вы используете ChildEventListener вместо ValueEventListener, тогда клиент будет вызывать onChildAdded на шаге 3 и onChildChanged на шаге 8.

Ничего не изменилось в способе создания ServerValue.TIMESTAMP, поскольку Firebase присоединилась к Google. Код, который работал до этого, будет продолжать работать. Это также означает, что первый ответ, который вы связали, является допустимым способом его обработки.

Ответ 2

Я делаю это по-другому.

Решение 1: push() метод в POJO

Поскольку я не хочу загромождать свои POJO со странными getters или свойствами, я просто определяю метод push() внутри своих POJO, который выглядит следующим образом:

/**
 * Pushes a new instance to the DB.
 * 
 * @param parentNode `DatabaseReference` to the parent node this object shall be attached to
 */
fun push(parentNode: DatabaseReference) {
    parentNode
        .push()
        .apply {
            setValue([email protected])
            child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
        }
}

Затем я могу просто создать экземпляр POJO и вызвать push() на нем, который правильно заполняет свойство времени создания.

Это определенно делает POJO немного менее понятным и включает в себя логику, о которой POJO не должен знать. Однако использование @Exclude аннотаций и/или кастингов, как описано в некоторых ответах здесь, также требует знания механизма хранения.

Решение 2: Помощник или расширение DatabaseReference (Kotlin)

Чтобы преодолеть это, вы, конечно, можете просто создать метод pushTask(task: Task) в помощнике или - если используете Kotlin - метод расширения, например. DatabaseReference, который может выглядеть так:

fun DatabaseReference.push(pojo: Pojo) {
    push()
    .apply {
        setValue(pojo)
        child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
    }
}

Глядя на это сейчас, я пришел к мысли, что мне действительно нравится второй подход (если у меня есть Котлин в моем распоряжении - мне не нравятся помощники). Но это, вероятно, только вопрос вкуса.;)