【问题标题】:Jodatime time difference between 2 timezones2个时区之间的Jodatime时差
【发布时间】:2014-10-10 11:17:05
【问题描述】:

在不输入任何日期时间的情况下,我必须使用什么 api 来获取 2 个时区之间的时差。

我现在正在做的是从一个时区创建一个临时日期(午夜),然后转换为 utz,然后再次转换为另一个时区,然后计算两者的持续时间。它正在工作,但是否有一个更简单的 api 可以通过给出区域的名称来获取时差?

评论太长,所以放在这里。 服务器在差异。时区,数据/信息来自不同时区的客户端,并保存在 UTC 的数据库中。当客户请求某个日期的交易历史时,就会出现问题。看来这不是将请求的日期(客户端时区)简单地转换为 utc,我需要将服务器时区和客户端时区的时差添加到转换后的 utc 时间才能得到正确的日期。 (所以我像上面说的那样做)。现在我发现应该增加或减少时差,这取决于谁的时区在前面。好吧,无论如何感谢大家的投入。需要项目尽快运行,所以他们决定同时只使用一个时区。实施与否,我将为未来的项目寻求解决方案。 :)

【问题讨论】:

  • 您可以在不同的 TZ 中维护一个静态时间图。即时计算差异。您不必在运行时创建新日期。
  • @TJ- 请举例回答
  • @Nabin 我可以,但这并不是 OP 正在寻找的。 OP 想要 JodaTime 的东西。我的评论是一个建议。
  • 问题是当dst发生变化时,需要再次手动更改地图。
  • 这听起来像是一个 XY 问题:您想通过获取此信息来解决什么问题? Joda 很可能可以解决您的实际问题,而无需您跟踪这些信息。

标签: java jodatime


【解决方案1】:

我不确定我是否正确理解了您的问题。您是否尝试在不使用任何其他 API 的情况下获取不同时区的偏移量?

下面的代码将使用纯 Java 来实现:

String[] ids = TimeZone.getAvailableIDs();
HashMap<String, Integer> map = new HashMap<String, Integer>();
for (String id : ids) {
    TimeZone tz = TimeZone.getTimeZone(id);
    map.put(id, tz.getOffset(new Date().getTime()) / 1000 / 60); //in minutes
}
System.out.println(map.toString());

【讨论】:

  • 谢谢,我要做的是获取以UTC为基础的2个时区之间的时差(以小时为单位)。
  • 调用 tz.getOffset(..) 返回相对于 UTC 的时差。您会观察到值 Etc/UTC=0。您可以修改上面的代码以返回小时而不是分钟(注意 int 类型)。
  • 请不要使用过时的Date 类。使用来自java.time 的类。
【解决方案2】:
private final static int tzDiff = TimeZone.getDefault().getRawOffset() - TimeZone.getTimeZone("America/New_York").getRawOffset();

【讨论】:

  • TimeZone 类是多年前被 JSR 310 中定义的现代 java.time 类取代的遗留日期时间类的一部分。具体而言,替换为ZoneId & ZoneOffset.
【解决方案3】:

Joda-Time 处于维护模式

自从您提出问题后,Joda-Time 的创建者继续领导 JSR 310 为 Java 带来了一组新的日期时间类。这些类在 Java 8 中出现。

从那时起,Joda-Time项目进入维护模式,项目建议迁移到java.time

比较时区需要一点时间

在不输入任何日期时间的情况下获取两个时区之间的时差。

这没有任何意义。您必须指定一个时刻,即时间线上的一个点,以比较两个不同时区的有效偏移量。

了解offset 仅比计时本初子午线UTC 提前或落后几个小时-分钟-秒。

time zone 更多。时区是过去、现在和未来对特定地区人民使用的偏移量变化的历史记录,这些变化由政治家决定。

根据定义,对于任何特定时区,使用的偏移量都会随时间而变化。因此,在没有特定时间点的情况下尝试比较时区是没有意义的。

我现在正在做的是从一个时区创建一个临时日期(午夜),然后转换为 utz,然后再次转换为另一个时区,然后计算两者的持续时间。

我不确定你的目标,但这里是做类似事情的代码。

指定您感兴趣的一对时区。

ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ;
ZoneId zEdmonton = ZoneId.of( "America/Edmonton" ) ;

指定日期。

LocalDate ld = LocalDate.of( 2022 , Month.JANUARY , 23 ) ;

确定该日期在东京时区的开始时间。不要假设一天从 00:00 开始。某些地区的某些日期可能开始于不同的时间。让 java.time 确定一天中的第一个时刻。

ZonedDateTime firstMomentOfDayTokyo = ld.atStartOfDay( zTokyo ) ;

调整一下,看看加拿大艾伯塔省埃德蒙顿挂钟时间的某个时刻。

ZonedDateTime zdtEdmonton = firstMomentOfDayTokyo.atZoneSameInstant( zEdmonton ) ;

通过提取Instant 对象以零偏移量再次查看同一时刻。

Instant instant = firstMomentOfDayTokyo.toInstant() ;

这是要理解的关键点:firstMomentOfDayTokyozdtEdmontoninstant 都代表同一时刻,即时间线上的同时同一点。想象一下来自日本、加拿大和冰岛(他们的时区始终为零偏移)的电话会议中的三个人。如果他们都同时抬头看挂在各自墙上的时钟,他们都会看到不同的当地时间,但同时经历相同的时刻。

现在我们手头有一个 Instant 对象,我们可以着手解决您的挑战:获取这两个时区中每个时区的使用偏移量。

获取每个的时区规则。

ZoneRules rulesTokyo = zTokyo.getRules() ;
ZoneRules rulesEdmonton = zEdmonton.getRules() ;

获取此时每个区域中有效的偏移量。注意这里使用ZoneOffset 类而不是ZoneId

ZoneOffset offsetTokyo = rulesTokyo.getOffset( instant ) ;
ZoneOffset offsetEdmonton = rulesEdmonton.getOffset( instant ) ;

计算每个区域之间的差异。

Duration d = Duration.ofSeconds( offsetTokyo.getTotalSeconds() - offsetEdmonton.getTotalSeconds() ) ;

但时区之间的这种偏移比较不是解决您的潜在问题的最佳方法。继续阅读以获得更好的解决方案。

查询数据库一天

你说:

服务器处于差异状态。时区

您应该始终以不受服务器当前默认时区影响的方式进行 Java 编程。始终为各种与日期时间相关的 Java 方法指定其他可选的时区(或偏移量)参数。

以UTC格式保存在数据库中

很好。通常最好在适应 UTC 后存储时刻。一些关系数据库(例如 Postgres)在接收到类似于 SQL 标准类型TIMESTAMP WITH TIME ZONE 的类型的列的输入时会进行这样的调整。

在 JDBC 4.2 及更高版本中使用 java.time 类检索此类值。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

将这样的值写入数据库。

myPreparedStatement.setObject( … , odt ) ;

你说:

当客户端请求某个日期的交易历史时会出现问题。看来这不是将请求日期(客户端时区)简单转换为utc,我需要将服务器时区和客户端时区的时差添加到转换后的utc时间才能得到正确的日期。

不,不是最好的方法。您正在考虑当地时区。

当您以程序员、DBA 或系统管理员的身份工作时,您应该忘记您的本地时区。学会用 UTC 来思考,偏移量为零。日志记录、数据存储、数据交换和大部分业务逻辑都应该在 UTC 中完成。将桌面上的第二个时钟设置为 UTC;我是认真的,你的生活会更轻松。

例如,假设您的用户想要查询所有记录,其时间戳发生在日本东京一整天中的某个时间点。

要查询一天的记录,与我们在上面看到的大致相同。我们在东京度过了一天的第一刻。

ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ;
LocalDate ld = LocalDate.of( 2022 , Month.JANUARY , 23 ) ;
ZonedDateTime firstMomentOfDayTokyo = ld.atStartOfDay( zTokyo ) ;

这是我们要查询的时间跨度(一天)的开始。

通常最好使用半开方法来定义时间跨度。在这种方法中,开头是inclusive,而结尾是exclusive。因此,一天从日期的第一刻开始,一直到(但不包括)下一个日期的第一刻。

ZonedDateTime firstMomentOfFollowingDayTokyo = ld.plusDays( 1 ).atStartOfDay( zTokyo ) ;

OffsetDateTime 是映射到标准 SQL 中定义的 TIMESTAMP WITH TIME ZONE 类型的类。所以从每个中提取一个OffsetDateTime

OffsetDateTime start = firstMomentOfDayTokyo.toOffsetDateTime() ;
OffsetDateTime end = firstMomentOfFollowingDayTokyo.toOffsetDateTime() ;

这样编写你的 SQL:

SELECT *
FROM event_
WHERE when_ >= ?
AND when_ < ?
;

不要使用 SQL 命令BETWEEN 进行这项工作。该命令是完全关闭的而不是半打开的。

在 JDBC 中:

myPreparedStatement.setObject( 1 , start ) ;
myPreparedStatement.setObject( 2 , end ) ;

请记住,特定时区的一整天不一定是 24 小时。由于政治时间异常,例如Daylight Saving Time (DST),一天可能是 23、23.5、25 或其他一些小时数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多