Can a timestamp be extracted from the Firebase GUID [duplicate] - firebase

According to this blog post, firebase array keys are created using a timestamp:
It does this by assigning a permanent, unique id based on the current timestamp (offset to match server time).
Is there a way to recover this timestamp for use later, given the key?

As I said in my comment, you should not rely on decoding the timestamp from the generated id. Instead of that, you should simply store it in a property in your Firebase.
That said, it turns out to be fairly easy to get the timestamp back:
// DO NOT USE THIS CODE IN PRODUCTION AS IT DEPENDS ON AN INTERNAL
// IMPLEMENTATION DETAIL OF FIREBASE
var PUSH_CHARS = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
function decode(id) {
id = id.substring(0,8);
var timestamp = 0;
for (var i=0; i < id.length; i++) {
var c = id.charAt(i);
timestamp = timestamp * 64 + PUSH_CHARS.indexOf(c);
}
return timestamp;
}
var key = prompt("Enter Firebase push ID");
if (key) {
var timestamp = decode(key);
console.log(timestamp+"\n"+new Date(timestamp));
alert(timestamp+"\n"+new Date(timestamp));
}
I'll repeat my comment, just in case somebody thinks it is a good idea to use this code for anything else than as an exercise in reverse engineering:
Even if you know how to retrieve the timestamp from the key, it would be a bad idea to do this in production code. The timestamp is used to generate a unique, chronologically ordered sequence. If somebody at Firebase figures out a more efficient way (whichever subjective definition of efficiency they happen to choose) to accomplish the same goal, they might change the algorithm for push. If your code needs a timestamp, you should add the timestamp to your data; not depend on it being part of your key.
Update
Firebase documented the algorithm behind Firebase push IDs. But the above advice remains: don't use this as an alternative to storing the date.

Here's a version of Frank's code re-written in Swift (4.2 at the time of writing.)
Just to be clear, my use case for this was to patch my old models with no timestamps (createdAt, updatedAt.) I could just throw in random dates in them just to save me some headaches. But then that wouldn't be relevant to their models. I knew that there's an element of time baked into these auto-ids based on what I've read from other articles.
let PUSH_CHARS = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
func decode(autoId: String) -> TimeInterval {
let substring = autoId.substring(toIndex: 8)
var timestamp = 0
for i in 0..<substring.length {
let c = Character(substring[i])
timestamp = (timestamp * 64) + PUSH_CHARS.firstIndex(of: c)!.encodedOffset
}
return TimeInterval(exactly: timestamp)!
}
Grab the Playground-ready code here: https://gist.github.com/mkval/501c03cbb66cef12728ed1a19f8713f7.

And in python
PUSH_CHARS = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
def get_timestamp_from_id(id):
timestr = id[0:8]
timestamp = 0
for idx, ch in enumerate(timestr):
timestamp = timestamp * 64 + PUSH_CHARS.index(ch)
return timestamp/1000

Related

DoGet with multiple parameters not being recognized

I'm currently trying to connect a Lua Script with a GS WebApp. The connection is working but due to my lack of knowledge in GScripting I'm not sure why it isn't saving my data correctly.
In the Lua side I'm just passing in a hard-code a random name and simple numerical userid.
local HttpService = game:GetService("HttpService")
local scriptID = scriptlink
local WebApp
local function updateSpreadSheet ()
local playerData = (scriptID .. "?userid=123&name:Jhon Smith")
WebApp = HttpService:GetAsync(playerData)
end
do
updateSpreadSheet()
end
On the Google Script side i'm only saving the data on the last row and then add the value of the userid and the name.
function doGet(e) {
console.log(e)
// console.log(f)
callName(e.parameter.userid,e.parameter.name);
}
function callName(userid,name) {
// Get the last Row and add the name provided
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(sheet.getLastRow() + 1,1).setValues([userid],[name]);
}
However, the only data the script is saving is the name, bypassing the the userid for reasons I have yet to discover.
setValues() requires a 2D array and range dimensions should correspond to that array. The script is only getting 1 x 1 range and setValues argument is not a 2D array. Fix the syntax or use appendRow
sheet.getRange(sheet.getLastRow() + 1,1,1,2).setValues([[userid,name]]);
//or
sheet.appendRow([userid,name])
References:
appendRow

Only able to access first record in a query

(server side script)
This is a stripped down version of my code but what this should be doing is
find records where the "uniqueid" is equal to matchid
return 0 if there are less than two of these items
print the region of each item if there are two or more items
return the number of items
function copyFile(matchid){
var fileName = getProp('projectName')+" "+row[0];
var query = app.models.Files.newQuery();
query.filters.uniqueid._equals = matchid;
records = query.run();
var len = records.length;
if (len < 2) return 0;
console.log(row[2]+" - "+len);
for (var i=0; i<len;i++){
console.log("Loop "+i);
var r = records[i];
console.log(r.region);
}
return records.length
Strangely, it can only get at the region (or any of the other data for the FIRST record ( records[0]) for the others it says undefined. This is extremely confusing and frustrating. To reiterate it passes the len < 2 check, so there are more records in the set returned from the query, they just seem to be undefined if I try to get them from records[i]
Note: uniqueid is not actually a unique field, the name is from something else, sorry about confusion.
Question: WHY can't I get at records[1] records [2]
This was a ridiculous problem and I don't entirely understand the solution.
Changing "records" to "recs" entirely fixes my problem.
why does records[0] work, records[1] does not
but recs[0] and recs[1] both work.
I believe "records" has a special meaning and points at something regardless of assignment in this context.

Regex verification correct birth date and check age

I need a regex which takes the string YYYY-MM-DD-XXXX (The last 4 are just for purpose of gender/area) It's mostly important to check the first 8 Digits for a valid birth date.
So far i have this:
/^([0-9]{4})\-([0-9]{2})\-([0-9]{2})\-([0-9]{4})$/
Also i want to check so the input age is at least 18 years old. Would appreciate if somone had some input on how to achieve this.
Edit: The regex above was tested in JS, but should work fine in ASP as well?
I have changed your regex a bit to make it look more authentic
^([1-2]\d{3})\-([0-1][1-9])\-([0-3][0-9])\-([0-9]{4})$
years like 3012 will not pass.
Now you want to find whether a person is 18 years or not.
One approach could be to find the difference between the years of dates provided like this
var str = '1990-09-12-5555';
var res = /^([1-2]\d{3})\-([0-1][1-9])\-([0-3][0-9])\-([0-9]{4})$/.exec(str);
var year_now = new Date().getFullYear();
console.log(year_now-res[1]);
a second approach will be more precise one :
var str = '1990-09-12-5555';
var res = /^([1-2]\d{3})\-([0-1][1-9])\-([0-3][0-9])\-([0-9]{4})$/.exec(str);
var todays_date = new Date();
var birth_date = new Date(res[1],res[2],res[3]);
console.log(todays_date-birth_date);
will output the result in milliseconds. You can do the math to convert it into year
Cheers , Hope that helps !
I suggest using moment.js which provides an easy to use method for doing this.
interactive demo
function validate(date){
var eighteenYearsAgo = moment().subtract("years", 18);
var birthday = moment(date);
if (!birthday.isValid()) {
return "invalid date";
}
else if (eighteenYearsAgo.isAfter(birthday)) {
return "okay, you're good";
}
else {
return "sorry, no";
}
}
To include moment in your page, you can use CDNJS:
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.4.0/moment.min.js"></script>
Source
The following will match any year with a valid day/month combination, but won't do validation such as checking you've not entered 31 days for February.
^[0-9]{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])\-[0-9]{4}$
Not sure exactly what you're trying to achieve but I'd suggest using a date library for this sort of thing. You could return a message to the user somehow if the entered date fails to parse into an object.
In order to do age validation, you will certainly need to use a library so a regex should only be used for date validation purposes

Compare two different date formats in a query

I have to compare a user entered date, "Dt" (in mm/dd/yyyy format) with the date in RavenDB - "ReleaseDate" (time stamp like "/Date(1187668800000)/"). For this I am using the following code which almost gets the job done, but I need little help to finalize loose ends...
How can I compare the two dates so I can get the query to run successfully.
public ActionResult Calculation(DateTime? Dt)
{
var store = new DocumentStore { Url = "http://localhost:80" };
store.Initialize();
var CalcModel = new CalcViewModel();
using (var session = store.OpenSession())
{
//Converting user entered date dt in mm/dd/yyyy format to total
//milliseconds - So that later I can compare this value to RavenDB
//time stamp date format (older versions)
DateTime d1 = new DateTime(1970, 1, 1);
DateTime d2 = Dt.Value.ToUniversalTime();
TimeSpan ts = new TimeSpan(d2.Ticks - d1.Ticks);
double tmillisecs = ts.TotalMilliseconds; //Not yet using this value.
CalcModel.MoviesByDate = session.Query<Movies>()
.Where(x => x.ReleaseDate.Ticks == ts.Ticks)
.Count();
// this is where I need to compare two dates - ts.ticks gives the
// required value of date (1187668800000) multiplied by 10000.
}
return View(CalcModel);
}
Right now, when I debug I know what value ts.ticks is showing... and its like I said above in the code comments, the required value multiplied by 10000. But I have no clue at run time , what the value in x.ReleaseDate is or x.ReleaseDate.Ticks is.. am I doing this correctly. Thanks for the help.
Umm... I think you seriously misunderstand how SQL dates work, and how it applies to .NET. The whole point about dates is that they're stored in a numeric format, not a text one. So when you have a DateTime object, it's not stored as the text date, it's stored as a numeric type that you can convert to any format you want.
Because the .net provider converts database native datetime objects to DateTime objects, you can just compare them natively. ie:
DateTime d1 = new DateTime(1970, 1, 1);
CalcModel.MoviesByDate = session.Query<Movies>()
.Where(x => x.ReleaseDates.Date == d1.Date)
.Count();
Regardless of how RavenDB stores the dates internally, when the DateTime object is materialized in the query, it will be in native .NET format.

Linq-to-entities date value in database is string

Personally, I know just enough Linq to be dangerous.
The task at hand is; I need to query the DAL and return a list of objects based on a date range. Sounds simple enough, however the date is a string, and for some reason it needs to stay a string.
I spent some time with this a while ago and got a solution working but I am iterating through a list of objects and selecting individual records by date one at a time, this is badddd! If the date range spans more than a few days its slow and I don't like it, and I've even busted a few of the Sr devs around here for doing iterative queries, so I definitely don't want to be a hypocrite.
Here is the crappy iteration way... each date pegs the database, which I hate doing.
- This works
DateTime start = Convert.ToDateTime(RecipientSearch.TransplantSearchStartDate);
DateTime end = Convert.ToDateTime(RecipientSearch.TransplantSearchEndDate);
var tempselectQuery = selectQuery;
while (start <= end)
{
tempselectQuery = selectQuery;
string sStart = Convert.ToDateTime(start).ToString(ResourceFormatting.DateOnly);
tempselectQuery = (ObjectQuery<DAL.Recipients>)tempselectQuery.Where(item => item.TransplantDate == sStart);
if (tempselectQuery.Count() != 0) TXPlistQueryDAL.AddRange(tempselectQuery.ToList());
start = start.AddDays(1);
}
Here is my attempt at trying to get my query to work in one db call
- This does not work... yet
DateTime start = Convert.ToDateTime(RecipientSearch.TransplantSearchStartDate);
DateTime end = Convert.ToDateTime(RecipientSearch.TransplantSearchEndDate);
List<string> sdates = new List<string>();
// Put my date strings in a list so I can then do a contains in my LINQ statement
// Date format is "11/29/2011"
while (start <= end)
{
string sStart = Convert.ToDateTime(start).ToString(ResourceFormatting.DateOnly);
sdates.Add(sStart);
start = start.AddDays(1);
}
// Below is where I get hung up, to do a .contains i need to pass in string, however x.TransplantDate
// includes time, so i am converting the string to a date, then using the EntityFunction to Truncate
// the time off, then i'd like to end up with a string, hence the .ToString, but, linq to entities
// thinks this is part of the sql query and bombs out... This is where I'm stumped on what to do next.
selectQuery =
(ObjectQuery<DAL.Recipients>)
from x in entities.Recipients
where sdates.Contains(EntityFunctions.TruncateTime(Convert.ToDateTime(x.TransplantDate)).ToString())
select x;
The error i get as follows:
I understand why I get the error, but I don't know the proper LINQ code to be able to acheive what I am trying to do. Any help will be greatly appreciated.
Ughh I feel dumb. I tried a bunch of tricky little things to get x.TransplantDate to just a date only string within my Linq query, E.G. 10/15/2011
where sdates.Contains(EntityFunctions.TruncateTime(Convert.ToDateTime(x.TransplantDate)).ToString())
Turns out it already is in the correct format in the database, and if i simplify it down to just
where sdates.Contains(x.TransplantDate) It works. The reason I wasnt getting any records returned was because I was testing date ranges that didnt have any data for those specific dates... UGHH.
So in conclusion this ended up working fine. And if anyone is doing something similar maybe you can learn from this example.
DateTime start = Convert.ToDateTime(RecipientSearch.TransplantSearchStartDate);
DateTime end = Convert.ToDateTime(RecipientSearch.TransplantSearchEndDate);
List<string> sdates = new List<string>();
while (start <= end)
{
string sStart = Convert.ToDateTime(start).ToString(ResourceFormatting.DateOnly);
sdates.Add(sStart);
start = start.AddDays(1);
}
selectQuery =
(ObjectQuery<DAL.Recipients>)
from x in entities.Recipients
where sdates.Contains(x.TransplantDate)
select x;

Resources