Java 8 java.time:在Instant vs LocalDateTime中添加TemporalUnit

我正在使用Java 8中的新java.time包。我有一个遗留数据库,它给了我java.util.Date ,我转换为Instant

我想要做的是添加一段基于另一个数据库标志的时间。 我可以添加几天,几周,几个月或几年。 我不想关心我要添加的内容,我希望将来能够添加更多选项。

我的第一个想法是Instant.plus() ,但是对于大于一天的值,这给了我一个UnsupportedTemporalTypeException 。 瞬间显然不支持大单位时间的操作。 无论如何, LocalDateTime都可以。

所以这给了我这个代码:

 private Date adjustDate(Date myDate, TemporalUnit unit){ Instant instant = myDate.toInstant(); LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); dateTime = dateTime.plus(1, unit); Instant updatedInstant = dateTime.atZone(ZoneId.systemDefault()).toInstant(); return new Date(dueInstant.toEpochMilli()); } 

现在,这是我第一次使用新的时间API,所以我可能在这里错过了一些东西。 但是我必须离开这似乎很笨重:

 Date --> Instant --> LocalDateTime --> do stuff--> Instant --> Date. 

即使我不必使用Date部分,我仍然认为它有点尴尬。 所以我的问题是这个,我这样做完全错了,最好的办法是什么?


编辑 :扩展评论中的讨论。

我想我现在更好地了解了LocalDateTime和Instant如何使用java.util.Date和java.sql.Timestamp。 感谢大家。

现在,更实际的考虑。 假设用户向我发送了一个日期,无论他们身在何处,都是任意时区。 他们发送给我2014-04-16T13:00:00我可以解析为LocalDateTime。 然后我将它直接转换为java.sql.Timestamp并在我的数据库中保留。

现在,我没有做任何其他事情,我从我的数据库中提取java.sql.timestamp,使用timestamp.toLocalDateTime()转换为LocalDateTime 。 都好。 然后我使用ISO_DATE_TIME格式将此值返回给我的用户。 结果是2014-04-16T09:00:00

我认为这种差异是因为某种类型的隐式转换到/来自UTC。 我认为我的默认时区可能会应用于该值(EDT,UTC-4),这可以解释为什么数字关闭4小时。

新问题。 从本地时间到UTC的隐式转换在哪里? 保留时区的更好方法是什么。 我不应该直接从本地时间作为字符串(2014-04-16T13:00:00)到LocalDateTime吗? 我应该期待用户输入的时区吗?

我将继续根据我的最终解决方案发布答案​​,以及对很长评论链的总结。

首先,整个转换链:

 Date --> Instant --> LocalDateTime --> Do stuff --> Instant --> Date 

有必要保留时区信息,并仍然在Date上执行操作,就像知道日历及其中所有上下文的对象一样。 否则我们冒着隐式转换到本地时区的风险,如果我们试图将其置于人类可读的日期格式中,那么时间可能因此而改变。

例如, java.sql.Timestamp类上的toLocalDateTime()方法隐式转换为默认时区。 这对我的目的来说是不可取的,但不一定是坏行为。 但是,重要的是要意识到这一点。 这是将遗留的Java日期对象直接转换为LocalDateTime对象的问题。 由于传统对象通常假定为UTC,因此转换使用本地时区偏移量。

现在,假设我们的程序接受2014-04-16T13:00:00的输入并作为java.sql.Timestamp保存到数据库。

 //Parse string into local date. LocalDateTime has no timezone component LocalDateTime time = LocalDateTime.parse("2014-04-16T13:00:00"); //Convert to Instant with no time zone offset Instant instant = time.atZone(ZoneOffset.ofHours(0)).toInstant(); //Easy conversion from Instant to the java.sql.Timestamp object Timestamp timestamp = Timestamp.from(instant); 

现在我们采用时间戳并为其添加一些天数:

 Timestamp timestamp = ... //Convert to LocalDateTime. Use no offset for timezone LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); //Add time. In this case, add one day. time = time.plus(1, ChronoUnit.DAYS); //Convert back to instant, again, no time zone offset. Instant output = time.atZone(ZoneOffset.ofHours(0)).toInstant(); Timestamp savedTimestamp = Timestamp.from(output); 

现在我们只需要以ISO_LOCAL_DATE_TIME的格式输出为人类可读的String。

 Timestamp timestamp = .... LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); String formatted = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(time);