Roller Fernandez
Roller Fernandez

Reputation: 309

Convert Hexadecimals to timestamp in postgres

I have the following hexadecimal data that I want to convert using the postgres functions, the data they receive is the following:

   @AVLData_TimeStamp          @AVLData_GPSElement_Altitude
   00000174C0FA7EA0            0140
   

And I want to convert them to the following data

  @MOPO_FECHAHORA             @MOPO_ALTITUD
  2020-09-24 09:37:56.000     320

I have tried to do it using this function but have not succeeded

  SELECT to_timestamp(@AVLData_TimeStamp)

I have two functions in SQLserver that do this but in postgres I don't know how to do it

First Function:

CREATE FUNCTION FUN_Unix_To_Date(
    @FechaUnix CHAR(38)
)
RETURNS DATETIME
AS
BEGIN
    DECLARE
     @LocalTime BIGINT
    ,@Adjusted  BIGINT
    ,@seconds   BIGINT = @FechaUnix

    SET @LocalTime = DATEDIFF(MS,GETDATE(),GETUTCDATE())
    SET @Adjusted = @seconds - @LocalTime

    RETURN (DATEADD(SECOND,CONVERT(BIGINT, LEFT(CAST(@Adjusted AS VARCHAR(100)),13))/1000, CAST('1970-01-01 00:00:00' AS DATETIME)))

END

Two Function:

CREATE FUNCTION HexadecimalToDec_v2(
    @hexval CHAR(38)
)
RETURNS NUMERIC(38)
AS
BEGIN
    DECLARE  @i INT
            ,@digits INT
            ,@result NUMERIC
            ,@current_digit CHAR(1)
            ,@current_digit_dec NUMERIC
    SET @digits = LEN(@hexval)
    SET @i = 0
    SET @result =0

    WHILE @i <= @digits  
    BEGIN
        SET @current_digit = SUBSTRING(@hexval, @i, 1)
        
        IF  @current_digit = 'A' OR 
            @current_digit = 'B' OR
            @current_digit = 'C' OR
            @current_digit = 'D' OR
            @current_digit = 'E' OR
            @current_digit = 'F' 

            SET @current_digit_dec = ASCII(@current_digit) - ASCII('A') + 10

        ELSE
            SET @current_digit_dec = CONVERT(INT,@current_digit)

            SET @result = (@result * 16) + @current_digit_dec

        SET @i = @i + 1
    END
RETURN(@result)
END

and finally the function is used like this

@MOPO_FECHAHORA = (dbo.Unix_To_Date(dbo.FUN_HexadecimalToDec_v2(@AVLData_TimeStamp)))

And this is the result

 @MOPO_FECHAHORA             
  2020-09-24 09:37:56.000    

Thank you very much for your help.

Upvotes: 2

Views: 399

Answers (2)

Abelisto
Abelisto

Reputation: 15624

Just to clarify the source of issue.

The problem is that the your value(s) is the "local" epoch but to_timestamp() function returns timestamp with timezone value. Lets try some example:

with t(x) as (values('2020-09-24 09:37:56'))
  select
    x::timestamp as srcts,
    x::timestamptz as srctstz,
    to_timestamp(extract(epoch from x::timestamp)) as cnvts,
    to_timestamp(extract(epoch from x::timestamptz)) as cnvtstz
  from t;

┌─[ RECORD 1 ]─────────────────────┐
│ srcts   │ 2020-09-24 09:37:56    │
│ srctstz │ 2020-09-24 09:37:56+03 │
│ cnvts   │ 2020-09-24 12:37:56+03 │ <- Here is the issue in our case
│ cnvtstz │ 2020-09-24 09:37:56+03 │
└─────────┴────────────────────────┘

As you can see the data source type is critical. That's why you got the increased (actually converted from UTC to the local time zone) value in the first try using @LaurenzAlbe answer.

To fix this issue you need to perform some "reverse" calculations:

with t(x) as (values('2020-09-24 09:37:56'))
  select
    x::timestamp as srcts,
    x::timestamptz as srctstz,
    to_timestamp(extract(epoch from x::timestamp)) as cnvts,
    (to_timestamp(extract(epoch from x::timestamp)))::timestamptz at time zone 'utc' as cnvtsrecalc,
    to_timestamp(extract(epoch from x::timestamptz)) as cnvtstz
  from t;

┌─[ RECORD 1 ]┬────────────────────────┐
│ srcts       │ 2020-09-24 09:37:56    │
│ srctstz     │ 2020-09-24 09:37:56+03 │
│ cnvts       │ 2020-09-24 12:37:56+03 │ <- Here is the issue in our case
│ cnvtsrecalc │ 2020-09-24 09:37:56    │ <- Issue fixed
│ cnvtstz     │ 2020-09-24 09:37:56+03 │
└─────────────┴────────────────────────┘

The following function wraps all this logic including the conversion of the hex value to bigint:

create or replace function hex2ts(text)
  returns timestamp
  language sql
  immutable
  strict
as $$
  select 
    (to_timestamp(('x' || lpad($1, 16, '0'))::bit(64)::bigint / 1000.0))::timestamptz at time zone 'utc'
$$;

Upvotes: 1

Laurenz Albe
Laurenz Albe

Reputation: 247235

Use the function from this answer to convert hexadecimal to decimal (you might want to use bigint rather than integer):

CREATE OR REPLACE FUNCTION hex_to_int(hexval text) RETURNS bigint
   LANGUAGE plpgsql IMMUTABLE STRICT AS
$$DECLARE
   result bigint;
BEGIN
   EXECUTE 'SELECT x' || quote_literal(hexval) || '::bigint'
   INTO result;
   RETURN result;
END;$$;

The timestamp can then be converted with

to_timestamp(hex_to_int('00000174C0FA7EA0') / 1000.0)

Upvotes: 3

Related Questions