Skip to content

Reading timestamp information

Greg Bowler edited this page Apr 19, 2026 · 1 revision

One of the most useful parts of a sortable identifier is that the creation time is embedded in the string itself.

With phpgt/ulid, we can read that information in three main ways:

  • as the raw integer timestamp
  • as the encoded timestamp section
  • as a DateTime

Getting the raw timestamp

use GT\Ulid\Ulid;

$id = new Ulid();
echo $id->getTimestamp();

When the library generates a fresh ID with no explicit timestamp argument, it uses the current Unix time in milliseconds.

Getting the encoded timestamp section

echo $id->getTimestampString();

Example output:

01KPKT4PFT

This is the part that makes lexicographic sorting work. If we compare two ULID strings created at different times, the newer one sorts after the older one.

Converting to DateTime

echo $id->getDateTime()->format("Y-m-d H:i:s");

This is usually the most convenient choice when we want to display or compare the creation time in application code.

Setting an explicit timestamp

If we need a ULID for a known point in time, we can pass the timestamp into the constructor:

$knownTime = strtotime("2024-05-04 17:24:00 UTC") * 1000;
$id = new Ulid(timestamp: $knownTime);

echo $id->getDateTime()->format("Y-m-d H:i:s");

Important

To keep getDateTime() and lexicographic ordering aligned with generated IDs, pass Unix time in milliseconds when supplying your own timestamp.

Sorting behaviour

Because the timestamp section comes first, ordinary string sorting is enough:

$a = new Ulid(timestamp: strtotime("2024-01-01 00:00:00 UTC") * 1000);
$b = new Ulid(timestamp: strtotime("2024-02-01 00:00:00 UTC") * 1000);

var_dump((string)$a < (string)$b);

A practical use

If we already have the identifier, we may not need to store an extra created_at column purely to answer broad questions such as:

  • was this record created before or after another record?
  • roughly when was it created?
  • how should a list of IDs be ordered by default?

We may still want a dedicated timestamp column for indexing, reporting, or database-native date logic, but the ULID already carries useful timing information for application-level decisions.


Next we will look at rebuilding and dissecting IDs that already exist in Parsing existing ULIDs.