用于在 GMT 中获取 UNIX 时间戳的快速 Java 6 实现
Posted
技术标签:
【中文标题】用于在 GMT 中获取 UNIX 时间戳的快速 Java 6 实现【英文标题】:fast Java 6 implementation for getting UNIX Timestamps in GMT 【发布时间】: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跨度>
【参考方案1】:
java.sql.Date date = ...;
long unixTimestamp = date.getTime() / 1000;
【讨论】:
问题是:GMT Epoch 与某个时间点 (date
) 之间的秒数与时区无关。
Epoch 有一个时区,date
也有一个时区... - 我认为问题在于他必须确保它们具有相同的 (GMT)。
@Roman - 一个定期创建的 java.sql.Date 对象将表示自纪元以来的毫秒 在您的本地时区,当您调用 getTime() 时,您将完全得到它.
@Olli javadocs 与您所说的完全矛盾。纪元是 1970 年 1 月 1 日 00:00:00.000 GMT。因此,如果您的日期“在不同的时区”,则它自纪元以来的毫秒数相同。您是说您的日期是从不正确的纪元创建的吗?【参考方案2】:
我不知道有什么比使用 currentTimeInMillis 并转换为秒更快。这将在几秒钟内从 EPOCH 返回,它应该非常快。对我来说,使用 Date 对象似乎没有必要。
BigDecimal seconds = BigDecimal.valueOf(System.currentTimeMillis() / 1000.0);
System.out.println(seconds);
【讨论】:
我需要 GMT。这将导致自本地时区纪元以来的秒数 但是,你不能把时区的小时差加起来吗? 是的 - 几秒钟前有同样的想法。很好 ;) 但是 - 它不适用于像 0001-01-01 这样的日期,因为从儒略历到公历的默认更改在这里会带来麻烦。不知道 java 什么时候假设 Julian - 也许在 1582 年之前?我不知道。但这在我的情况下会引起问题。我为此编辑了我的初始帖子。以上是关于用于在 GMT 中获取 UNIX 时间戳的快速 Java 6 实现的主要内容,如果未能解决你的问题,请参考以下文章