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

Pac*_*myc -1 java utc localtime simpledateformat

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

Bas*_*que 6

太长了;博士

\n

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

\n

请改用现代java.time类。

\n
OffsetDateTime                                     // Represent a date, a time-of-day, and an offset-from-UTC.\n        .of(                                       // Static factory method.\n                LocalDate.now( ZoneOffset.UTC ) ,  // Date\n                LocalTime.parse( "02:36:10" ) ,    // Time-of-day\n                ZoneOffset.UTC                     // A constant representing an offset from UTC of zero hours-minutes-seconds.\n        )                                          // Returns an `OffsetDateTime` object.\n        .atZoneSameInstant(                        // Adjust to a specific time zone. Same moment, different wall-clock time/calendar.\n                ZoneId.of( "Europe/Stockholm" )\n        )                                          // Returns a `ZonedDateTime` object.\n        .toString()                                // Returns a `String` object containing text in standard ISO 8601 format wisely extended by appending the name of the time zone in square brackets.\n
Run Code Online (Sandbox Code Playgroud)\n
\n

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

\n
\n

正如预期的那样,我们看到您的 2 小时偏移量。

\n

避免遗留日期时间类

\n

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

\n

UTC 时间毫无意义

\n

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

\n

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

\n
LocalTime lt = LocalTime.parse( "02:36:10" ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

LocalDate&OffsetDateTime

\n

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

\n
LocalDate todayUtc = LocalDate.now( ZoneOffset.UTC ) ;\nOffsetDateTime odt = OffsetDateTime.of( todayUtc , lt , ZoneOffset.UTC ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

ZonedDateTime

\n

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

\n
ZoneId zStockholm = ZoneId.of( "Europe/Stockholm" ) ;\nZonedDateTime zdtStockholm = odt.atZoneSameInstant( zStockholm ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

生成标准 ISO 8601 格式的文本。

\n
String output = zdtStockholm.toString() ;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

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

\n
\n

在那里我们看到了您预期的两小时偏移。

\n

您的代码使用 1970

\n

您的代码失败,因为您的java.util.Date对象代表 1970 年第一天的凌晨 2 点(按 UTC 格式显示)。

\n

避免打电话java.util.Date#toString。不幸的是,该方法在生成文本时应用了 JVM\xe2\x80\x98s 当前默认时区。这个糟糕的设计决定让事情变得更加混乱。

\n

为了方便起见,我们将您的旧java.util.Date对象转换为其现代替代对象,java.time.Instant对象 \xe2\x80\x94 都表示与 UTC 零小时-分钟-秒的偏移量所看到的时刻。还好,Instant#toString说的是实话,不像Date#toString

\n
System.out.println( new SimpleDateFormat( "hh:mm:ss a" ).parse( "2:36:10 AM" ).toInstant() );\n
Run Code Online (Sandbox Code Playgroud)\n
\n

1970-01-01T10:36:10Z

\n
\n

让我们看看当时瑞典的时区规则。

\n
LocalTime lt = LocalTime.parse( "02:36:10" ) ;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

isDst = 假

\n
\n

因此,1970 年瑞典时区并没有实行夏令时 (DST)。

\n
rules.getTransitions().toString()\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n