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

2022-09-01 11:02:58

我正在Java 8中玩新的java.time包。我有一个遗留数据库,它给了我,我将其转换为。java.util.DateInstant

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

我的第一个想法是,但这给了我一个大于一天的for值。Instant显然不支持大时间单位的操作。好吧,不管怎样,都可以。Instant.plus()UnsupportedTemporalTypeExceptionLocalDateTime

所以这给了我这个代码:

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有了更好的想法。谢谢大家。

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

现在,无需执行任何其他操作,我从数据库中提取java.sql.timestamp,转换为使用.都很好。然后,我使用ISO_DATE_TIME格式将此值返回给我的用户。结果是 。LocalDateTimetimestamp.toLocalDateTime()2014-04-16T09:00:00

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

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


答案 1

我将根据我的最终解决方案和很长的评论链的摘要继续发布答案。

首先,整个转换链:

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

有必要保留时区信息,并且仍然对 Date 类似的对象执行操作,该对象可以识别日历及其中的所有上下文。否则,我们冒着隐式转换为本地时区的风险,如果我们尝试将其转换为人类可读的日期格式,则时间可能因此而改变。

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

现在,假设我们的程序将 的输入作为 .2014-04-16T13:00:00java.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);

答案 2

推荐