gpt4 book ai didi

java - 如何在不进行转换的情况下将Unix时间戳转换为LocalDate(Time)

转载 作者:行者123 更新时间:2023-11-30 06:17:57 25 4
gpt4 key购买 nike

我正在使用org.joda.time.LocalDate和LocalDateTime。从外部来源获得Unix时间戳,并希望从中获取LocalDate(Time)。关键是,在该外部系统的界面中定义,所有日期/时间都在UTC时区中。因此,我想避免从该时间戳到本地系统的任何默认时区进行任何隐式转换,而这可能与UTC不同。对于此类事情,有一个LocalDateTime的构造函数,因此我尝试了一下(作为示例):

System.out.println(new LocalDateTime(3600000L));
--> 1970-01-01T02:00:00.000

System.out.println(new LocalDateTime(3600000L, DateTimeZone.UTC));
--> 1970-01-01T01:00:00.000

结果让我有些惊讶。看完JavaDoc,第一个构造函数评估时间戳“在默认区域中使用ISO年代”。根据定义,Unix时间戳是从01-JAN-1970T00:00:00UTC开始的秒数(此处为毫秒)!因此,如果将值3600000(=精确地2小时,以毫秒为单位)添加到该基数,它将得出01-JAN-1970T02:00:00UTC。我的本地系统设置为欧洲/柏林(CET)时区,即UTC + 1。准确地说,我们现在有夏令时,所以它甚至应该是UTC + 2,但是假设我们现在是UTC + 1。因此,如果时间戳按照定义是UTC,那么我希望得到的时间是01:00:00(如果它将时间戳的值解释为转换为UTC的CET)或03:00:00(如果)它正确地希望时间戳记具有转换为CET的UTC值。但实际上它显示的是未转换的时间戳,距基准时间仅2个小时。
第二个构造函数应该“在指定区域中使用ISO年代”来评估时间戳。 (来自JavaDoc)因此,如果我明确指定UTC时区,则完全不会期望任何转换,而是02:00:00的时间。一个基于UTC的时间戳会导致一个本身被声明为UTC的时间,但结果应该是01:00:00!只是仔细检查一下,我显式地使用CET进行了调用,并得到与未提供任何时区相同的结果。

如此看来,该时间戳记不被认为是UTC,而是在本地时区中。创建LocalDateTime会使用它,并将从本地时区到目标时区(构造函数的第二个参数)的转换应用。首先,我想知道这是否真的可以。其次,我必须保证在我的代码中不会发生这种转换。所以我可以相信,保留第二个参数并使用默认时区可以解决问题,但是可以保证吗?还是如果我们将夏令时更改为夏令时,是否有可能发生一些奇怪的转换?即使更改本地时区也不会有任何后果,这就是为什么我们从该外部系统获得的所有时间戳都已经转换为UTC的原因。

我发现一个邪恶的场景是,时间戳应该只是一个日期(没有时间)。在这种情况下,时间戳记可以是时间设置为00:00:00的任何日期。当我使用与上面示例中的LocalDateTime相同的方式使用LocalDate时,它将时间戳转换为日期+时间(当然),并且只是减少了时间。但是,如果日期是15-JUL-2014T00:00:00UTC,并且我结尾处的结果与我其他示例中的结果偏移了一个小时,则结果变为14-JUL-2014T23:00:00,并随之移至日期2014年7月14日!这实际上是一场灾难,绝不能发生!

那么,您是否有人知道LocalDate(Time)为何会这样?或者我背后的概念是什么,我可能会误解。还是如何保证不发生转换?

最佳答案

tl; dr

您的问题令人困惑,但您似乎声称数字3_600_000L表示自1970年1月19日UTC 1970-01-01T00:00Z的纪元引用以来的毫秒数。

因此,解析为Instant

Instant                         // Represent a moment in UTC, an offset of zero hours-minutes-seconds.
.ofEpochMilli( 3_600_000L ) // Parse a count of milliseconds since 1970-01-01T00:00Z. Returns a `Instant` object.
.toString() // Generate text representing this value, using standard ISO 8601 format.

结果显示为1970年第一天凌晨1点(以UTC表示)。最后的 Z表示UTC。

1970-01-01T01:00:00Z



获取日期部分,如UTC所示。
Instant                           // Represent a moment in UTC, an offset of zero hours-minutes-seconds.
.ofEpochMilli( 3_600_000L ) // Parse a count of milliseconds since 1970-01-01T00:00Z.
.atOffset( // Convert from `Instant` (always in UTC, an offset of zero) to `OffsetDateTime` which can have any offset.
ZoneOffset.UTC // A constant representing an offset of zero hours-minutes-seconds, that is, UTC itself.
) // Returns a `OffsetDateTime` object.
.toLocalDate() // Extract the date portion, without the time-of-day and without the offset-from-UTC.
.toString() // Generate text representing this value, using standard ISO 8601 format.

1970-01-01



将该时刻从UTC调整为时区 Europe/Berlin
Instant                           // Represent a moment in UTC, an offset of zero hours-minutes-seconds.
.ofEpochMilli( 3_600_000L ) // Parse a count of milliseconds since 1970-01-01T00:00Z.
.atZone( // Convert from UTC to a particular time zone.
ZoneId.of( "Europe/Berlin" ) // A time zone is a history of the past, present, and future changes to the offset-from-UTC used by the people of a particular region.
) // Returns a `ZonedDateTime` object.
.toString() // Generate text representing this value, using standard ISO 8601 format wisely extended to append the name of the time zone in square brackets.

1970-01-01T02:00+01:00[Europe/Berlin]



请注意,该结果在柏林地区的凌晨2点不同于在UTC看到的凌晨1点。那时 Europe/Berlin时区比UTC提前一个小时,因此,凌晨1点前一小时就是凌晨2点-同一时刻,时间轴上的同一点,不同的时钟时间。

从那一刻开始获取仅日期部分,如 Europe/Berlin所示。
Instant                           // Represent a moment in UTC, an offset of zero hours-minutes-seconds.
.ofEpochMilli( 3_600_000L ) // Parse a count of milliseconds since 1970-01-01T00:00Z.
.atZone( // Convert from UTC to a particular time zone.
ZoneId.of( "Europe/Berlin" ) // A time zone is a history of the past, present, and future changes to the offset-from-UTC used by the people of a particular region.
) // Returns a `ZonedDateTime ` object.
.toLocalDate() // Extract the date only, without the time-of-day and without the time zone. Returns a `LocalDate` object.
.toString() // Generate text representing this value, using standard ISO 8601.

1970-01-01



在这种情况下,柏林地区的日期与UTC的日期相同。但是在其他情况下,日期可能会有所不同。例如,UTC的1月23日晚上9点(21:00)同时在日本东京的24日“明天”。

java.time

显然,您使用术语“Unix时间戳”表示自1970 UTC的第一刻1970-01-01T00:00Z起的毫秒数。

将该数字解析为 Instant对象。 Instant 类表示 UTC在时间轴上的某个时刻,分辨率为 nanoseconds(十进制小数的九(9)位)。
Instant instant = Instant.ofEpochMilli( 3_600_000L ) ;

instant.toString(): 1970-01-01T01:00:00Z



非常简单: Instant始终是UTC,始终是时刻,是时间轴上的一个点。

when a timestamp was supposed to be just a date (without time).



为此,请使用 LocalDate类。 LocalDate 类表示没有日期和时区的仅日期值。

时区对于确定日期至关重要。在任何给定的时刻,日期都会在全局范围内变化。例如,在 Paris France中午夜之后的几分钟是新的一天,而在 Montréal Québec中仍是“昨天”。

如果未指定时区,则JVM隐式应用其当前的默认时区。该默认值可能随时更改,因此您的结果可能会有所不同。最好将所需/期望的时区明确指定为参数。

continent/region的格式指定 proper time zone name,例如 America/Montreal Africa/Casablanca Pacific/Auckland。切勿使用3-4个字母的缩写,例如 ESTIST,因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;  

通过应用 Instant生成 ZoneId,将UTC值( ZonedDateTime)调整为另一个时区。
ZonedDateTime zdt = instant.atZone( z ) ;  

从那里,我们可以将仅日期部分提取为 LocalDate对象。
LocalDate ld = zdt.toLocalDate() ;

如果要在该日期的第一天,则必须指定时区的上下文。在任何给定时刻,日期都会随时区在全局范围内变化。当印度新的一天到来时,法国仍然是“昨天”。

始终让java.time确定一天的第一时刻。不要假设00:00。在某些日期的某些区域中,由于诸如夏令时(DST)之类的异常,一天可能在其他时间(例如01:00)开始。
ZonedDateTime zdtStartOfDay = ld.atStartOfDay( z ) ;

如果您希望看到与UTC相同的时刻,只需提取一个 Instant即可。
Instant instant = zdtStartOfDay.toInstant() ;

java.time类还具有 LocalDateTime类。请注意,此类 LocalDateTime并不代表片刻! 它不代表时间线上的一个点。除非将其放在时区的上下文中,否则它没有真正的意义。此类仅用于两种含义:
  • 区域/偏移量未知(不好的情况)。
  • 每个/任何区域/偏移都是预期的。例如,“圣诞节从2018年12月25日的00:00开始”,这意味着在不同的地方出现不同的时刻。第一个圣诞节发生在Kiribati中。然后,在每个连续的午夜向西移动通过亚洲,然后印度和之后,连续的圣诞节开始。到欧洲/非洲,最后到美洲,所以圣诞老人至少要花26个小时才能送出所有礼物。

    希望您一旦了解了核心概念并使用了精心设计的出色的java.time类,就不会觉得这项工作令人困惑。

    Table of all date-time types in Java, both modern and legacy

    关于java.time

    java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如 java.util.Date Calendar SimpleDateFormat

    现在位于Joda-Time中的maintenance mode项目建议迁移到java.time类。

    要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规范为JSR 310

    您可以直接与数据库交换java.time对象。使用兼容JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

    在哪里获取java.time类?
  • Java SE 8Java SE 9和更高版本
  • 内置。
  • 是标准Java API的一部分,具有 bundle 的实现。
  • Java 9添加了一些次要功能和修复。
  • Java SE 6Java SE 7
  • 许多java.time功能都已在ThreeTen-Backport中反向移植到Java 6和7。
  • Android
  • 更高版本的java.time类的Android bundle 实现。
  • 对于早期的Android(<26),ThreeTenABP项目改编了ThreeTen-Backport(如上所述)。参见How to use ThreeTenABP…

  • Table of which java.time library to use with which version of Java or Android

    ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如 Interval YearWeek YearQuarter more

    关于java - 如何在不进行转换的情况下将Unix时间戳转换为LocalDate(Time),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25384680/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com