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

19

我有一些日期,格式为20Jan2013、08Aug2012等,它们各自属于特定的时区。例如,20Jan2013可能具有澳大利亚/墨尔本的时区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。我已经寻找了一些方法,但找不到任何东西。有没有办法可以做到这一点?我唯一看到的选项是将输出转换为字符串并使用substring方法获取UTC偏移量。
上述代码确实考虑了夏令时。例如,如果我有: DateTime 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偏移量。例如,在美国夏令时切换日,当偏移在凌晨2点跳跃一个小时时,1点的偏移量与4点不同。一个日期,两个偏移量。 - Basil Bourque
5个回答

16

获得时区名称和偏移小时数的简单方法

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
  • 此代码返回的格式类似于"Mountain Standard Time (-7 Hours)",但您可以轻松地根据需要进行格式化。

希望这有所帮助。

JP


2
注意:偏移量可能包括半小时,比如在印度。因此,“长时间”会错过它。 - Amir Uval
1
不要使用 TimeUnit(它会丢失小数值,正如 @auval 所指出的那样),而是使用 tz.getName(instant) - dimo414

4
为了让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; 

3
如果您只需要时区偏移量,请使用DateTimeZone.forID()获取时区,然后使用tz.getOffset(instant)以毫秒为单位获取到UTC的偏移量。可能看起来很奇怪,需要一个时刻来计算到UTC的偏移量,但这是必要的,以考虑夏令时以及时区的变化。是的,有些国家不时更改其时区设置:为什么时区数据会发生变化? 时区设置是在本地采用的,没有世界时区机构。编辑,这会给您正确的结果:
    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


抱歉,之前忘了提到上述代码确实考虑了夏令时(Daylight Saving Time)。例如,如果我有以下代码: DateTime thisDate = dtf1.parseDateTime("30Feb2013");输出将是: 2013-07-30T00:00:00.000+11:00(末尾的+11:00而不是+10:00) - PrincessBelle
很抱歉,请问下面的代码是怎么工作的呢?assertEquals( 10 * CommonConstants.MILLISECONDS_PER_HOUR, zone.getOffset( thisDate ) );它报了CommonConstants的错误。你从哪里获取这个常量的? - PrincessBelle
这是我编写的一个类,其中包含我经常使用的常量。主要目的是使代码易于阅读。你不认为修复它很容易吗? - Aaron Digulla

2
Java 8引入了更好的日期和时间处理来解决一些语言在这方面的先前限制。我的几个项目已经开始使用它而不是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);

变量“hours”被设置为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();

1
回答很好,但还有改进的空间。您假设一天始终从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() - Basil Bourque
@BasilBourque 说得好。虽然不在他们的代码中,但我使用了00:00:00,因为它出现在OP的文本中。根据我的理解,他们想要偏移量,并知道如果必要,可以从字符串中提取它,但是希望有更好的方法。我会根据您的建议编辑回复以改进它。 - sjgp

0

当你知道偏移量和时间戳时,为了获取当前时间,你可以使用以下代码:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接