I want to get my server time in Firebase Firestore so I can use it for my countdown logic. I can't find anywhere and mostly they suggest to use FiedValue.serverTimeStamp or ServerValue.TIMESTAMP which is both are used to save data. I don't want to save data I just want to get the server time.
Is there a static way like FirebaseFirestore.getInstance().getServerTime()?
There is no provided method to get the actual server time.
Server timestamps are just token values on the client that are give a final value when they reach Firestore. The best you can do is write a server timestamp to a field in a document, then read the doucument back from Firestore after field has a value. Bear in mind that the timestamp will be "late" at that point, because of the time it takes your code to actually write and read the data. You have no guarantee how long that process will take.
For a more details explanation of how server timestamps work, read this article.
As per said by the #Doug there is no way of getting just only time from Firebase. However there are many round-ways for the same.
Firstly, you can use the count-down using the device of the app user since all the time in today era is synced with satellite. This can be done using:
Date dateStart = new Date();
Date dateEnd = new Date();
System.out.println(dateEnd.getTime() - dateStart.getTime()/1000);
Secondly, create an object in firebase for the starting time (for particular user) and then when the countdown is over then count the difference between the current time and the time uploaded in database.
FirebaseFirestore db = FirebaseFirestore.getInstance();
// At the time of setting.
Map<String, Object> map= new HashMap<>();
map.put("startTime", (new Date()).getTime());
db.collection(userdata).document(userid).set(map);
// At the time of retrieving:
db.collection(userdata).document(userid).get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
// Here you can get the time..
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
Now if you don't like any of the above method then you can file a support request for time at https://firebase.google.com/support
Related
I'm creating this post as I can't solve my problem with existing ones.
I'm saving the current time on Firestore with :
Map<String, Object> docData = new HashMap<>();
docData.put("Starting date", new Timestamp(new Date()).toDate());
docData.put("Quantification", 3.14569);
userManager.getUsersCollection().document(userManager.getCurrentUser().getUid())
.collection("habits")
.document(selectedHabit)
.set(docData)
Then, I would like to get back the timestamp and use it to count since how many days it's been created. I'm stuck here. The only way I found to access specific data of a document is by doing this :
userManager.getLoggedUserHabitsSubCollection()
.get()
.addOnCompleteListener(new OnCompleteListener < QuerySnapshot > () {#
Override
public void onComplete(#NonNull Task < QuerySnapshot > task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document: task.getResult()) {
Log.d("test", document.getId() + " => " + document.getData() + " => " + document.get("Starting date"));
}
} else {}
}
});
with "document.get("Starting date") I can access my saved date, that's the only thing that I was able to make work to do so. Now I would like to be able to display this Object in a string, and to check differences between dates to know how much time have passed. And here I'm stuck. I see a lot of different answers on forums, like using .toDate() for example, but when I write document.getData().toDate(), it's in red. I tried a lot of things with different ways of writing it, it's always in red and I can't make it work. Any help appreciated.
I see a lot of different answers on forums, like using .toDate() for example, but when I write document.getData().toDate(), it's in red.
DocumentSnapshot#getData() method returns an object of type Map<String, Object>. So there is no way you can add a call to .toDate() on such an object. If you need to get the value of a specific field as a Date object, then you should use the getDate(String field) method, which:
Returns the value of the field as a Date.
Or DocumentSnapshot#getTimestamp(String field) method which:
Returns the value of the field as a com.google.firebase.Timestamp.
Once you have the Date object, you can then calculate the difference between two Java date instances.
I have been searching for quite a while now and I can't seem to find anything to help me...
I want to fetch data from a firebase firestore collection that I have in reference (timeEntryTable). In the debugger, I can see that my data is accessed correctly, but when I get out of the method, it seems like everything is gone...
private void getTimes(){
float totalTime = 0;
timeEntryTable
.whereEqualTo("person", "Alexandre")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
TimeEntry timeEntry = new TimeEntry(document.getData());
times.add(timeEntry.getTime());
Log.d(TAG, document.getId() + " => " + document.getData());
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
System.out.println("times = " + times);
}
In this snippet, times is a private ArrayList member and TimeEntry is a POJO that has the structure of the documents in the collection.
I can really see in the debugger that times is filled with the right data, but when I call System.out.println("times = " + times);, the value is []... Is there something I am not doing correctly?
Thank you :)
To add to the previous answer that "the first time you can use the results is inside the callback itself", move the "System.out.println("times = " + times);" line right outside underneath your for loop.
Firestore's get() operation, and all of its APIs, are asynchronous and return immediately, before the data is available. Your code continues to execute while the query completes. Some time later, the callback you attached with addOnCompleteListener will be invoked with the results of the query.
The first time you can use the results is inside the callback itself. You can't be certain that any other access to your times array will contain anything. This means that you will have to build your program around these asynchronous APIs instead of depending on line-by-line execution.
May I ask if there is any implementation to collect the time when the first data is updated. For example, there is a queue function in my app. When an user has taken the queue ticket, Firebase will then be updated.
Therefore, I would like to know the time that the first user in the queue.
Is there any code for this in Android Studio? Many thanks!!
The Firebase Database does not store metadata (informations like the timestamp) for CRUD operations that are performed. Because of that, you need to store this kind of data yourself by creating your own mechanism.
In fact, you need to create a new field for each child you want to trace and change the value of the timestamp every time a action is performed. The best practice within a Firebase database is to save your data as a timestamp using: ServerValue.TIMESTAMP.
Note, that when you are saving the timestamp, you are saving as a Map and when you are retrieving, you are retrieving it as a long.
To set the timestamp, I recommend you to use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Map<String, Object> map = new HashMap<>();
map.put("timestamp", ServerValue.TIMESTAMP);
rootRef.child("yourNode").updateChildren(map);
To get you data back, I recommend you using the following method:
public static String getTimeDate(long timeStamp){
try{
DateFormat dateFormat = getDateTimeInstance();
Date netDate = (new Date(timeStamp));
return dateFormat.format(netDate);
} catch(Exception e) {
return "date";
}
}
In our app it is really important to also save the end datetime of a new data.
For example, if writing a new message, I would like it to be visible for users only ten hours. With rest api & sql database, I can easily create end datetime with server time(+ten hours) and save the message in database. When loading messages for users, I can select all messages where end datetime is before now.
How can I do this with Firebase Realtime Database? Maybe with Firebase Cloud Functions? I don't want to use client time.
This would actually be a good usage example for Cloud Functions.
When saving the message to the database, you can use ServerValue.TIMESTAMP to automatically insert the Firebase server's timestamp, so you could do something like:
public void saveMessage(String content) {
DatabaseReference messagesRef = FirebaseDatabase.getInstance().getReference("messages");
HashMap<String, Object> message = new HashMap<>();
message.put("content", content);
message.put("startTime", ServerValue.TIMESTAMP);
messagesRef.push().setValue(message);
}
Then you could attach a Cloud Function to the messages location to listen for new data. This function could perform the logic of adding 10 hours to the startTime value and saving it as endTime back to the same child, something like:
exports.calculateEndTime = functions.database
.ref('/messages/{messageId}').onCreate(event => {
const message = event.data.val();
// Only calculate endTime if it doesn't already exist.
if (message && !message.endTime) {
// Add 10 hours (in milliseconds) to the startTime to obtain the endTime.
const endTime = message.startTime + (10 * 3600000);
// Update the Firebase Database with the new endTime value.
return event.data.adminRef.update({
endTime: endTime
});
}
});
Finally, to query the list of messages to only obtain those where the endTime has not yet passed, you could do something like:
// Obtain the server time using the .info/serverTimeOffset value.
DatabaseReference offsetRef = FirebaseDatabase.getInstance().getReference(".info/serverTimeOffset");
offsetRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
// Calculate the estimated server time based on the offset.
double offset = snapshot.getValue(Double.class);
double estimatedServerTimeMs = System.currentTimeMillis() + offset;
// Use this server time to get the messages.
getMessages(estimatedServerTimeMs);
}
#Override
public void onCancelled(DatabaseError error) { }
});
public void getMessages(double endTime) {
DatabaseReference messagesRef = FirebaseDatabase.getInstance().getReference("messages");
// Create a query to limit results where endTime is greater than the current time.
Query messagesQuery = messagesRef.orderByChild("endTime").startAt(endTime);
messagesRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot message : dataSnapshot.getChildren()) {
// ...
}
}
#Override
public void onCancelled(FirebaseError firebaseError) { }
});
}
This example first grabs the clock skew value (from .info/serverTimeOffset) to calculate the estimated current server time and then uses this in a startAt() query to only return messages with an endTime after the current time.
There's likely a few other ways to approach this too, like obtaining the server timestamp and performing the logic above while saving the message, or even just querying on the startTime (using startAt 10 hours ago).
However, using the Cloud Functions route is a good way to ensure that the endTime is calculated server-side, saving of a new message is performed quickly and the messages actually contain an endTime value in the database.
I'm trying to set the epoch when data is created in Firestore. I'm looking to get some similar result to what is done in the real-time database, using ServerValue.TIMESTAMP.
I don't want to set it by using the device time System.getCurrentMillis because that time can be changed by the user.
According to docs an update needs to be done, the problem with that is the format. This is my code:
Map<String, Object> map = new HashMap<>();
map.put("timestamp", FieldValue.serverTimestamp());
reference.update(map);
And this is the result in the Firebase web console:
I was very surprised it is in spanish, which could be useful in some situations but epoch is what I'm chasing. Try to see the bright side and stick with it and thought that I was seeing the web in spanish, so I changed the language in the footer selector, it didn't change. On this point I'm assuming is set in the project language.
Back to the epoch attempt. Considering my project is using the real-time database as well, I try to set it in that way:
Map<String, Object> map = new HashMap<>();
map.put("timestamp", ServerValue.TIMESTAMP);
reference.update(map);
It did upload something, but it was just nonsense.
I think using epoch as the server-side timestamp is a better standard approach, after that every client can transform it to the user convenience and locale.
Can the epoch by set as server value in Firestore?
UPDATE
The answer marked as correct lead me to some interesting findings that I would like to share, so others in the same situation can benefit from:
There is no need to set the epoch because of the FieldValue.serverTimestamp() it is a date object handled by the database, what we see in the console is just a friendly way to show it.
Since FieldValue.serverTimestamp() is a date object it can be sort as any other timestamp could be, if you add orderBy("timestamp", Query.Direction.DESCENDING) to your query (or Query.Direction.ASCENDING) it will sort the results correctly.
And regarding to a the #34m0 comment, that is right, clients should not take care of the logic for setting the creation time, but it should be done in Functions.
The object that results from setting a Firestore field with FieldValue.serverTimestamp() is an instance of java.util.Date. When you later read the value, you can get the epoch time using getTime().
As an example, for a document created like this:
Map<String, Object> doc = new HashMap<>();
doc.put("timestamp", FieldValue.serverTimestamp());
The resulting value can be read like this:
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot snapshot = task.getResult();
if (snapshot != null) {
Map<String,Object> map = snapshot.getData();
Date date = (Date) map.get("timestamp");
Log.d(TAG, "date=" + date);
Log.d(TAG, "time=" + date.getTime());
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get() failed with ", task.getException());
}
}
});