FunPHP#7: ID'шники в которых зашита дата

January 14, 2022

Те кто работает с MongoDB знают что там в качестве уникального автоинкремента используется поле _id, которое обязательно для всех по дефолту и оно отличается от числовых автоинкрементов, как в других SQL базах.

Не все знают, что мало того, что по таким полям можно сортировать, но в самом этом ID зашита дата (которая по сути является датой создания этого ID - тобиш записи).

function generateObjectId(date) {
    return Math.floor((new Date(date)).getTime()/1e3).toString(16) +
              (('x'.repeat(16).replace(/x/g,
                  _=>(Math.random()*16|0).toString(16))
              ))
}

let date = new Date('2022/01/13 22:22:22')
let id = generateObjectId(date)


console.log(id) // 61e07beea8d49039c3de64b6

Чтобы вернуть все это обратно в дату можно использовать такую функцию:

function objectIdToDateTime(objectId) {
  return new Date(parseInt(objectId.substring(0,8),16)*1e3)
}

objectIdToDateTime('5e1b270142dcae0010186f02')
// Sun Jan 12 2020 17:02:41 GMT+0300

// В самой монге есть возможность получить время от ObjectId:
ObjectId("5e1b270142dcae0010186f02").getTimestamp()
// ISODate("2020-01-12T17:02:41.000+03:00")

Подробнее про это я писал в этой статье: https://geekjob.tech/fun-mongo-1-objectid/

Так к чему все это я и причем тут PHP?

В PHP есть функция uniqid:

uniqid(string $prefix = "", bool $more_entropy = false): string

Получает уникальный идентификатор с префиксом, основанный на текущем времени в микросекундах.

Если бы нас попросили написать похожую функцию снуля, мы могли бы написать ее так, например:

<?php

var_dump(uniqid());
var_dump(uniq_d());

// string(13) "61e12c4400e9d"
// string(13) "61e12c44eb02c"


function uniq_d(): string
{
    ['sec'=>$sec, 'usec'=>$usec] = gettimeofday();
    return sprintf('%x%x%x', $sec, $usec, date('s'));
}

Результат почти совпадает.

Ну и собственно теперь к теме, как получить дату из такого ID:


$d1 = uniqid();
$d2 = uniq_d();

var_dump($d1); // string(13) "61e12dbe01013"
var_dump($d2); // string(13) "61e12dbe101a2"

$d = date('r',uiqid2date($d1));
var_dump($d); // string(31) "Fri, 14 Jan 2022 09:01:02 +0100"

$d = date('r',uiqid2date($d2));
var_dump($d); // string(31) "Fri, 14 Jan 2022 09:01:02 +0100"


function uiqid2date(string $uniqid): int
{
    return hexdec(substr($uniqid,0,8));
}

Таким образом можно хранить 1 поле в базе вместо 2х, сохраняя дату создлания в самом ID.


Profile picture

Written by Alexander Mayorov
Full Stack CTO