【问题标题】:From String without timezone to time with Europe/Berlin从没有时区的字符串到欧洲/柏林的时间
【发布时间】:2021-05-03 13:37:20
【问题描述】:

我已经尝试了一整天来解析一个日期,我从系统作为没有时区的字符串到有时区的字符串并应用了时差

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

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

问题:目标系统无法更改格式。 我需要申请,它会根据夏季/冬季时间自动减去正确的小时数。因此,仅减去 -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”

难道没有简单的解决方案来应用这些时间差吗?

【问题讨论】:

  • (1) 原始字符串是 UTC 吗?在这种情况下,您必须为柏林添加 1 或 2 小时,而不是减去。 (2) 需要使用 Joda-Time 吗?今天大多数人(包括 Joda-Time 本身)建议您使用java.time, the modern Java date and time API

标签: java timezone jodatime


【解决方案1】:

答案(从 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 随意格式化时间戳字符串。

【讨论】:

  • 很好的答案,但您应该合并解析作为问题一部分的输入字符串,并放弃 .now 调用。
【解决方案2】:

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 决定无需转换时间,只为您提供一天中的同一小时。相反,我们首先需要指定原始字符串所在的时区,然后将其转换为德国时间。

链接

【讨论】:

  • 我无法更好地解释它。感谢您的解释。有时有些概念我理解但无法雄辩地解释这一点。你捕捉到了我想要完美解释的一切。基本上,让图书馆完成它的工作并简化你的生活!
  • 基本上,让图书馆完成它的工作并简化你的生活!我称之为雄辩!感谢您的评论。
  • 非常感谢您的解释。这就像一个魅力,我什至理解它,谢谢。
【解决方案3】:

使用 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。

这里有两点需要理解:

  1. 只有当给定的本地日期时间来自时区偏移为UTC+02:00 的时区时,才可以将给定的本地日期时间转换为目标日期时间。 Africa/Johannesburgfollowing diagram 描述了 Europe/Berlin 的时区
  2. 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

通过Trail: Date Time了解更多关于modern date-time API的信息。

使用 Joda-Time API

注意:Home Page of Joda-Time查看以下通知

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

【讨论】:

    猜你喜欢
    • 2019-06-22
    • 2014-05-19
    • 2021-09-16
    • 1970-01-01
    • 2013-09-12
    • 2021-04-21
    • 2018-10-30
    • 2020-05-24
    • 2023-03-12
    相关资源
    最近更新 更多