2

我有一个数据库表,其中填充了通过 ETL 来自大型机的数据。该表的一列称为“TOD”,如 Time-Of-Day。

此列存储以下值:“CAE7631DC43DC686”“CAE7631C4AC6DC0B”“CAE6216DF2BC0D04”“CAE621D8F9916E8E”

所有这些值都在 2013 年 2 月 10 日和 2013 年 2 月 11 日左右。现在,在大型机上,这是一个时间日期表示(TOD 时钟)。

它以宏秒(1/1 000 000 秒)表示从 01.01.1900 过去的时间。

我需要的是一个可以将这些字符串转换为 java.util.Date 的 java 库/方法/算法实现。

在网上找到这些网站: http: //paul.saers.com/Tod_howto.html http://www.longpelaexpertise.com.au/toolsTOD.php

这个页面解释了如何计算它,但对我来说有点太多了。我确定我会在某处犯一些错误。

所以,我的问题是;你知道我可以使用的图书馆(Joda Time 吗?)?我需要将这些值转换为 java.util.Date 并将 Date 对象转换为字符串表示形式(如“CAE621D8F9916E8E”)。

提前致谢。

4

3 回答 3

4

在我的用例中,我有一个 getter 方法,它直接将 8 字节 TOD 读取为字节数组并将其转换为 long,但这里要遵守海报:

BigInteger bi = new BigInteger    ("CAE7631DC43DC686", 16); //  no strip off of 686 
long tod = bi2.longValue();

我使用以下方法来避免 BigDecimal 计算开销:

tod = tod >>> 12;  // remove rightmost 3 bytes and replace with zeros
tod = tod - 2208988800000000l;  // substract 1970
tod = tod/1000; // make millis out of micros
// timeformatter and dateformatter without Joda
SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss.SS z Z", Locale.getDefault());
SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
// Display
System.out.println(timeFormatter.format(new Date(tod)));
System.out.println(dateFormatter.format(new Date(tod)));

输出将是:

22:59:46.420 欧洲中部时间 +0100

10.02.2013

于 2015-09-24T07:10:21.040 回答
2

一步一步,使用 Joda:

计算中使用的数据可以在您引用的网站上找到您提供其他参考说明 TOD 以 UTC 表示

// we start with your string minus the three last digits
// which are some internal z/Series cruft
BigInteger bi = new BigInteger    ("CAE7631DC43DC", 16); // 686 stripped off
// then, from tables the website we get the TOD value for start of epoch
// here also, minus the three last digits                                 
BigInteger startOfEpoch70 = new BigInteger ("7D91048BCA000", 16); // 000 stripped off
// using that we calculate the offset in microseconds in epoch
BigInteger microsinepoch = bi.subtract(startOfEpoch70);
// and reduce to millis
BigInteger millisinepoch = microsinepoch.divide(new BigInteger("1000"));
// which we convert to a long to feed to Joda
long millisinepochLong = millisinepoch.longValue();
// Et voila, the result in UTC
DateTime result = new DateTime(millisinepochLong).withZone(DateTimeZone.UTC);
// Now, if you want a result in some other timezone, that's equally easy
// with Joda:
DateTime result2 = result.toDateTime(DateTimeZone.forID("EET"));

System.out.println("The result is " + result + " or represented in timezone EET "
                   + result2);

这给出了这个输出:

结果为 2013-02-10T21:59:46.420Z 或以 EET 时区 2013-02-10T23:59:46.420+02:00 表示

我所指的“cruft”解释如下:

我们跳过最后 12 位(通常,这些位中的一些位被 MVS 用来判断哪个处理器用于读取 TOD 时钟以及哪个 LPAR 处于活动状态)。

当然,与其从字符串中粗暴地剪掉这些字节,还可以这样做

bi = bi.divide(new BigInteger("1000", 16));

因为除以十六进制 1000 也会去掉最后 12 位。

编辑:正如 Mehmet 在评论中指出的那样,TOD 是 UTC,这意味着应该告诉生成的 DateTime。为方便起见,我还展示了如何将该 DateTime 转换为另一个时区(EET用作示例)

于 2013-02-11T18:08:22.280 回答
1

使用 BigInteger 解析您的十六进制日期:

new BigInteger("CAE7631DC43DC686", 16);

然后使用 BigInteger 提供的各种方法(乘法,...)对 Unix 纪元进行必要的转换。

于 2013-02-11T17:31:34.127 回答