Java 8 java.time : Adding TemporalUnit in Instant vs LocalDateTime

标签 java java-8 java-time

我正在使用 Java 8 中的新 java.time 包。我有一个旧数据库,它为我提供 java.util.Date,我将其转换为 Instant.

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

我的第一个想法是 Instant.plus(),但是对于大于一天的值,这给了我一个 UnsupportedTemporalTypeException。 Instant 显然不支持大时间单位的操作。好吧,不管 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 的格式输出为人类可读的字符串。

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

关于Java 8 java.time : Adding TemporalUnit in Instant vs LocalDateTime,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22793315/

相关文章:

Java 8 性能对比。 Java 7

java-8 - 如何在一个 DateTimeFormatter Java 8 中处理所有区域偏移

java - 有没有办法在java项目中全局设置jdbc查询超时

Java 数据库 ID - 原始类型或包装类型

java - Scala、SQL Server - 如何使用 Scala 将当前时间戳作为日期时间插入 SQL Server 中?

java - 如何在Java中执行sudo命令并获得错误输出?

java - 迭代时在Java8中修改流中的对象

java - EBean (4.5.x) 是否支持映射 Java 8 的时间类型,例如 OffsetDateTime

Java String to Date 格式为 “MM/dd/yyyy HH:mm:ss a” 的对象

java - 如何在java中将时间格式转换为日期格式的今天时间?