Home > Back-end >  Convert HEXTORAW value to TIMESTAMP
Convert HEXTORAW value to TIMESTAMP

Time:06-12

I am working on some analysis tools and I have a specific situation where I have a value that is represented using the HEXTORAW Oracle function as follows:

HEXTORAW('7876031b0d233900786450')

This value represents what would otherwise be written as TO_TIMSTAMP('2018-03-27 12:34:56.00789'). So far I have found two ways to attempt to convert this HEXTORAW value:

  • Using dbms_stats.convert_raw_to_date
  • Using dbms_stats.convert_raw_value in a PL/SQL block

In both cases, the return value doesn't have the fractal seconds portion of the timestamp and I suspect this is likely due to the underlying calls relying on a DATE data type rather than a true TIMESTAMP data type.

Does anyone know if there is another PL/SQL package function that could be used to decode the raw value back to a complete timestamp that includes the fractal seconds?

CodePudding user response:

There is not a function that converts a hex-string to a TIMESTAMP but you can easily calculate the TIMESTAMP value.

Convert the first 7 bytes to a DATE and then cast it to a TIMESTAMP(9) (which will have fractional seconds of 0) and then you can convert the last 4 bytes to a number, which represents the number of nanoseconds and then add that to the timestamp:

SELECT CAST(
         DBMS_STATS.CONVERT_RAW_TO_DATE(HEXTORAW(SUBSTR(hex_value,1,14)))
         AS TIMESTAMP(9)
       )
         INTERVAL '1' SECOND(9)
         * TO_NUMBER(HEXTORAW(SUBSTR(hex_value,15,8)), 'XXXXXXXX') / 1e9
         AS timestamp_val,
       hex_value
FROM   (
  SELECT '7876031b0d233900786450' AS hex_value FROM DUAL
)

Outputs:

TIMESTAMP_VAL HEX_VALUE
2018-03-27 12:34:56.007890000 7876031b0d233900786450

and

CREATE TABLE table_name (value TIMESTAMP(9));
INSERT INTO table_name (value) VALUES (TIMESTAMP '2018-03-27 12:34:56.007890000');

SELECT value, DUMP(value,16) FROM table_name

Outputs:

VALUE DUMP(VALUE,16)
2018-03-27 12:34:56.007890000 Typ=180 Len=11: 78,76,3,1b,d,23,39,0,78,64,50

Which has the same timestamp and same byte values.


If you want to wrap it into a function then:

CREATE FUNCTION hex_to_timestamp(
  hex_value IN VARCHAR2
) RETURN TIMESTAMP DETERMINISTIC
IS
BEGIN
  RETURN CAST(
           DBMS_STATS.CONVERT_RAW_TO_DATE(HEXTORAW(SUBSTR(hex_value,1,14)))
           AS TIMESTAMP
         )
           INTERVAL '1' SECOND
           * TO_NUMBER(HEXTORAW(SUBSTR(hex_value,15,8)), 'XXXXXXXX') / 1e9;
END;
/

db<>fiddle here


Update

Perhaps the "simplest" method is to use something that will already convert a byte string to a timestamp. In this case, the Java class oracle.sql.TIMESTAMP has a constructor that takes a byte array (and there is a similar constructor for oracle.sql.TIMESTAMPTZ).

Therefore, if you have Java enabled in the database, you can use a small class to convert the hex-string to a byte array and then to wrap the constructors in functions:

CREATE AND COMPILE JAVA SOURCE NAMED HexToTimestampConverter AS
import oracle.sql.TIMESTAMP;
import oracle.sql.TIMESTAMPTZ;

public class HexToTimestampConverter {
  public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i  = 2) {
      data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             Character.digit(s.charAt(i 1), 16));
    }
    return data;
  }

  public static TIMESTAMP hexToTimestamp(final String hex)
  {
    return new TIMESTAMP(hexStringToByteArray(hex));
  }

  public static TIMESTAMPTZ hexToTimestamptz(final String hex)
  {
    return new TIMESTAMPTZ(hexStringToByteArray(hex));
  }
}

Then you can create an SQL function to call the Java code:

CREATE FUNCTION hex_to_timestamp( in_value IN VARCHAR2 ) RETURN TIMESTAMP
AS LANGUAGE JAVA NAME 'HexToTimestampConverter.hexToTimestamp(java.lang.String) return oracle.sql.TIMESTAMP';
/

And for TIMESTAMP WITH TIME ZONE:

CREATE FUNCTION hex_to_timestamptz( in_value IN VARCHAR2 ) RETURN TIMESTAMP WITH TIME ZONE
AS LANGUAGE JAVA NAME 'HexToTimestampConverter.hexToTimestamptz(java.lang.String) return oracle.sql.TIMESTAMPTZ';
/

(Note: you probably want to add some error checking to check that the string is hexadecimal and of the correct length.)

db<>fiddle here

  • Related