使用 Joda-Time 获取给定日期和时区的 UTC 偏移量

Posted

技术标签:

【中文标题】使用 Joda-Time 获取给定日期和时区的 UTC 偏移量【英文标题】:Using Joda-Time to get UTC offset for a given date and timezone 【发布时间】:2013-01-14 04:28:39 【问题描述】:

我的日期格式为 2013 年 1 月 8 日、2012 年 8 月 8 日等,它们有自己的特定时区。例如,2013 年 1 月 20 日的时区 ID 可能是澳大利亚/墨尔本,而 08Aug2012 的时区 ID 可能是欧洲/伦敦。我想要做的是,根据这些时区和日期,计算给定日期该时区的 UTC 偏移量。到目前为止,我已经想出了这个:

DateTimeFormatter dtf = DateTimeFormat.forPattern("ZZ");
DateTimeFormatter dtf1 = DateTimeFormat.forPattern("ddMMMYYYY");

DateTimeZone zone = DateTimeZone.forID("Australia/Melbourne");  

DateTime thisDate = dtf1.parseDateTime("30Jul2013");                                            
System.out.println("\nZone: " + thisDate.withZone(zone));

这给了我输出:

Zone: 2013-07-30T00:00:00.000+10:00

这是正确的,但我想从中提取 UTC 偏移量,在本例中为 +10:00。我一直在寻找方法来做到这一点,但找不到任何东西。有什么办法可以做到这一点吗?我看到的唯一选择是将输出转换为字符串并使用子字符串方法获取 UTC 偏移量。

上面的代码确实考虑了 DST(夏令时)。例如,如果我有: 日期时间 thisDate = dtf1.parseDateTime("30Jan2013");

输出将是:2013-01-30T00:00:00.000+11:00

(最后是+11:00,而不是+10:00)

所以基本上我需要做的就是找到一种方法从 2013-07-30T00:00:00.000+11:00 中提取 +11:00。请帮忙!

【问题讨论】:

您需要时间和日期来确定与 UTC 的偏移量。例如,在美国的 DST 转换日,当偏移量在凌晨 2 点跳跃一小时时,凌晨 1 点的偏移量与凌晨 4 点的偏移量不同。一个日期,两个偏移量。 【参考方案1】:

如果您只需要时区偏移量,请使用DateTimeZone.forID() 获取时区,然后使用tz.getOffset(instant) 获取与UTC 的偏移量(以毫秒为单位)。

您需要一个瞬间来计算与 UTC 的偏移量可能看起来很奇怪,但这是考虑夏令时以及时区变化所必需的。是的,国家偶尔会改变他们的时区:

Why does timezone data change?

时区设置在本地采用,没有世界时区权威。

EDIT这会给你正确的结果:

    DateTimeFormatter dtf1 = DateTimeFormat.forPattern("ddMMMYYYY");

    DateTimeZone zone = DateTimeZone.forID("Australia/Melbourne");  

    DateTime thisDate = dtf1.parseDateTime("30Jul2013").withZone(zone);                                            
    assertEquals( 10 * CommonConstants.MILLISECONDS_PER_HOUR,
        zone.getOffset( thisDate ) );

thisDate.get

【讨论】:

对不起,上面的代码确实考虑了 DST(夏令时)。例如,如果我有: DateTime thisDate = dtf1.parseDateTime("30Feb2013");输出将是:2013-07-30T00:00:00.000+11:00(最后是 +11:00 而不是 +10:00) 很抱歉下面的代码是如何工作的? assertEquals(10 * CommonConstants.MILLISECONDS_PER_HOUR, zone.getOffset(thisDate));它给了我一个 CommonConstants 的错误。你从哪里得到的? 这是我编写的一个类,其中包含我一直使用的常量。主要目的是使代码可读。你不同意你很容易解决这个问题吗?【参考方案2】:

获取时区名称和小时偏移量的简单方法

public static String getCurrentTimeZoneOffset() 
    DateTimeZone tz = DateTimeZone.getDefault();
    Long instant = DateTime.now().getMillis();

    String name = tz.getName(instant);

    long offsetInMilliseconds = tz.getOffset(instant);
    long hours = TimeUnit.MILLISECONDS.toHours( offsetInMilliseconds );
    String offset = Long.toString( hours );

    return name + " (" + offset + " Hours)";
    // Example: "Mountain Standard Time (-7 Hours)"

几个注意事项:

这会从 JodaTime 获取默认的 DateTimeZone。您可以对其进行修改以接受传递给该方法的特定 DateTimeZone。 这会以“山地标准时间(-7 小时)”之类的格式返回它,但您可以很容易地按照您认为合适的格式对其进行格式化。

希望对您有所帮助。

日本

【讨论】:

警告:偏移量可能包括半小时,例如在印度。所以“长时间”会错过它。 不要使用TimeUnit(如@auval 注释那样删除小数),只需使用tz.getName(instant)【参考方案3】:

为了让 Joda 给出正确的偏移量,您必须提供日期时间瞬间。没有日期时间瞬间,就不可能计算偏移量,因为我们有不同的偏移量(夏令时)。这就是我将如何使用 Joda 以 + HH:mm 格式获取偏移量:

    int offsetInMillis = DateTimeZone.forID(zoneId).getOffset(new DateTime().getMillis());
    String offset = String.format("%02d:%02d", Math.abs(offsetInMillis / 3600000),
            Math.abs((offsetInMillis / 60000) % 60));
    offset = (offsetInMillis >= 0 ? "+" : "-") + offset; 

【讨论】:

【参考方案4】:

当您知道偏移量和时间戳时,为了获取当前时间,您可以使用

 public static String formatMonthDayMinuteByGivenUtcOffset(long timestamp, int offset) 
        return JODA_FORMATTER.print(createDateTime(timestamp, offset));
    

【讨论】:

【参考方案5】:

Java 8 introduced better Date and Time handling 解决了该语言先前在该领域的一些限制。我的一些项目已经开始使用它而不是 Joda。

使用 java.time 包:

    ZonedDateTime dateTime = LocalDate.of(2013 , 1 , 20).atStartOfDay( ZoneId.of("Australia/Melbourne"));
    ZoneOffset zo = dateTime.getOffset();
    int offset = zo.getTotalSeconds();

    long hours = TimeUnit.SECONDS.toHours(offset);
    long minutes = TimeUnit.SECONDS.toMinutes(offset % 3600);

小时变量设置为 11,分钟设置为 0。

它还计算部分小时时区的分钟偏移量,例如加拿大东部的纽芬兰和拉布拉多:

ZonedDateTime dateTime = LocalDate.of(2013, 1, 20).atStartOfDay( ZoneId.of("Canada/Newfoundland"));

在这种情况下,偏移量是 -03:30(比 UTC 晚三个半小时),hours 是 -3,minutes 是 -30。

对于字符串表示,而不是小时和分钟的整数,请使用 ZoneOffset 的 toString() 方法。所以对于上面的例子,使用:

String offsetString = zo.toString();

【讨论】:

很好的答案,但可能会更好。您假设一天总是从 00:00:00 开始,但在某些时区并非如此。最好让 java.time 确定一天中的第一时刻:LocalDate.of( 2013 , 1 , 20 ).atStartOfDay( myZoneId )。此外,该问题要求提供整个偏移量的字符串而不是数字。 ZoneOffset::toString 方法为您提供该字符串。所以整个解决方案可以是一行:LocalDate.of( 2013 , 1 , 20 ).atStartOfDay( ZoneId.of("Australia/Melbourne") ).getOffset().toString() @BasilBourque 公平点。我使用了 00:00:00 ,因为它出现在 OP 的文本中,尽管没有出现在他们的代码中。当我阅读问题时,他们想要偏移量并且知道他们可以在必要时从字符串中提取它,但想要一个更好的方法。我将根据您的建议编辑回复以改进它。

以上是关于使用 Joda-Time 获取给定日期和时区的 UTC 偏移量的主要内容,如果未能解决你的问题,请参考以下文章

使用时区将字符串转换为日期

iPhone - 获取给定地点/时区的当前日期和时间并将其与同一地点的另一个日期/时间进行比较的正确方法

获取特定时区的时间[重复]

Joda-Time,夏令时计算,时区独立测试

给定 UTC 时间,获取特定时区是午夜

Joda-Time、夏令时更改和日期时间解析