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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22793315/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:54:24  来源:igfitidea点击:

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

javajava-8java-time

提问by jacobhyphenated

I'm playing around with the new java.time package in Java 8. I have a legacy database that gives me java.util.Date, which I convert to Instant.

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

What I am trying to do is add a period of time that is based off of another database flag. I could be adding days, weeks, months, or years. I don't want to have to care what I am adding, and I would like to be able to add more options in the future.

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

My first thought was Instant.plus(), but that gives me an UnsupportedTemporalTypeExceptionfor values greater than a day. Instant apparently does not support operations on large units of time. Fine, whatever, LocalDateTimedoes.

我的第一个想法是Instant.plus(),但这给了我一个UnsupportedTemporalTypeException大于一天的值。Instant 显然不支持大时间单位的操作。好吧,随便吧LocalDateTime

So that gives me this code:

所以这给了我这个代码:

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());
}

Now, this is my first time using the new time API, so I may have missed something here. But it seems clunky to me that I have to go:

现在,这是我第一次使用新的时间 API,所以我可能在这里遗漏了一些东西。但对我来说,我必须去似乎很笨重:

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

Even if I did not have to use the Date part, I would still think it was a bit awkward. So my question is this, am I doing this completely wrong and what is the best way to do this?

即使我不必使用Date部分,我仍然会觉得它有点尴尬。所以我的问题是,我这样做是否完全错误,最好的方法是什么?



Edit: Expanding on the discussion in the comments.

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

I think I have a better idea now about how LocalDateTime and Instant are playing with java.util.Date and java.sql.Timestamp. Thanks everyone.

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

Now, a more practical consideration. Let's say a user sends me a date from wherever they are in the world, arbitrary time zone. They send me 2014-04-16T13:00:00which I can parse into a LocalDateTime. I then convert this directly to a java.sql.Timestamp and persist in my database.

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

Now, without doing anything else, I pull my java.sql.timestamp from my database, convert to LocalDateTimeusing timestamp.toLocalDateTime(). All good. Then I return this value to my user using the ISO_DATE_TIME formatting. The result is 2014-04-16T09:00:00.

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

I assume this difference is because of some type of implicit conversion to/from UTC. I think my default time zone may be getting applied to the value (EDT, UTC-4) which would explain why the number is off by 4 hours.

我认为这种差异是因为某种类型的与 UTC 之间的隐式转换。我认为我的默认时区可能会应用于值(EDT、UTC-4),这将解释为什么该数字会减少 4 小时。

New question(s). Where is the implicit conversion from local time to UTC happening here? What is the better way to preserve time zones. Should I not be going directly from Local time as a string (2014-04-16T13:00:00) to LocalDateTime? Should I be expecting a time zone from the user input?

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

采纳答案by jacobhyphenated

I will go ahead and post an answer based on my final solution and a sort of summary of the very long comment chain.

我将继续根据我的最终解决方案和非常长的评论链的总结发布一个答案。

To start, the whole conversion chain of:

首先,整个转换链:

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

Is necessary to preserve the time zone information and still do operations on a Date like object that is aware of a Calendar and all of the context therein. Otherwise we run the risk of implicitly converting to the local time zone, and if we try to put it into a human readable date format, the times may have changed because of this.

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

For example, the toLocalDateTime()method on the java.sql.Timestampclass implicitly converts to the default time zone. This was undesirable for my purposes, but is not necessarily bad behavior. It is important, however, to be aware of it. That is the issue with converting directly from a legacy java date object into a LocalDateTimeobject. Since legacy objects are generally assumed to be UTC, the conversion uses the local timezone offset.

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

Now, lets say our program takes the input of 2014-04-16T13:00:00and save to a database as a java.sql.Timestamp.

现在,假设我们的程序接受输入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);

Now we take a timestamp and add some number of days to it:

现在我们获取一个时间戳并为其添加一些天数:

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);

Now we just need to output as a human readable String in the format of ISO_LOCAL_DATE_TIME.

现在我们只需要以ISO_LOCAL_DATE_TIME.

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