【问题标题】:Java 8 convert Date String into Date with timezoneJava 8 将日期字符串转换为带时区的日期
【发布时间】:2020-09-24 23:52:13
【问题描述】:

我收到了格式为“yyyy-MM-dd'T'HH:mm:ssXXX”的字符串

例如“2020-06-01T11:04:02+02:00”

我想把它转换成“yyyy-MM-dd'T'HH:mm:ss:SSSZ”

例如“2020-06-01T11:04:02.000+0200”

我实际上不知道时区。它应该从字符串的最后一部分中获取。

我已经尝试过,但是当我将字符串转换为日期(即 IST)时,它占用了我的本地时间和时区。

        SimpleDateFormat sd1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        //sd1.setTimeZone(TimeZone.getTimeZone("PST"));
        Date dt = sd1.parse("2020-06-01T11:04:02+02:00");
        SimpleDateFormat sd2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSSZ");
        System.out.println(sd2.format(dt));

输出:

2020-06-01T14:34:02:000+0530

只有日期是正确的,时间和时区已经改变。

我知道我做错了,如果有人能告诉我该怎么做,那将非常有帮助。 感谢您的帮助。

【问题讨论】:

  • Date Time 教程
  • 这能回答你的问题吗? SimpleDateFormat parse loses timezone
  • 你为什么使用DateSimpleDateFormat? Java 8 具有 java.time 以及许多改进的替代方案。
  • @rph 不,那个问题对我没有帮助
  • [1] 您对 sd1.parse("2020-06-01T11:04:02+02:00") 的调用中的 "+02:00" 不正确,但被忽略而不是视为无效。 parse() 的 Javadoc 声明 “该方法可能不会使用给定字符串的整个文本,并且时区值对于 Date 没有意义。通过更改它的一些其他值(例如 "+09:00")来验证这一点,看看它没有效果。 [2] 请更新您的问题以澄清您想要/期望的一些示例输入的确切输出。

标签: java date java-8


【解决方案1】:

我要给你的第一个建议是从使用 Date 对象切换到 LocalDateTime (java 8+)

使用新的 API 会以这种方式工作

String YOUR_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YOUR_DATE_TIME_PATTERN);
LocalDateTime dateTime = LocalDateTime.parse(input_date, formatter);

//Then you can set your timezone in this way - remember to replace the values with the proper timezone you want. 



ZonedDateTime zonedUTC = dateTime.atZone(ZoneId.of("UTC"));

ZonedDateTime zonedIST = zonedUTC.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));

让我知道这是否适合你

【讨论】:

  • 它给了我 2020-06-01T11:04:02 输出,我想要 +0200(参见有问题的示例),实际上日期时间可以来自各种时区,有什么方法可以识别。谢谢。
  • 你能用一些正则表达式或字符串解析来提取你需要的时区吗?
  • 切换到 java.time 是一个非常好的主意。 LocalDateTime 是使用错误的类,您正在丢失来自搅拌的偏移信息。 ZonedDateTime 可以,但OffsetDateTime 更合适。
【解决方案2】:

要详细说明评论并了解@Daniel Vilas-Boas 的答案,您应该选择 Java8,我认为您想要的是:

public static void main(String[] args) {
    String YOUR_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
    String YOUR_NEW_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss:SSSZ";
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YOUR_DATE_TIME_PATTERN);
    DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern(YOUR_NEW_DATE_TIME_PATTERN);
    ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-06-01T11:04:02+02:00", formatter);

    ZoneId from = ZoneId.from(zonedDateTime);
    System.out.println(from);

    ZonedDateTime zonedIST = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));

    System.out.println(zonedDateTime.format(newFormatter));
    System.out.println(zonedIST.format(newFormatter));
}

prints 应该打印出来:

2020-06-01T11:04:02:000+0000
2020-06-01T16:34:02:000+0530

编辑

包含ZoneId 以允许处理不同的时区。

【讨论】:

  • 感谢您的回答。还有一件事。有什么方法可以识别我约会的时区吗?就像上面的例子一样,它是 +02:00,我希望输出是 +02:00,但在下一个输入中它可能是 +05:30 或其他什么我们如何解决这个问题。希望你明白我想说的话。
  • 我已更新我的代码以包含您所要求的内容。 from 将返回 ZoneId(在本例中为 +02:00),您可以使用它来处理不同的区域
  • 感谢您的帮助,因为我不知道实际的时区,我使用 ZoneOffset 从字符串本身提取了该偏移量。
【解决方案3】:

OffsetDateTime

你说:

我收到了格式为“yyyy-MM-dd'T'HH:mm:ssXXX”的字符串

例如“2020-06-01T11:04:02+02:00”

无需定义格式模式。您的输入符合ISO 8601 标准。

java.time 类在解析/生成字符串时默认使用这些标准格式。

您的输入应被解析为OffsetDateTime

String input = "2020-06-01T11:04:02+02:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input ) ;

odt.toString(): 2020-06-01T11:04:02+02:00

从 UTC 偏移与时区的关系

你说:

我实际上不知道时区。

最后的+02:00 不是时区。该文本仅代表与 UTC 的偏移量。偏移量只是小时-分钟-秒数,正数或负数。时区更多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。时区的名称格式为Continent/Region,例如Europe/BrusselsAfrica/Cairo

您可以从单纯的偏移调整到特定的时区。申请ZoneId 以获得ZonedDateTime

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ; 
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

zdt.toString(): 2020-06-01T14:34:02+05:30[亚洲/加尔各答]

你说:

它应该取自字符串的最后一部分。

我不确定你的意思。如果您将输入解析为 OffsetDateTime,则该对象知道其偏移量,可作为 ZoneOffset 访问。

ZoneOffset offset = odt.getOffset() ;

请参阅此答复中显示的代码run live at IdeOne.com

offset.toString(): +02:00

格式化字符串

你说:

我想把它转换成“yyyy-MM-dd'T'HH:mm:ss:SSSZ”

例如“2020-06-01T11:04:02.000+0200”

不知道你在这里的意思。即使值为零,您的意思是强制显示毫秒吗?首先,您应该知道 java.time 对象对于最多 9 个十进制数字的分辨率为纳秒,比以 3 位十进制小数显示的毫秒数要精细得多。其次,在 Stack Overflow 上已经覆盖了强制显示小数秒,例如 here。发帖前请务必搜索 Stack Overflow

或者您的意思是显示没有冒号字符的偏移量作为分钟和秒之间的分隔符?

  • 我建议不要这样做。虽然 ISO 8601 标准在技术上允许删除 COLON,但我看到不止一个软件库或系统在没有该分隔符的情况下无法处理偏移量。同上使用没有分钟的小时偏移量。我建议始终使用小时、分钟和分隔符。
  • 如果您坚持,请使用带有格式模式的DateTimeFormatter。研究 Javadoc,记住格式化代码 (a) 区分大小写,并且 (b) 对重复字符 0、1 次或更多次敏感。在这里,我们使用xx 来获取没有冒号字符分隔的偏移量的小时和分钟。 (同样,我不推荐这种格式。)

same IdeOne.com page中显示的代码。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSxx" ) ;     
String output = odt.format( f ) ;

输出:2020-06-01T11:04:02.000+0200

Date::toString 注入时区

你说:

我已经尝试过,但是当我将字符串转换为日期(即 IST)时,它占用了我的本地时间和时区。

java.util.Date::toString 方法会撒谎。虽然出于善意,但不幸的是,该方法在生成文本时将 JVM 的当前默认时区应用于 Date 值。 Date 类实际上代表 UTC 中的一个时刻。这是从不使用Date的众多原因之一。现在该课程已被java.time.Instant 取代。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 和 JPA 2.2 支持 java.time

从哪里获得 java.time 类?

【讨论】:

  • 是的,实际上我对时区和区域偏移量感到困惑,ZoneOffset 完成了这项工作。非常感谢您的回答和澄清。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 2017-07-29
  • 1970-01-01
  • 2011-06-13
  • 2019-03-04
相关资源
最近更新 更多