问题描述
是否有一种方法可以从Oracle PL / SQL中的ULID数据中提取时间戳?最近,我们不得不与一家供应商合作,该供应商使用ULID为变更数据提供唯一的变更序列标识符,并且需要提取时间戳。如创建者的github所述,解决方案还可以使用其他语言,但是我们希望能够在Oracle中运行转换。
解决方法
这花费了太长时间才能实施,我想节省别人尝试自己做的时间
DECLARE
p_Ulid VARCHAR2(200) := '01EG664DVCY5NTH7WFN57PA7TM'; --Example ULID
FUNCTION Get_Ulid_Ts(p_In VARCHAR2) RETURN TIMESTAMP
WITH TIME ZONE IS
Dec_Value NUMBER := 0;
t_Time_Part VARCHAR2(10) := Substr(p_In,10); --First 10 characters are the timestamp
Ret TIMESTAMP WITH TIME ZONE := To_Timestamp_Tz('19700101 +00:00','yyyymmdd TZH:TZM'); --Unix timestamp sentinal
c_Base NUMBER := 32;
c_Base32 VARCHAR2(32) := '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; --Crockford's base32
TYPE B32_Map_Typ IS TABLE OF NUMBER INDEX BY VARCHAR2(1);
B32map B32_Map_Typ;
BEGIN
--initialize base32 map
FOR i IN 0 .. Length(c_Base32) - 1
LOOP
B32map(Substr(c_Base32,i + 1,1)) := i;
END LOOP;
--convert base 32 to base 10
FOR i IN 1 .. Length(t_Time_Part)
LOOP
Dec_Value := Dec_Value +
Power(c_Base,i - 1) *
B32map(Substr(t_Time_Part,-i,1));
END LOOP;
--add to unix timestamp sentinal
Ret := Ret +numtodsinterval(((Dec_Value/ 1000) ),'SECOND') ;
RETURN Ret;
END;
BEGIN
--ISO8601 timestamp formats
EXECUTE IMMEDIATE q'!alter session set nls_timestamp_format = 'YYYY-MM-DD"T"HH24:MI:SS.ff3"Z"' !';
EXECUTE IMMEDIATE q'!alter session set nls_timestamp_tz_format = 'YYYY-MM-DD"T"HH24:MI:SS.ff3 TZR' !';
--test function
Dbms_Output.Put_Line(Get_Ulid_Ts(p_Ulid));
Dbms_Output.Put_Line(Get_Ulid_Ts(p_Ulid) At TIME ZONE
'America/New_York');
END;
/
如果有人有更高效的服务,请发表评论。 (我也没有足够的声誉在StackOverflow中创建“ ulid”标签)
以下是一个包装变体,可以稍微改善性能:
CREATE OR REPLACE PACKAGE LUTOOLS.Ulid IS
Nls_Timestamp_Format VARCHAR2(64) := 'YYYY-MM-DD"T"HH24:MI:SS.ff3"Z"';
Nls_Timestamp_Tz_Format VARCHAR2(64) := 'YYYY-MM-DD"T"HH24:MI:SS.ff3"Z"';
TYPE B32_Map_Typ IS TABLE OF NUMBER INDEX BY VARCHAR2(1);
B32map B32_Map_Typ;
c_Base NUMBER := 32;
c_Base32 VARCHAR2(32) := '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; --Crockford's base32
FUNCTION Get_Ulid_Ts(p_In VARCHAR2) RETURN TIMESTAMP
WITH TIME ZONE;
END Ulid;
/
CREATE OR REPLACE PACKAGE BODY LUTOOLS.Ulid IS
FUNCTION Get_Ulid_Ts(p_In VARCHAR2) RETURN TIMESTAMP
WITH TIME ZONE IS
Dec_Value NUMBER := 0;
t_Time_Part VARCHAR2(10) := Substr(p_In,'yyyymmdd TZH:TZM');
BEGIN
--convert base 32 to base 10
FOR i IN 1 .. Length(t_Time_Part)
LOOP
Dec_Value := Dec_Value +
Power(c_Base,1));
END LOOP;
--add to unix timestamp sentinal
Ret := Ret + Numtodsinterval(((Dec_Value / 1000)),'SECOND');
RETURN Ret;
END;
BEGIN
--initialize base32 map
FOR i IN 0 .. Length(c_Base32) - 1
LOOP
B32map(Substr(c_Base32,1)) := i;
END LOOP;
END Ulid;
/