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

Java 如何在不进行转换的情况下将unix时间戳转换为LocalDate(Time),java,jodatime,unix-timestamp,Java,Jodatime,Unix Timestamp,我正在使用org.joda.time.LocalDate和LocalDateTime。我从外部源获得一个Unix时间戳,并希望从中生成一个LocalDate(Time)。关键是,在该外部系统的接口中定义,所有日期/时间均为UTC时区。因此,我希望避免将时间戳隐式转换为本地系统的任何默认时区,这可能与UTC不同。对于这些东西,有一个LocalDateTime的构造函数,所以我尝试(作为示例): 结果有点让我吃惊。查看JavaDoc后,第一个构造函数评估时间戳“在默认区域中使用ISO年表”。根据定义

我正在使用org.joda.time.LocalDate和LocalDateTime。我从外部源获得一个Unix时间戳,并希望从中生成一个LocalDate(Time)。关键是,在该外部系统的接口中定义,所有日期/时间均为UTC时区。因此,我希望避免将时间戳隐式转换为本地系统的任何默认时区,这可能与UTC不同。对于这些东西,有一个LocalDateTime的构造函数,所以我尝试(作为示例):

结果有点让我吃惊。查看JavaDoc后,第一个构造函数评估时间戳“在默认区域中使用ISO年表”。根据定义,Unix时间戳是从197001-JAN到0:00:00UTC的秒数(这里是毫秒)!因此,如果将值3600000(=正好2小时,单位为毫秒)添加到该基数中,它将成为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的任何日期。当我使用LocalDate的方式与上面示例中使用LocalDateTime的方式相同时,它会将时间戳转换为日期+时间(当然),并简单地缩短时间。但是,如果日期是2014年7月15日00:00:00UTC,并且我的终点的结果与我的另一个示例中的结果移动了一个小时相同,则会变为2014年7月14日23:00:00,并随之变为2014年7月14日!这实际上是一场灾难,决不能发生

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

为什么不:

timeStamp.toLocalDateTime().toLocalDate(); // JAVA 8
newlocaldatetime(3600000L,DateTimeZone.UTC)
没有真正意义:
LocalDateTime
根据定义,在您的时区中。因此,它的作用是:假设时间戳是使用本地时区获取的,并且您想知道UTC时区中的时间戳是什么。这正是您想要执行的相反转换

尝试
新建日期时间(3600000L).toLocalDateTime()
将UTC时间戳转换为本地时间

[编辑]你说得对,我上面的建议有误导性。文件说:

LocalDateTime是一个不可修改的datetime类,表示没有时区的datetime

所以这个东西是“当前时区”的局部-不管是什么。创建格式化程序时,它隐式地获取时区(默认时区)。因此,当你格式化这样一个本地时间时,它将“移动”到你的时区,因为它本身没有时区

您可以使用此类型来表示“12:00”的概念,而不带时区。如果添加到新加坡的日期,它将继承新加坡的时区。所以你可以用它来计算日期,比如“我想在世界上不同的城市里为“9:00”得到一个
DateTime
。”

另一方面,
DateTime
有一个固定的时区,不随上下文而变化。如果不给它一个,那么Java虚拟机的当前时区将是默认时区

有了这些知识,
newdatetime(3600000L,DateTimeZone.UTC)。toLocalDateTime()
显然必须导致
1970-01-01T01:00:00.000

首先,我们创建一个带有固定时区(UTC)的
DateTime
。当你单独格式化时,格式化程序会看到“哦,这有一个时区,所以我可以使用它。”现在你将它转换成一个本地时间,去掉时区信息,格式化程序将使用默认值

要解决您的问题,请使用以下代码:

new DateTime(3600000L, DateTimeZone.UTC).withTimeZone(DateTimeZone.getDefault())
应与以下内容相同:

new DateTime(3600000L)
因为所有的时间戳都是相对于
1970-01-01T00:00:00Z
Z
==UTC时区)。

tl;博士 您的问题令人困惑,但您似乎声称数字3_600_000L表示自UTC 1970年第一时刻(1970-01-01T00:00Z)的历元参考以来的毫秒计数

因此,解析为一个
即时

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

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.
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.
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.
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.
Instant instant = Instant.ofEpochMilli( 3_600_000L ) ;
ZoneId z = ZoneId.of( "America/Montreal" ) ;  
ZonedDateTime zdt = instant.atZone( z ) ;  
LocalDate ld = zdt.toLocalDate() ;
ZonedDateTime zdtStartOfDay = ld.atStartOfDay( z ) ;
Instant instant = zdtStartOfDay.toInstant() ;