从没有时区的字符串到有 Europe/Berlin 的时间

From String without timezone to time with Europe/Berlin

我一整天都在尝试解析一个日期,我从一个系统中得到一个没有时区的字符串到一个有时区的字符串并应用了时差

原始字符串:“01/27/2021 14:47:29”

目标字符串:“2021-01-27T13:47:29.000+01:00”

问题:目标系统无法更改格式。 我需要申请,它会根据 summer/winter 时间自动减去正确的小时数。所以只减去 -1 不是解决方案。

我试过几个 这是我最后一次尝试,几乎是正确的,但它没有正确更改时间:

DateTimeFormatter fmt = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss").withZone(DateTimeZone.forID(
                "Europe/Berlin"));
DateTime dt = fmt.parseDateTime(lastModifid);
dt.toString()

结果为“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 以这种方式解释它。如果这个假设是正确的,那么看来你误会了。德国时间 (Europe/Berlin)、夏令时 (DST) 标准 +01:00 和 +02:00 的偏移量意味着德国比 提前 1 或 2 小时UTC,因此我们需要增加而不是减少 1 或 2 小时。正如 hfontanez 已经说过的,图书馆正在为我们正确地做这件事。
  • 您要求的格式和 Joda-Time 生成的格式也是 ISO 8601。根据 ISO 8601,秒上的小数位在零时是可选的,因此为了您的目的,您不需要 .000 目标字符串的一部分。

与 hfontanez 一样,我正在使用 java.time,现代 Java 日期和时间 API。为什么?我认为 java.time 是 Joda-Time 的优秀继承者。 Joda-Time 主页本身说(粗体字是原创的):

Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

你的代码出了什么问题?

您正在将 .withZone(DateTimeZone.forID("Europe/Berlin")) 应用到用于 解析原始字符串 的格式化程序。这与告诉 Joda-Time 原始字符串已经是德国时间相同。因此,Joda-Time 决定无需转换时间,只需返回一天中的同一小时。相反,我们首先需要指定原始字符串所在的时区,然后将其转换为德国时间。

链接

使用 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"));

这意味着通过解析器应用时区是一种要求它将日期时间字符串解析为本地日期时间并将时区固定在其中的方法。

现在您已经理解了这个概念,让我们讨论您发布的要求。

I've got from a System as a String without Timezone to a String with Timezone and applied time difference

Original String: "01/27/2021 14:47:29"

Target String: "2021-01-27T13:47:29.000+01:00"

Problem: The target System can not change the format. I need to apply, that it substracts automatically the correct amount of hours, depending on summer/winter time. So it's not a solution to just subtract -1.

这里有两点需要理解:

  1. 只有当给定的本地日期时间来自时区偏移量为 UTC+02:00 的时区时,才有可能将给定的本地日期时间转换为目标日期时间,例如Africa/Johannesburgfollowing diagram describes the timezone of Europe/Berlin
  2. ZonedDateTime 被设计为根据 summer/winter 时间自动调整日期时间;所以,你不需要明确地做任何事情。

说够了;让我们看一些代码!

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 from Trail: Date Time

使用 Joda-Time API

注意:Home Page of Joda-Time

查看以下通知

Joda-Time is the de facto standard date and time library for Java prior to Java SE 8. Users are now asked to migrate to 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