【问题标题】:Convert UTC TIme HH:mm:ss to Device time HH:mm:ss and adding Day saving time if available将 UTC 时间 HH:mm:ss 转换为设备时间 HH:mm:ss 并添加夏令时(如果有)
【发布时间】:2019-09-24 07:51:44
【问题描述】:

我有格式为 (HH: mm: ss) 的 UTC 字符串,我需要以相同格式 (HH: mm: ss) 将字符串值转换为设备时间,如果可用的话,还添加节时间我试过了通过此代码将 UTC 字符串转换为设备时间,但由于(日节省时间),我得到了 1 小时的延迟。

String utcTimeString = "12:15:00"
SimpleDateFormat formatter = new SimpleDateFormat("HH: mm: ss");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Date value = formatter.parse(utcTimeString);

SimpleDateFormat dateFormatter = new SimpleDateFormat("HH: mm: ss"); 
dateFormatter.setTimeZone(TimeZone.getDefault());
utcDateString = dateFormatter.format(value);

我的时区是 GMT-4

预期输出:08:15:00; 给定输出:07:15:00;

【问题讨论】:

  • 能否请您添加String 以及您当前和所需的输出?
  • 您使用的是哪个 Java 版本?低于 Java 8 或 8+ 的东西?时间是否始终为当天的时间?
  • 我正在使用 Java8。不,它与当天不同。

标签: java android date datetime


【解决方案1】:

Answer by deHaar 通常是正确的,并且明智地利用了现代 java.time 类。但是,我会使用稍微不同的方法。

tl;博士

OffsetDateTime.of(                           // Represent a moment as a date, a time-of-day, and an offset-from-UTC.
    LocalDate.now( ZoneOffset.UTC ) ,        // Current date as seen right now in UTC. Beware: For any given moment, the date varies around the globe by zone.
    LocalTime.parse( "12:15:00" ) ,          // Your specified time-of-day.
    ZoneOffset.UTC                           // An offset of zero hours-minutes-seconds, for UTC itself.
)                                            // Returns an `OffsetDateTime` object.
.atZoneSameInstant(                          // Adjust from UTC to a time zone. Same moment, different wall-clock-time.
    ZoneId.of( "America/Port_of_Spain" ) ;   // One of the many time zones that are behind UTC by four hours on that date.
)                                            // Returns a `ZonedDateTime` object.
.toLocalTime()                               // Extract the time-of-day only, leaving behind the date and the zone.

时区

我的时区是 GMT-4

不。那不是时区。

GMT-4 仅代表offset-from-UTC。 UTC 基线之前或之后的小时-分钟-秒数。

时区更多。时区有一个名字,代表了过去、现在和未来的历史变化,特定地区的人们使用的偏移量。因此,时区总是比单纯的偏移更可取。

Continent/Region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 2-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

ZoneId z = ZoneId.of( "Africa/Casablanca" ) ;  

如果您的时区当前比 UTC 晚 4 小时,则您必须位于 America/ArubaAmerica/Puerto_RicoAmerica/ManausAmerica/Martinique 等时区。

ZoneId z = ZoneId.of( "America/Martinique" ) ;

UTC

我有格式为 (HH: mm: ss) 的 UTC 字符串

不。

"12:15:00" 等值不能说是 UTC 值。没有日期,该值就没有真正的意义。时刻由三部分组成:

  • 一个日期,
  • 一天中的某个时间,并且
  • 偏移量/区域。

说“UTC 中午”只给了我们 3 个部分中的 2 个。日期不见了。

今天……什么概念

也许您想将该时间应用到当前日期,如 UTC 所示。

LocalDate todayUTC = LocalDate.now( ZoneOffset.UTC ) ;

请记住,在任何特定时刻,全球各地的日期都会因地区而异。此时此刻,日本东京的日期是“明天”,而美国俄亥俄州托莱多的日期仍然是“昨天”。

OffsetDateTime

将所有三个组合成一个OffsetDateTime 对象:日期、时间和偏移量/区域。

LocalTime localTime = LocalTime.parse( "12:15:00" ) ;
OffsetDateTime odt = OffsetDateTime.of( todayUTC , localTime, ZoneOffset.UTC ) ;

ZonedDateTime

从 UTC 调整到您的特定时区。同一时刻,时间线上的同一同时点,不同的挂钟时间。应用ZoneId 以获取ZonedDateTime 对象。时区现在知道Daylight Saving Time (DST) 是否以及何时适用于该特定时区,并进行相应调整。

ZoneId z = ZoneId.of( "America/Martinique" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

错误的方法

您不应该从LocalTime 中添加/减去一些小时数。在某些区域的某些日期,特定时间可能不存在。例如,对于夏令时,在美国的“Spring-ahead”这一天,不存在 02:15:00 的时间,因为时钟从 02:00:00 开始向前跳到 03:00:00。

使用ZonedDateTime 类的正确方法将自动进行相应调整。

【讨论】:

    【解决方案2】:

    您可以使用现代日期时间 API java.time,并将时间 String 解析为某个时刻,即 Instant
    然后使用ZonedDateTime对象来应用某个时区,这可能有不同的方式来完成,我在这里给你展示其中一种:

    public static void main(String[] args) {
        // the source is just a time, but to correctly convert it, you need a date
        String utcTime = "12:15:00";
        // take today's date
        LocalDate today = LocalDate.now();
        // create a parseable date time String
        String parseableDateTime = today.format(DateTimeFormatter.ISO_DATE) + "T" + utcTime + "Z";
        // then create an Instant parsing the date time String
        Instant instant = Instant.parse(parseableDateTime);
    
        // get the ZoneId of UTC in order to have the time in UTC
        ZoneId utc = ZoneId.of("UTC");
        // do the same with your ZoneOffset of -4 Hours
        ZoneId gmtMinusFour = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4));
    
        // create a UTC ZonedDateTime of the instant and the UTC ZoneID 
        ZonedDateTime utcZdt = ZonedDateTime.ofInstant(instant, utc);
        // then use that ZonedDateTime to convert it to a time with your ZoneId
        ZonedDateTime gmtMinusFourZdt = utcZdt.withZoneSameInstant(gmtMinusFour);
    
        // finally print both ZonedDateTimes in order to compare them
        System.out.println("UTC time is:\t\t" 
                + utcZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
        System.out.println("GMT-4 time is:\t\t"
                + gmtMinusFourZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
    
        // then just get the time part of the converted ZonedDateTime
        LocalTime localTime = gmtMinusFourZdt.toLocalTime();
        // and print it
        System.out.println("Converted time is:\t" 
                + localTime.format(DateTimeFormatter.ISO_TIME));
    }
    

    输出:

    UTC time is:        2019-09-24T12:15:00Z[UTC]
    GMT-4 time is:      2019-09-24T08:15:00-04:00[GMT-04:00]
    Converted time is:  08:15:00
    

    可能有更好的解决方案,但无论如何我希望这会有所帮助......

    【讨论】:

    • 在 SDK 版本高于 26 的情况下工作起来就像一个魅力。但我的应用程序的最低 SDK 版本为 17,不支持 ZonedDateTime。
    • @koushikchaturvedula 有一个可以在较旧的 Java 版本中使用的反向移植,我认为您的应用可以使用它来支持 Android 中的较旧 API,请查看 ThreeTen backport。它提供了与 java.time 的兼容性,但缺点是另一个库
    • 我建议使用with 方法而不是操作字符串。此外,OffsetDateTime 更适合处理仅具有偏移量(小时-分钟-秒数)的值。 ZonedDateTime 类型用于时区(特定地区的人们使用的偏移量的过去、现在和未来变化的历史)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 2016-06-14
    • 2013-12-24
    • 1970-01-01
    • 1970-01-01
    • 2019-05-26
    相关资源
    最近更新 更多