Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast or extract timestamp from v1 UUID in PostgreSQL

I'm trying to extract the timestamp from a Version 1 UUID, naively wished this worked:

SELECT '3efe0a20-f1b3-11e3-bb44-14109fec739e'::uuid::timestamp;

Here is a quick example showing how to extract the time in Go, but I'm hoping PostgreSQL has something built-in rather than creating a one-off PL/pgSql function:
http://play.golang.org/p/XRCooLgfaG

like image 588
Krut Avatar asked Jun 12 '14 07:06

Krut


People also ask

How is TimeStamp stored in PostgreSQL?

By casting "TimeStamp" to date you throw away the time part of the timestamp, so all values within one day will be considered equal and are returned in random order. It is by accident that the first rows appear in the order you desire. Don't cast to date in the ORDER BY clause if the time part is relevant for sorting.

Does Postgres support UUID?

PostgreSQL allows you store and compare UUID values but it does not include functions for generating the UUID values in its core. Instead, it relies on the third-party modules that provide specific algorithms to generate UUIDs.

Can I order by UUID?

There is no universal ordering of UUIDs (or even byte layout). However, PostgreSQL has an implementation specific UUID ordering, as does SQL Server (and this is not guaranteed to be the same; .

What is UUID data type in PostgreSQL?

The data type uuid stores Universally Unique Identifiers (UUID) as defined by RFC 4122, ISO/IEC 9834-8:2005, and related standards. (Some systems refer to this data type as a globally unique identifier, or GUID, instead.)


2 Answers

An alternative to @Krut's implementation that is quite a bit faster based in our testing:

CREATE OR REPLACE FUNCTION uuid_timestamp(uuid UUID) RETURNS TIMESTAMPTZ AS $$
DECLARE
  bytes bytea; 
BEGIN
  bytes := uuid_send(uuid);
  RETURN to_timestamp(
             (
                 (
                   (get_byte(bytes, 0)::bigint << 24) |
                   (get_byte(bytes, 1)::bigint << 16) |
                   (get_byte(bytes, 2)::bigint <<  8) |
                   (get_byte(bytes, 3)::bigint <<  0)
                 ) + (
                   ((get_byte(bytes, 4)::bigint << 8 |
                   get_byte(bytes, 5)::bigint)) << 32
                 ) + (
                   (((get_byte(bytes, 6)::bigint & 15) << 8 | get_byte(bytes, 7)::bigint) & 4095) << 48
                 ) - 122192928000000000
             ) / 10000 / 1000::double precision
         );
END
$$ LANGUAGE plpgsql
IMMUTABLE PARALLEL SAFE
RETURNS NULL ON NULL INPUT;

Note, it will only do millisecond precision so you may want to tweak the function's "/ 10000 / 1000::double precision" bit to just "/ 10000000::double precision" instead.

Example:

=> select uuid_timestamp(uuid_generate_v1()), now();
       uuid_timestamp       |              now
----------------------------+-------------------------------
 2020-04-29 17:40:54.519+00 | 2020-04-29 17:40:54.518204+00
(1 row)

Also, it is assuming the input is a v1. If you attempt to give it something like a v4, expect to get weird answers or alter the function to RAISE if it isn't v1.

=> select uuid_timestamp(uuid_generate_v4());
       uuid_timestamp
----------------------------
 4251-12-19 17:38:34.866+00
(1 row)
like image 105
vincentjames501 Avatar answered Oct 19 '22 18:10

vincentjames501


I've tested this with uuid's from my database and it seems to work very well, even without the unsigned bigints

CREATE FUNCTION uuid_timestamp(id uuid) RETURNS timestamptz AS $$
  select TIMESTAMP WITH TIME ZONE 'epoch' +
      (((('x' || lpad(split_part(id::text, '-', 1), 16, '0'))::bit(64)::bigint) +
      (('x' || lpad(split_part(id::text, '-', 2), 16, '0'))::bit(64)::bigint << 32) +
      ((('x' || lpad(split_part(id::text, '-', 3), 16, '0'))::bit(64)::bigint&4095) << 48) - 122192928000000000) / 10000000 ) * INTERVAL '1 second';    
$$ LANGUAGE SQL
  IMMUTABLE
  RETURNS NULL ON NULL INPUT;

a V1 uuid I created in the 2099 future!

select uuid_timestamp('6d248400-65b7-1243-a57a-14109fec739e');
uuid_timestamp     
------------------------
 2099-08-01 11:30:00-07
(1 row)
like image 31
Krut Avatar answered Oct 19 '22 19:10

Krut