问题描述
我已经尝试了一整天来解析一个日期,我从一个系统中作为一个没有时区的字符串到一个带有时区和应用时差的字符串
原始字符串:“01/27/2021 14:47:29”
目标字符串:“2021-01-27T13:47:29.000+01:00”
问题:目标系统无法更改格式。 我需要申请,它会根据夏季/冬季时间自动减去正确的小时数。所以仅仅减去-1不是解决方案。
我试过几个 这是我最后一次尝试,几乎是正确的,但它没有正确更改时间:
github
结果是“2021-01-27T14:47:29.000+01:00”
解决方法
答案(自 Java 8 起)是使用 ZonedDateTime。我认为你不需要做任何“时区算术”来做你需要的。只需像这样将时间从一个区域转换为另一个区域:
ZoneId sourceTZ = ZoneId.of(...); //String denoting source time zone
ZoneId targetTZ = ZoneId.of(...); //String denoting target time zone
LocalDateTime now = LocalDateTime.now();
ZonedDateTime sourceTime = now.atZone(sourceTZ);
ZonedDateTime targetTime = sourceTime.withZoneSameInstant(targetTZ);
此后,您可以使用 DateTimeFormatter
随意设置时间戳字符串的格式。
java.time
我只是详细说明了 hfontanez 的好建议。我首先声明:
private static final DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm:ss");
private static final ZoneId targetZone = ZoneId.of("Europe/Berlin");
然后处理你的字符串是这样的:
String originalString = "01/27/2021 14:47:29";
OffsetDateTime dateTime = LocalDateTime.parse(originalString,formatter)
.atOffset(ZoneOffset.UTC);
String targetString = dateTime.atZoneSameInstant(targetZone)
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
System.out.println(targetString);
输出为:
2021-01-27T15:47:29+01:00
你要求 13:47:29.000,我给了你 15:47:29。为什么?
- 我假设原始字符串是 UTC,所以在代码中我明确告诉 Java 以这种方式解释它。如果这个假设是正确的,那么您似乎误解了。德国时间(欧洲/柏林),夏令时 (DST) 标准 +01:00 和 +02:00 的偏移意味着德国比 UTC 提前 1 或 2 小时,因此我们需要增加而不是减少 1 或 2 小时。正如 hfontanez 已经说过的,图书馆正在为我们做正确的事情。
- 您要求的格式以及 Joda-Time 生成的格式也是 ISO 8601。根据 ISO 8601,秒的小数在为零时是可选的,因此出于您的目的,您不应该需要
.000
部分目标字符串。
像 hfontanez 一样,我使用 java.time,现代 Java 日期和时间 API。为什么?我认为 java.time 是 Joda-Time 的优秀继承者。 Joda-Time 主页本身说(粗体为原创):
请注意,从 Java SE 8 开始,要求用户迁移到 java.time
(JSR-310) - JDK 的核心部分,它取代了这个
项目。
您的代码出了什么问题?
您将 .withZone(DateTimeZone.forID("Europe/Berlin"))
应用于用于解析原始字符串的格式化程序。这与告诉 Joda-Time 原始字符串已经在德国时间相同。因此,Joda-Time 决定不需要转换时间,只为您提供一天中的同一小时。相反,我们首先需要指定原始字符串所在的时区,然后将其转换为德国时间。
链接
- Oracle tutorial: Date Time 解释如何使用 java.time。
- Wikipedia article: ISO 8601
- Joda-Time - Home
使用 Java SE 8 日期和时间 API
以下代码
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm:ss",Locale.ENGLISH)
.withZone(ZoneId.of("Africa/Johannesburg"));
ZonedDateTime zdtSource = ZonedDateTime.parse(strSource,fmt);
做同样的事情
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm:ss",Locale.ENGLISH);
ZonedDateTime zdtSource = LocalDateTime.parse(strSource,fmt)
.atZone(ZoneId.of("Africa/Johannesburg"));
这意味着使用解析器应用时区是一种要求它将日期时间字符串解析为本地日期时间并将时区粘贴到它的方法。
既然您已经理解了这个概念,让我们来讨论您发布的要求。
我从一个系统作为一个没有时区的字符串到一个带有时区的字符串 时区和应用时差
原始字符串:“01/27/2021 14:47:29”
目标字符串:“2021-01-27T13:47:29.000+01:00”
问题:目标系统无法更改格式。我需要申请, 它会自动减去正确的小时数, 取决于夏/冬时间。所以这不是一个解决方案 减去-1。
这里有两件事需要理解:
- 只有当给定的本地日期时间来自时区偏移为
UTC+02:00
的时区时,才可以将给定的本地日期时间转换为目标日期时间,例如Africa/Johannesburg
。 following diagram 描述了Europe/Berlin
的时区
-
ZonedDateTime
旨在根据夏令时/冬令时自动调整日期时间;因此,您无需明确执行任何操作。
说得够多了;让我们看看一些代码!
import java.time.LocalDateTime;
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 strSource = "01/27/2021 14:47:29";
DateTimeFormatter fmtInput = DateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm:ss",Locale.ENGLISH);
ZonedDateTime zdtSource = LocalDateTime.parse(strSource,fmtInput)
.atZone(ZoneId.of("Africa/Johannesburg"));
System.out.println(zdtSource);
ZonedDateTime zdtTarget = zdtSource.withZoneSameInstant(ZoneId.of("Europe/Berlin"));
// Default format
System.out.println(zdtTarget);
// Custom format
DateTimeFormatter fmtOutput = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX",Locale.ENGLISH);
System.out.println(zdtTarget.format(fmtOutput));
}
}
输出:
2021-01-27T14:47:29+02:00[Africa/Johannesburg]
2021-01-27T13:47:29+01:00[Europe/Berlin]
2021-01-27T13:47:29.000+01:00
从 modern date-time API 了解有关 Trail: Date Time 的更多信息。
使用 Joda-Time API
查看以下通知Joda-Time 是 Java 的事实上的标准日期和时间库 在 Java SE 8 之前。现在要求用户迁移到 java.time (JSR-310)。
但是,为了完整起见,我使用 Joda-time API 编写了以下代码:
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
String strSource = "01/27/2021 14:47:29";
DateTimeFormatter fmtInput = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss")
.withZone(DateTimeZone.forID("Africa/Johannesburg"));
DateTime dtSource = fmtInput.parseDateTime(strSource);
System.out.println(dtSource);
DateTime dtTarget = dtSource.withZone(DateTimeZone.forID("Europe/Berlin"));
System.out.println(dtTarget);
}
}
输出:
2021-01-27T14:47:29.000+02:00
2021-01-27T13:47:29.000+01:00