如何解析格式为 yyyy-MM-ddTHH:mm:ss.SSS+ZZZZ 的 UTC 时间戳

How to parse UTC timestamp of format yyyy-MM-ddTHH:mm:ss.SSS+ZZZZ

我在流式批处理中收到 String 格式的 2020-12-03T05:35:59.398+0000 时间戳。

我只想要 2020-12-03 05:35:59 作为 java.sql.Timestamp 实例,以便能够将它与其他 Timestamp 实例进行比较。

使用 Timestamp.valueOf() 函数出现以下错误:

Exception in thread "main" java.time.format.DateTimeParseException : Text '2020-12-03T05:35:59.398+0000' could not be parsed at index 23

我尝试了 给出的答案,确实发生了转换,但时间更改为 2020-12-03 11:05:59

我已经尝试在给定的格式之间进行更改 here 但仍然没有解决方案。

398+0000 之间甚至有一个带有奇怪 + 的时间戳格式吗?

您可以对非标准格式使用自定义 DateFormatter。这是您的用例的工作示例。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;

import static java.time.temporal.ChronoField.*;

public class Main {

    private static final DateTimeFormatter INPUT_NON_STANDARD_FORMAT;

    static {
        INPUT_NON_STANDARD_FORMAT =
                new DateTimeFormatterBuilder()
                        .parseCaseInsensitive()
                        .append(DateTimeFormatter.ISO_LOCAL_DATE)
                        .appendLiteral('T')
                        .appendValue(HOUR_OF_DAY, 2)
                        .appendLiteral(':')
                        .appendValue(MINUTE_OF_HOUR, 2)
                        .optionalStart()
                        .appendLiteral(':')
                        .appendValue(SECOND_OF_MINUTE, 2)
                        .optionalStart()
                        .appendLiteral('.')
                        .appendValue(MILLI_OF_SECOND, 3)
                        .appendLiteral("+0000")
                        .toFormatter();
    }

    public static void main(String[] args) {
        String timestamp = "2020-12-03T05:35:59.398+0000";
        final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.parse(timestamp, INPUT_NON_STANDARD_FORMAT);
        System.out.println(localDateTime.format(dateTimeFormatter));
    }
}

输出

2020-12-03 05:35:59

java.time

我建议您使用 java.time,现代 Java 日期和时间 API,作为您的日期和时间工作。

    DateTimeFormatter isoFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxx");
    DateTimeFormatter sqlTimestampFormatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE)
            .appendLiteral(' ')
            .append(DateTimeFormatter.ISO_LOCAL_TIME)
            .toFormatter();
    
    String aTimestampString = "2020-12-03T05:35:59.398+0000";
    String anotherTimestampString = "2020-12-04 06:43:58.556385";
    
    Instant anInstant = isoFormatter.parse(aTimestampString, Instant::from);
    Instant anotherInstant = LocalDateTime.parse(anotherTimestampString, sqlTimestampFormatter)
            .atOffset(ZoneOffset.UTC)
            .toInstant();
    
    if (anInstant.isBefore(anotherInstant)) {
        System.out.println(aTimestampString + " is earlier");
    } else {
        System.out.println(anotherTimestampString + " is earlier");
    }

这个例子的输出是:

2020-12-03T05:35:59.398+0000 is earlier

上面前一个字符串中的 +0000 是与 UTC 的偏移量 — 00 小时 00 分钟的偏移量。因为它是零,我们知道时间是 UTC 时间。我不知道另一个字符串的时区或 UTC 偏移量。你需要知道,否则你会得到不正确的结果。在上面的代码中,我假设另一个字符串也是 UTC。

不要使用时间戳

I tried the answer given here, and conversion did happen but the time was changed to 2020-12-03 11:05:59

这就是 Timestamp class 的混乱程度。您获得了正确的时间戳值。当您 print Timestamp 对象时,会发生什么情况,即您正在(隐式或显式)调用它的 toString 方法。 Timestamp.toString() 混淆地使用 JVM 的默认时区来呈现字符串。因此,如果您的时间戳等于 2020-12-03T05:35:59.398 UTC,并且您的时区是 Asia/Kolkata,那么时间将转换为 Asia/Kolkata 时区和字符串 2020-12-03 11:05:59 返回并打印。

老式的java.sql.Timestampclass你没什么好用的。它最初用于在 SQL 数据库之间传输带时区和不带时区的时间戳值。从 JDBC 4.2 开始,我们更喜欢 OffsetDateTimeInstantLocalDateTime。所以忘记 Timestamp class.

Link

Oracle tutorial: Date Time 解释如何使用 java.time.

Is there even a format for timestamp with wierd + in between 398+0000?

398部分是秒的小数部分(毫秒),而+0000部分是区域偏移部分。

您可以使用格式模式 uuuu-MM-dd'T'HH:mm:ss.SSSX.

2020-12-03T05:35:59.398+0000 解析为 OffsetDateTime

演示:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        System.out.println(odt);
    }
}

输出:

2020-12-03T05:35:59.398Z

查看 DateTimeFormatter documentation page 以了解有关用于格式化的字母的更多信息。

您可以使用 OffsetDateTimeisBeforeisAfter 函数来比较它的两个实例。

演示:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        OffsetDateTime odtNow = OffsetDateTime.now();
        System.out.println(odtNow.isBefore(odt));
        System.out.println(odtNow.isAfter(odt));
    }
}

输出:

false
true

Trail: Date Time. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and .

了解有关现代日期时间 API 的更多信息

java.util 的日期时间 API 及其格式 API、SimpleDateFormat 已过时且容易出错。建议完全停止使用它们,转而使用modern date-time API. Since java.sql.Timestamp extends java.util.Date, it is recommended to stop using that as well. However, for any reason, if you still want to use conversion between the modern and the legacy date-time API, use Instant作为桥梁。

import java.sql.Timestamp;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        Instant instant = odt.toInstant();
        Timestamp timestamp = new Timestamp(instant.toEpochMilli());
        System.out.println(timestamp);
    }
}

输出:

2020-12-03 05:35:59.398