Java 8 Java.time:在Instant与LocalDateTime中添加临时单元

Java 8 Java.time:在Instant与LocalDateTime中添加临时单元,java,java-8,java-time,Java,Java 8,Java Time,我正在使用Java8中的新java.time包。我有一个遗留数据库,它为我提供了java.util.Date,我将其转换为Instant 我试图做的是根据另一个数据库标志添加一段时间。我可以加上几天、几周、几个月或几年。我不想关心我正在添加的内容,我希望将来能够添加更多选项 我的第一个想法是Instant.plus(),但对于大于一天的值,这给了我一个不受支持的temporaltypeexception。Instant显然不支持大时间单位上的操作。好吧,不管怎样,LocalDateTime都可以

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

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

我的第一个想法是
Instant.plus()
,但对于大于一天的值,这给了我一个
不受支持的temporaltypeexception
。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.
即使我不必使用日期部分,我仍然会觉得有点尴尬。所以我的问题是,我这样做是完全错误的吗?最好的方法是什么


编辑:在评论中展开讨论

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

现在,一个更实际的考虑。比如说,一个用户向我发送了一个来自世界任何地方的日期,任意时区。他们给我发送了
2014-04-16T13:00:00
,我可以将其解析为LocalDateTime。然后我将其直接转换为java.sql.Timestamp并保存在数据库中

现在,不做任何其他事情,我从数据库中提取java.sql.timestamp,使用
timestamp.toLocalDateTime()
转换为
LocalDateTime
。一切都好。然后使用ISO_日期_时间格式将该值返回给用户。结果是
2014-04-16T09:00:00

我假设这种差异是由于某种类型的与UTC之间的隐式转换造成的。我认为我的默认时区可能会应用于该值(EDT,UTC-4),这可以解释为什么该数字会关闭4小时


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

我会根据我的最终解决方案和很长的评论链的总结发布一个答案

首先,整个转换链包括:

Date --> Instant --> LocalDateTime --> Do stuff --> Instant --> 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\u LOCAL\u DATE\u TIME
的格式输出一个人类可读的字符串

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

你想在这里代表什么价值?
Instant
在逻辑上并不知道日历系统——它只是一个时间点——因此在日历系统中添加一个月是没有意义的。你也应该仔细考虑你是否真的想使用系统时区——你想为相同的值得到不同的结果,这取决于你在哪里运行吗?如果所有的东西都存储在你的数据库中的UTC中,你可以认为时间戳是UTC时区中的一个瞬间,并使用UTC将它转换成一个LoalDATETIME。您可以将此转换封装在您的实体中,或者使用jadira(AFAIK),它将允许您直接映射LocalDateTime。如果您将UTC指定为时区,我认为这是正确的。我会使用Date.from(即时)而不是毫秒。Hibernat的未来版本可能会直接支持新的时间类型。JDBC要求驱动程序将没有时区的时间和时间戳解释为本地时区,因此除非您的时区是utc,或者您的驱动程序与jdbc不兼容,这一假设是错误的。@jacob使用了
java.sql的连字符。Timestamp
有一个方法和一个静态值。应该不需要使用
即时
作为中介。小时的ZoneOffset.of(0)可以更好地表示为ZoneOffset.UTC。您还可能会发现使用ZonedDateTime更容易,因为该类支持LocalDateTime的所有加减行为,但保留了时区信息。对于天的特定用例,您不需要(本地/分区)日期时间,因为可以将天的持续时间直接添加到瞬间:
Timestamp.from(Timestamp.toInstant().plus)(Duration.of Days(1))
Duration.of Days
添加到
即时
将不考虑夏令时或类似情况,因此不建议这样做。改为将其转换为
ZonedDateTime
。什么是“建议的”取决于您想要的行为。是否要添加精确为24小时的单位?然后使用Instant.plus。当然,如果您想要计算与输入相同的时间,但在n天时间内,您需要一个时区,因此首先不应该使用Instant?ZoneOffset.UTC和/或ZoneOffset