简体   繁体   English

如何在java 8中将UTC日期转换为UTC OffsetDateTime?

[英]How to convert UTC date to UTC OffsetDateTime in java 8?

How can I convert the date object which is already in UTC to an OffsetDateTime Object in UTC itself in Java?如何在 Java OffsetDateTime已经在 UTC 中的日期对象转换为 UTC 本身中的OffsetDateTime对象? This logic should be written on a microservice where the timezone can be entirely different.这个逻辑应该写在一个时区可以完全不同的微服务上。 So .now() and other things are ruled out, I guess.所以.now()和其他东西被排除了,我猜。 Also, I don't want to pass Timezone as params anywhere.另外,我不想在任何地方将时区作为参数传递。

Sample code:示例代码:

public OffsetDateTime convertFrom(Date source) {
    LOGGER.info("source: " + source.toString());
    LOGGER.info("instant: " + source.toInstant().toString());
    LOGGER.info("response: " + source.toInstant().atOffset(ZoneOffset.UTC).toString());
    return source.toInstant().atOffset(ZoneOffset.UTC);
}

and the output I get is:我得到的输出是:

  • source: 2018-07-11 15:45:13.0来源:2018-07-11 15:45:13.0

  • instant: 2018-07-11T19:45:13Z即时:2018-07-11T19:45:13Z

  • response: 2018-07-11T19:45:13Z回复:2018-07-11T19:45:13Z

I want my output return to be 2018-07-11 15:45:13Z for input 2018-07-11 15:45:13.0我希望我的输出返回为2018-07-11 15:45:13Z输入2018-07-11 15:45:13.0

tl;dr tl; dr

A java.util.Date and a Instant both represent a moment in UTC . java.util.DateInstant都表示UTC中的时刻 Other time zones and offsets are irrelevant. 其他时区和偏移量无关。

Instant instant = myJavaUtilDate.toInstant() 

How can I convert the date object which is already in UTC to an OffsetDateTime Object in UTC itself in Java? 如何在Java中将UTC中已经存在的日期对象转换为UTC本身中的OffsetDateTime对象?

You don't need OffsetDateTime . 您不需要OffsetDateTime Use Instant as shown above. 如上所示使用Instant

Use ZonedDateTime , not OffsetDateTime 使用ZonedDateTime而不是OffsetDateTime

You do not need OffsetDateTime . 您不需要OffsetDateTime An offset-from-UTC is merely a number of hours and minutes. 与UTC的偏移量只是数小时和数分钟。 Nothing more, nothing less. 仅此而已。 In contrast, a time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. 相反,时区是过去,现在和将来更改特定区域的人们使用的偏移量的历史。 So a time zone, if known, is always preferable to a mere offset. 因此,时区,如果已知,总是比单纯的偏移更可取。 So use ZonedDateTime rather than OffsetDateTime wherever possible. 因此,请尽可能使用ZonedDateTime而不是OffsetDateTime

Use OffsetDateTime only when given an offset-from-UTC, such as +02:00 , without the context of a specific time zone, such as Europe/Paris . 仅当给定与UTC的偏移量(例如+02:00 )且没有特定时区(例如Europe/Paris的上下文时,才使用OffsetDateTime

Convert Date to Instant Date转换为Instant

If given a java.util.Date , concert to the modern class ( Instant ) that replaced that troublesome old class. 如果给定一个java.util.Date ,则替换为该麻烦的旧类的现代类( Instant )。 Both represent a moment in UTC as a count from the same epoch reference of first moment of 1970 in UTC. 两者都表示UTC中的某个时刻,作为1970年UTC中第一时刻的相同纪元参考的计数。 The modern class resolves to nanoseconds rather than milliseconds. 现代类解析为纳秒而不是毫秒。 To convert, call new methods added to the old class. 要进行转换,请调用添加到旧类中的新方法。

Instant instant = myJavaUtilDate.toInstant() ;

Remember that both java.util.Date and Instant always represent a moment in UTC. 请记住, java.util.DateInstant 始终代表UTC的时刻。

Capture current moment, “now” 捕捉当前时刻“现在”

Capture the current moment in UTC. 在UTC中捕获当前时刻。

Instant instant = Instant.now() ;

now() and other things are ruled out, I guess. 我想排除了now()和其他功能。

No, you can always capture the current moment by calling Instant.now() on any machine at any time. 不,您始终可以随时在任何计算机上调用Instant.now()来捕获当前时刻。 The JVM's current default time zone is irrelevant as Instant is always in UTC. JVM的当前默认时区无关紧要,因为Instant 始终使用UTC。

Adjust from UTC into another time zone. 从UTC调整到另一个时区。 Same moment, same point on the timeline, different wall-clock time. 同一时刻,时间轴上的同一点,不同的时钟时间。 <— That is the most important concept to comprehend in this discussion! <—这是本次讨论中最重要的概念!

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone() ;

As a shortcut, you can skip the Instant when capturing current moment. 作为一种快捷方式,可以在捕获当前时刻时跳过即时消息。

ZonedDateTime zdt = ZonedDateTime.now( z ) ;

Move back to UTC by extracting a Instant object. 通过提取Instant对象移回UTC。

Instant instant = zdt.toInstant() ; 

Tip: Focus on UTC 提示:关注UTC

Usually best to have most of your work in UTC. 通常最好在UTC中完成大部分工作。 When storing, logging, debugging, or exchanging moments, use UTC. 在存储,记录,调试或交换时间时,请使用UTC。 Forget about your own parochial time zone while on the job as a programmer or sysadmin; 在以程序员或系统管理员的身份工作时,忘记自己的时区; learn to think in UTC. 学习在UTC中思考。 Keep a second click in your office set to UTC. 在您的办公室中第二次单击以设置为UTC。

Avoid flipping between time zones all the time. 避免一直在时区之间切换。 Stick with UTC. 坚持使用UTC。 Adjust to a time zone only when presenting to the user or when business logic demands. 仅在向用户展示或业务逻辑需求时才调整为时区。

It is already working as intended, the problem is that Date.toString is "helpfully" converting the internal timestamp to your local timezone. 它已经按预期工作,问题在于Date.toString正在“有效地”将内部时间戳转换为本地时区。 Using Date.toGMTString would result in the exact same timestamp for each of the values. 使用Date.toGMTString将为每个值产生完全相同的时间戳。

If the resulting timestamp is wrong then the problem lies in the creation of the Date instance. 如果生成的时间戳错误,则问题出在Date实例的创建上。 Using the constructor like new Date(2018, 7, 11, 15, 45, 11) would result in that date being calculated for the system timezone , not UTC. 使用像new Date(2018, 7, 11, 15, 45, 11)这样的构造函数将导致为系统时区而不是UTC计算该日期。 To create it for UTC there is Date.UTC but all these APIs have been deprecated since Java 1.1 because they are so confusing. 要为UTC创建它,需要使用Date.UTC但是自Java 1.1以来,所有这些API都已被弃用,因为它们是如此混乱。

Is this what you are asking for, 这是您要的吗?

ZonedDateTime zonedDateTime = ZonedDateTime
    .of(LocalDateTime.of(2018, 7, 11, 15, 45, 13), ZoneId.of("UTC"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss'Z'", Locale.US);
System.out.println(zonedDateTime.format(formatter));

You need to have a DateTimeFormatter if you need to format it to the given pattern. 如果需要将其格式化为给定的模式,则需要有一个DateTimeFormatter

public static OffsetDateTime convertFrom(Date source) {
    if (source instanceof Timestamp) {
        return ((Timestamp) source).toLocalDateTime()
                .atOffset(ZoneOffset.UTC);
    }
    return source.toInstant().atOffset(ZoneOffset.UTC);
}

The object that was passed to your method was a java.sql.Timestamp , not a Date . 传递给您的方法的对象是java.sql.Timestamp ,而不是Date We can see this fact from the way it was printed: 2018-07-11 15:45:13.0 is the return value from Timestamp.toString() . 我们可以从打印方式中看到这一事实: 2018-07-11 15:45:13.0Timestamp.toString()的返回值。 The Timestamp class is implemented as a subclass of Date , but this doesn't mean that we can nor should handle it as a Date . Timestamp类是作为Date的子类实现的,但这并不意味着我们也不能将其作为Date处理。 The documentation warns us: 该文档警告我们:

Due to the differences between the Timestamp class and the java.util.Date class mentioned above, it is recommended that code not view Timestamp values generically as an instance of java.util.Date . 由于上述Timestamp类和java.util.Date类之间的差异,建议代码不要将java.util.Date的实例一般地查看Timestamp值。 The inheritance relationship between Timestamp and java.util.Date really denotes implementation inheritance, and not type inheritance. Timestampjava.util.Date之间的继承关系实际上表示实现继承,而不是类型继承。

In the implementation above I have assumed that you cannot mitigate the possibility of getting a Timestamp argument, so I am handling the possibility the best I can. 在上面的实现中,我假设您无法减轻获取Timestamp参数的可能性,因此,我将尽我所能处理这种可能性。 The code is still fragile, though, because sometimes a Timestamp denotes a point in time (I should say that this is the point), at other times it denotes a date and hour of day. 但是,代码仍然很脆弱,因为有时Timestamp表示一个时间点(我应该说这是时间点),而在其他时候,它表示一天中的日期和时间。 Granted that the Timestamp does not hold a time zone in it, the two are not the same. 如果Timestamp中不包含时区,则两者是不相同的。 I understand that your sample Timestamp denotes a date and time of 2018-07-11 15:45:13.0, and you want this interpreted in UTC. 我了解您的示例Timestamp表示日期和时间为2018-07-11 15:45:13.0,并且您希望将此时间解释为UTC。 My code does that (your code in the question, on the other hand, correctly handles the situation where the Timestamp denotes a point in time). 我的代码可以做到这一点(另一方面,您问题中的代码可以正确处理Timestamp指示时间点的情况)。 Also, even though no time zone is passed in my code, its behaviour still depends on the time zone setting of your JVM. 另外,即使我的代码中未传递任何时区,其行为仍取决于JVM的时区设置。

When I pass a Timestamp of 2018-07-11 15:45:13.0 to my method above, it returns an OffsetDateTime of 2018-07-11T15:45:13Z . 当我在上面的方法中传递2018-07-11 15:45:13.0Timestamp时,它将返回OffsetDateTime2018-07-11T15:45:13Z

The double nature of Timestamp is unfortunate and confusing, and the only real solution would be if you could avoid that class completely. Timestamp的双重性质是不幸的和令人困惑的,唯一真正的解决方案是,如果您可以完全避免该类的话。 The Date class too is poorly designed, and both are outdated and replaced by java.time, the modern Java date and time API. Date类的设计也很差,并且两者都已过时,被现代Java日期和时间API java.time取代。 If you cannot avoid the old classes in your code, I certainly understand your desire to convert to the modern OffsetDateTime first thing. 如果您无法避免代码中的旧类,那么我当然理解您想要转换为现代OffsetDateTime愿望。 If on the other hand I understand correctly that the date and time comes through JSON, you may be able to parse it on your side without any of the old date and time classes, which would be a good solution to your problem. 另一方面,如果我正确地理解日期和时间是通过JSON传递的,那么您可能无需任何旧的日期和时间类就可以解析它,这将是解决您的问题的好方法。 And under all circumstances, if your real goal is to represent the point in time in a time zone neutral way, I agree with Basil Bourque in preferring an Instant over an OffsetDateTime in UTC. 在所有情况下,如果您的真正目标是以时区中立的方式表示时间点,那么我同意Basil Bourque在UTC中更喜欢Instant而不是OffsetDateTime

Link: Documentation of java.sql.Timestamp 链接: java.sql.Timestamp文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM