【问题标题】:Java: convert string to different time zoneJava:将字符串转换为不同的时区
【发布时间】:2021-03-16 19:25:34
【问题描述】:

我已经用不同的 Java 格式化程序尝试了各种不同的转换,但我仍然对看似简单的事情没有任何运气。

我有一个 UTC 日期/时间字符串。我正在尝试将其转换为另一个时区。有人能告诉我为什么以下不起作用吗?时区正在改变,但改变的方式并不正确。

更新:(虽然我似乎没有正确地将时区设置为 UTC,因为转换也不正确)。

String dateInput = "2021-02-16 20:57:43";

SimpleDateFormat mdyUtc = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
mdyUtc.setTimeZone(TimeZone.getTimeZone("UTC");
Date utcOutput = mdyUtc.parse(dateInput);

SimpleDateFormat mdyOffset = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
mdyOffset.setTimeZone(TimeZone.getTimeZone("GMT-10:00");
Date localOutput = mdyOffset.parse(dateInput);

System.out.print("UTC date = " + utcOutput);

System.out.print("Changed date = " + localOutput);

输出:

UTC 日期 = 2021 年 2 月 16 日星期二 15:57:43 EST

更改日期 = 2021 年 2 月 17 日星期三 01:57:43 EST

【问题讨论】:

  • 你不是必须将输入时区设置为UTC,并使用本地时区格式化日期时间吗?
  • 我试过了,但还是没有运气。我可以重做我的帖子以包含,我试图让它尽可能少。
  • 我建议你不要使用SimpleDateFormatTimeZoneDate。这些类设计不良且过时,尤其是前者,尤其是出了名的麻烦。而是使用LocalDateTimeZoneId, and ZonedDateTime`,全部来自java.time, the modern Java date and time API
  • 对我来说,您的输出看起来确实正确。您期望得到哪个输出?
  • GMT-10:00 不是时区。例如,您想要太平洋/檀香山吗?始终以这种格式给出时区,即 region/city

标签: java


【解决方案1】:

java.time

java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,改用modern date-time API*

使用现代日期时间 API:

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String dateInput = "2021-02-16 20:57:43";
        // Replace ZoneId.systemDefault() with ZoneOffset.UTC if this date-time is in UTC
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d H:m:s", Locale.ENGLISH)
                .withZone(ZoneId.systemDefault());
        ZonedDateTime zdt = ZonedDateTime.parse(dateInput, dtf);
        ZonedDateTime result = zdt.withZoneSameInstant(ZoneId.of("GMT-10:00"));
        System.out.println(result);
    }
}

输出:

2021-02-16T10:57:43-10:00[GMT-10:00]

ONLINE DEMO

Trail: Date Time 了解有关现代日期时间 API 的更多信息。

我可以从ZonedDateTime 得到java.util.Date 吗?

如果你需要使用java.util.Date,你可以将ZonedDateTime转换成如下:

Date date = Date.from(result.toInstant());

注意java.util.Date 对象不是像modern date-time types 那样的真实日期时间对象;相反,它表示自称为“纪元”的标准基准时间以来的毫秒数,即January 1, 1970, 00:00:00 GMT(或 UTC)。当你打印一个java.util.Date 的对象时,它的toString 方法会返回JVM 时区中的日期时间,根据这个毫秒值计算得出。如果您需要在不同的时区打印日期时间,则需要将时区设置为SimpleDateFormat 并从中获取格式化字符串。


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个Android项目工作并且您的Android API级别仍然不符合Java-8,请检查Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

【讨论】:

  • 感谢您的详细回答。我的新输出最后显示 GMT-10:00,但仅更改了 5 小时。你知道为什么会这样吗?如何将原始字符串指定为 UTC(如果需要这样做)?谢谢
  • 您是否正在打印使用Date.from(result.toInstant()) 获得的date
  • 不,那是您的第一个代码块打印“结果”。
  • @Buster 你关注Replace ZoneId.systemDefault() with ZoneOffset.UTC if this date-time is in UTC的评论了吗?
  • @Ole 这就是时间变化不正确的原因。我在不知不觉中宣布时间是本地时间,而不是 UTC。谢谢!
【解决方案2】:

tl;博士

LocalDateTime                                    // Represent a date with time-of-day but lacking the context of a time zone or offset-from-UTC.
.parse(                                          // Interpret some text in order to build a date-time object.
    "2021-02-16 20:57:43".replace( " " , "T" )   // Convert to standard ISO 8601 string to parse by default without needing to specify a formatting pattern.
)                                                // Returns a `LocalDateTime` object.
.atOffset(                                       // Place that date with time into the context of an offset. Determines a moment, a specific point on the timeline.
    ZoneOffset.UTC                               // A constant for an offset of zero hours-minutes-seconds. 
)                                                // Returns an `OffsetDateTime` object.
.atZoneSameInstant(                              // Adjust the view of this moment as seen in the wall-clock time of some other time zone. Still the same moment, same point on the timeline.
    ZoneId.of( "Pacific/Honolulu" )              // Use a time zone, if known, rather than a mere offset. 
)                                                // Returns a `ZonedDateTime` object.
.toString()                                      // Generate text representing this moment in standard ISO 8601 format extended to append the time zone name in square brackets.

看到这个code run live at IdeOne.com

2021-02-16T10:57:43-10:00[太平洋/檀香山]

详情

Answer by Avinash 是正确的,使用 DateTimeFormatter 和分配的 ZoneId。这可行,但我更喜欢将区域分配与格式化程序分开,以便对阅读代码的人更明确。这只是关于我的偏好,而不是关于正确性;两个答案同样正确。

将您的输入解析为 LocalDateTime,因为输入表示带有时间的日期,但没有任何偏移或时区指示。

默认情况下,java.time 类使用ISO 8601 中定义的标准文本格式。如果输入符合要求,则无需指定格式模式。为了遵守,请将您输入的中间空格字符替换为T

String input = "2021-02-16 20:57:43".replace( " " , "T" ) ;
LocalDateTime ldt = LocalDateTime.parse( input ) ;

您说您肯定知道输入的意思是表示以 UTC 格式显示的带时间的日期,与 UTC 的偏移量为零时分秒。所以我们可以使用ZoneOffset 应用零偏移量来生成OffsetDateTime

另外,我建议您通过附加 Z(以及在中间使用 T)来教育数据馈送的发布者使用 ISO 8601 格式来传达零偏移的事实。

OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ; // Place date with time into context of an offset of zero.

最后,你说你想把那个时刻调整到另一个时区。应用ZoneId 以获取ZonedDateTime 对象。

实际上,您指定了"GMT-10:00" 的偏移量。但是如果知道,最好use a time zone 而不是仅仅偏移。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。

我猜你想要夏威夷时间,Pacific/Honolulu

ZoneId z = ZoneId.of( "Pacific/Honolulu" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

【讨论】:

    【解决方案3】:

    java.util.Date API 已弃用;您应该查看new Date and Time APIs 周围的LocalTime 等。

    也就是说,如果您想保留旧代码:它有点脆弱。您的初始日期输入未指定时区,因此您可能会获得系统的时区。您应该指定一个时区 --- 如果预期的输入是 UTC,请这样说。

    然后您需要以小时偏移量指定时区,而不是两者。

    当我更改您的代码以使用时

    mdyOffset.setTimeZone(TimeZone.getTimeZone("-10:00"));
    

    我明白了

    更改日期 = 2021 年 2 月 16 日星期二 14:57:43 CST

    这似乎很合适,因为我在 CST(目前是 GMT 后 6 小时),所以 20:57:43 减 6 是 14:57:43。同样,这显示在我当地的时区。您可能需要使用DateFormat 来根据需要调整输出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-22
      • 1970-01-01
      • 2012-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多