【发布时间】:2015-12-07 01:46:00
【问题描述】:
我正在寻找一种与 Java 6 兼容的方式,以从 java.sql.Date 对象中生成 自 GMT 纪元以来的秒数 / UTC(又名 UNIX 时间戳)。
我的 Java 8 代码是:
Long seconds = dateObject.toLocalDate().atStartOfDay(ZoneId.of("GMT")).toEpochSecond()
我的 Java 6 代码是:
final Calendar cal = Calendar.getInstance();
cal.setTime(dateObject);
final int year = cal.get(Calendar.YEAR);
final int month = cal.get(Calendar.MONTH);
final int day = cal.get(Calendar.DAY_OF_MONTH);
final GregorianCalendar c = new GregorianCalendar();
c.clear();
c.setGregorianChange(new Date(Long.MIN_VALUE));
c.setTimeZone(TimeZone.getTimeZone("GMT"));
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
Long seconds = c.getTimeInMillis() / 1000
不幸的是,Java 6 代码的速度大约是 Java 8 代码的一半。过去我有一个 Java 6 解决方案,代码更少(因此更快),但是当涉及到“0001-01-01”这样的不寻常日期时它是错误的,其中需要setGregorianChange(),而第二个GergorianCalendar 进来了。
知道我可以做些什么来获得快速的 Java 6 实现吗?
- 我需要线程安全
- “2010-01-01”必须导致 1262304000
- “0001-01-01”必须导致 -62135596800
- “2999-12-31”必须导致 32503593600
编辑 1
我还尝试了以下 Java 6 代码:
final TimeZone tz = TimeZone.getDefault();
Long seconds = (dateObject.getTime() + tz.getRawOffset()) / 1000;
这适用于“2010-01-01”和“2999-12-31”,但不适用于“0001-01-01”(预期:-62135596800,实际:-62135769600)-猜猜缺少的“setGregorianChange”是这里的麻烦。
编辑 2
代码在 JsonSerializer 中运行。我有一个应用程序在 JSON 中显示这些 UNIX 时间戳。消费方期望时间戳为 GMT 时间戳,并将通过此时间戳将时区硬编码为 GMT 创建日期,但稍后将其用作本地时区。
为了让问题更清楚,我将举一个例子。
- 我这边日期:2010-01-01 (UTC+100)
- 毫秒:1262300400000
- 秒:1262300400
消费者现在利用这一秒数并从中形成一个日期:
- 格林威治标准时间 2009 年 12 月 31 日星期四 23:00:00
当然 - 这是 100% 正确的,但消费者确实使用此日期,就好像它是本地时区日期...
在这个例子中,我期望传输的秒数是 1262300400 + 3600(我的 TZ 偏移量)。
编辑 3
我将GregorianCalendar 的东西降到了 4liner - 但即使这样也比 Java 8 的东西慢得多。问题是每次执行代码时都会创建一个新的GregorianCalendar 对象。但这是必须做的,因为Calendar 不是线程安全的。
因此,一个快速的解决方案必须以某种方式在没有任何对象实例化的情况下工作。
下面的例子很像 EDIT 1,甚至比 Java 8 代码还要快。它只是在 Julian -> Gregorian switch (15-Oct-1582) 之前的日期不能正常工作。
public class UnixTimestampSerializer extends JsonSerializer<Date> {
@Override
public void serialize(final Date dateObject, final JsonGenerator jgen, final SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeNumber((dateObject.getTime() - dateObject.getTimezoneOffset() * 60 * 1000) / 1000)
}
}
不幸的是,它适用于 a) 不推荐使用的方法,并且 b) 不能正常使用 1582 之前的日期:(。使用静态 TimeZone.getDefault().getRawOffset() 来解决 a) 不是一种选择,因为 TimeZone 可能会改变(夏季- vs . Wintertime) 在每个日期。
【问题讨论】:
-
我会成为“那个人”,问你是否尝试过 joda time joda.org/joda-time ?
-
为什么你得到一个日历实例并继续创建另一个 GregorianCalendar,完全忽略了之前获得的日历。从删除死代码开始?
-
@DamienO'Reilly - 我想要一个没有任何依赖关系的解决方案。
-
@Olli 如果您阅读文档 java.sql.Date 是时间戳的包装。 #getTime() 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。您可能会更改他的时区以进行表示,但它仍然是相同的时间戳。
-
@Durandal - 我可以创建一个
GregorianCalendar,调用setTime像上面那样进行y/m/d 提取,然后继续clear等等。这确实会为我节省第一个Calendar.getInstance(),但不会改变执行时间。 127,980 次执行 原始 Java6 代码:_35,402ms_,One-Calendar-Instance-Java6 代码:35,794ms,Java8 代码:19,086ms跨度>
标签: java date unix-timestamp gmt