在DB2数据库中转换UTC时间戳
【IT168 技术文档】
解决方案细节
Java 类有一个用来存储查找字典的类变量,其中包含了可能作为输入的每个地区所有可能的拼写和命名。
tz_map = new Hashtable();
…
tz_map.put("Eastern Daylight", "EST");
tz_map.put("Eastern Standard Time", "EST");
tz_map.put("America/New_York", "EST");
…
例如,上面所有的键都对应于值 “EST"。这就是类方法为了进行时间戳转换而在内部使用的值。
注意:这里鼓励为时区使用长名,例如 "America/New_York"。但是在这个特定的实现中,我们使用了短名称,因为 UDB DB2 version 7.2 使用的是 JDK 1.1.8,该版本只能使用短名称。
查找表的填充是手动完成的。我们花了很大的精力查找每个地区的内部 Java 设置,并将具有相同 DST 规则和时区的长名与短名进行组对。
我们这样映射了 250 多个地区。如果需要的话,还可以添加新的映射。这样,在将新数据源与新的地区一起添加到数据仓库时,我们便有了所需的灵活性。
对于实际的时间戳转换,我们使用了下面的类方法:
public static java.lang.String J_CONVERT_TIMEZONE(java.lang.String
ivc_UTCtimestamp, java.lang.String ivc_timezone)
首先将输入的时间戳字符串解析成它的各个组成部分,并从那些值例化出一个 Java 日历,然后通过格式转换器(formatter)产生一个新的转换后的时间戳。细微部分没有进行转换,直接变成输出,因为 Java Calendar 没有精确到那个程度。
可以用下面的语句将该 Java 类方法注册成一个 UDF 函数:
public static java.lang.String J_CONVERT_TIMEZONE
(java.lang.String ivc_UTCtimestamp,
java.lang.String ivc_timezone)
throws Exception
{
// get the short name equivalent of the input
ivc_timezone = (String)tz_map.get(ivc_timezone);
if (ivc_timezone == null)
ivc_timezone = "GMT"; // default to UTC if entry not found
// replace the . with - so that we only have one token separator instead of two
String ivc_UTCtimestamp_new = ivc_UTCtimestamp.replace('.', '-' );
// parse, validate and convert the TS string to integers, based on the one separator
StringTokenizer st = new StringTokenizer(ivc_UTCtimestamp_new, "-");
int year = Integer.parseInt(st.nextToken());
int month = Integer.parseInt(st.nextToken());
int day = Integer.parseInt(st.nextToken());
int hour = Integer.parseInt(st.nextToken());
int min = Integer.parseInt(st.nextToken());
int sec = Integer.parseInt(st.nextToken());
String micro = st.nextToken(); // just carried over from the input
// create with the above a calendar in UTC
Calendar calUTC = Calendar.getInstance();
calUTC.clear();
calUTC.setTimeZone(TimeZone.getTimeZone("GMT"));
calUTC.set(year, month-1, day, hour, min, sec );
// prepare the formatter for the specified timezone
DateFormat formatter = new SimpleDateFormat("yyyy'-'MM'-'dd'-'HH.mm.ss", Locale.US);
TimeZone tz = TimeZone.getTimeZone(ivc_timezone);
formatter.setTimeZone(tz);
// return the new value
return formatter.format(calUTC.getTime()) + "." + micro;
}
可以从 SQL 中调用上面的 DB2 UDF,但是为了方便起见,我们创建另一个 DB2 UDF,将输入从 DB2 时间戳转换成字符串,将输出从字符串转换回 DB2 时间戳,这样输入和输出都是与 DB2 兼容的时间戳。使用的代码如下:
CREATE FUNCTION ACME.F_CONVERT_TIMEZONE (
IPTS_TIMESTAMP TIMESTAMP,
IPCH_TIMEZONE VARCHAR(30))
RETURNS TIMESTAMP
BEGIN ATOMIC
DECLARE vvch_result VARCHAR(30);
SET vvch_result = j_convert_timezone(char(IPTS_TIMESTAMP), rtrim(IPCH_TIMEZONE));
RETURN CASE vvch_result
WHEN 'null' THEN NULL
ELSE timestamp(vvch_result)
END;
END
最后,我们可以在下面这样简单的 SQL 语句中调用该函数:
SELECT ACME.F_CONVERT_TIMEZONE(TRANSACTION_TIMESTAMP, "America/Nassau")
FROM ACME.TRANSACTION_TABLE;
将来的改进和变化
我们可以很容易地添加下面这些更改,以增强这个解决方案:
添加新的地区(方法是扩展查找字典)。
添加一个地区的新同义词(方法是扩展查找字典)。
当可以得到更改版本的 JDK 时,可用开始为内部转换使用长名(要更新 Java 代码)。
创建一个反转函数 —— 该函数将以某个地区的时间戳作为参数,并返回对应的 UTC 时间戳。
注意: UTC 正逐渐成为大家喜爱的 Greenwich Mean Time (GMT) 的同义词。
结束语
在数据仓库项目的 ETL 过程中,将时间戳从一个地区转换为另一个地区的函数被证明是转换方面的主力。本文提供了以 UDB DB2 UDF 的形式运行的这类 Java 函数的代码。