在 Java 中使用夏令时将 UTC 转换为本地时间

问题描述 投票:0回答:4

我正在尝试将 UTC 时间转换为本地时间,包括夏令时。 当地时间(斯德哥尔摩),在夏天,应该比 UTC 早 2 小时,但是当我用 Java 转换它时,它只增加了一个小时。

public class TimeConverter {

    public static void main(String[] args) throws ParseException {
        getLocalTime("2:36:10 AM");
    }

    public static String getLocalTime(String utcTime) throws ParseException {

        DateFormat utc = new SimpleDateFormat("hh:mm:ss a");
        utc.setTimeZone(TimeZone.getTimeZone("UTC"));

        Date date = utc.parse(utcTime);

        DateFormat local = new SimpleDateFormat("HH:mm:ss");
        local.setTimeZone(TimeZone.getDefault());

        System.out.println(utc.getTimeZone());
        System.out.println(local.getTimeZone());

        System.out.println("UTC TIME\t" + utcTime);
        System.out.println("LOCAL TIME\t" + local.format(date));

        return local.format(date);
    }
}

输出:

sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
sun.util.calendar.ZoneInfo[id="Europe/Stockholm",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Stockholm,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
UTC TIME    2:36:10 AM
LOCAL TIME  03:36:10
java simpledateformat utc localtime
4个回答
2
投票

tl;博士

您的代码无意中使用了 1970 年第一天凌晨 2 点 1970-01-01T02:36:10Z 的时刻,如 UTC 所示。然后你调整到斯德哥尔摩时间,仍然是 1970 年,当时没有夏令时。因此偏移一小时 (+01:00) 而不是您预期的两小时 (+02:00)。

使用现代 java.time 类代替。

OffsetDateTime
        .of(
                LocalDate.now( ZoneOffset.UTC ) ,
                LocalTime.parse( "02:36:10" ) ,
                ZoneOffset.UTC
        )
        .atZoneSameInstant(
                ZoneId.of( "Europe/Stockholm" )
        )
        .toString()

2023-05-06T04:36:10+02:00[欧洲/斯德哥尔摩]

避免遗留日期时间类

您正在使用糟糕的日期时间类,这些类现在已经成为遗留问题,多年前被 JSR 310 中定义的现代 java.time 类所取代。

UTC 中的时间毫无意义

在 UTC 中单独询问一天中的某个时间是没有意义的。你需要一个日期和一个时间来确定一个时刻,代表时间轴上的一个点。

当以文本方式传达数据以表示时间时,请使用标准 ISO 8601 格式。对于一天中的某个时间,这意味着只是带有填充零的 24 小时制。

LocalTime lt = LocalTime.parse( "02:36:10" ) ;

LocalDate
&
OffsetDateTime

应用日期来确定时刻。也许您想要 UTC 中看到的当前日期。

LocalDate todayUtc = LocalDate.now( ZoneOffset.UTC ) ;
OffsetDateTime odt = OffsetDateTime.of( todayUtc , lt , ZoneOffset.UTC ) ;

ZonedDateTime

调整时区。同一时刻,不同的挂钟时间和日历。

ZoneId zStockholm = ZoneId.of( "Europe/Stockholm" ) ;
ZonedDateTime zdtStockholm = odt.atZoneSameInstant( zStockholm ) ;

您的代码使用 1970

您的代码失败,因为您的

java.util.Date
对象代表 1970 年第一天的凌晨 2 点,如 UTC 所示。

System.out.println( new SimpleDateFormat( "hh:mm:ss a" ).parse( "2:36:10 AM" ).toInstant() );

1970-01-01T10:36:10Z

让我们看看当时瑞典的时区规则。为方便起见,我们将您的遗留

java.util.Date
对象转换为其现代替代品,一个
java.time.Instant
对象——两者都代表一个时刻,与 UTC 的偏移量为零小时-分钟-秒。

Instant instant = new SimpleDateFormat( "hh:mm:ss a" ).parse( "2:36:10 AM" ).toInstant(); 
ZoneRules rules = ZoneId.of( "Europe/Stockholm" ).getRules();
boolean isDst = rules.isDaylightSavings( instant );
System.out.println( "isDst = " + isDst );

isDst = 假

因此,在 1970 年的那个时刻,瑞典时区没有有效的夏令时 (DST)。

rules.getTransitions().toString()

我们可以在 1949-10-02 和 1980-04-06 之间的转换列表中看到偏移量为 +01:00。所以没有你预期的+02:00。


0
投票

java.time
是比旧包/类更好的选择。

看这个例子,它以 today 作为日期并从 UTC 转换为 Europe/Stockholm:

public static void main(String[] args) {
    // inpute time as String
    String someTime = "2:36:10 AM";
    // create a formatter for those time Strings
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("h:m:s a", Locale.ENGLISH);
    // parse the time (time only, no date or zone involved so far)
    LocalTime localTime = LocalTime.parse(someTime, dtf);
    // involve a date (today here)
    LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), localTime);
    // create a zone to involve
    ZoneId stockholm = ZoneId.of("Europe/Stockholm");
    // compose the (previously composed) date and time, and the zone
    ZonedDateTime utcTime = ZonedDateTime.of(localDateTime, ZoneOffset.UTC);
    // then use that moment in time and express it in a different zone (UTC here)
    ZonedDateTime stockholmTime = utcTime.withZoneSameInstant(stockholm);
    // print both zoned datetimes
    System.out.println("UTC time:   " + utcTime);
    System.out.println("local time: " + stockholmTime);
}

输出:

UTC time:   2023-05-07T02:36:10Z
local time: 2023-05-07T04:36:10+02:00[Europe/Stockholm]

0
投票

这回答了 OP 的问题(为什么会这样),因为其他人已经建议使用现代日期/时间类。

SimpleDateFormat#parse(...)
的定义包括:

public Date parse​(String text,
   ParsePosition pos)

解析字符串中的文本以生成日期。

该方法尝试从 pos 给定的索引开始解析文本

...

此解析操作使用日历来生成日期。在解析之前清除所有日历的日期时间字段,并且日历的日期时间字段的默认值用于任何丢失的日期时间信息。例如,如果解析操作中没有给出年份值,则解析日期的年份值为 1970 与 GregorianCalendar。 TimeZone 值可能会被覆盖,具体取决于给定的模式和文本中的时区值。之前通过调用 setTimeZone 设置的任何 TimeZone 值可能需要恢复以供进一步操作。

1970 年斯德哥尔摩没有观察员 DST,因此是 +01:00


-1
投票

也许您还没有为您的 Java 版本更新 IANNA 时区数据库,阅读这篇文章转到 java tine 页面上的索引。(html 页面) https://drive.google.com/file/d/1gjHmdC-BW0Q2vXiQYmp1rzPU497sybNy/view?usp=drivesdk

© www.soinside.com 2019 - 2024. All rights reserved.