如何将这种格式的字符串转换为Java8次并转换为long毫秒

How to convert string with this format to Java 8 time and convert to long milliseconds

我有一个 MapperUtility class 需要从发送字符串时间的 Web 服务映射字符串 "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)"

现在,我使用以下代码将其转换为 LocalDateTime:

String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ssZ");
dtf.withZone(ZoneId.of("UTC"));
LocalDateTime convertedDate = LocalDateTime.parse(time, dtf);

但我在 GMT+0000 (UTC) 开始时遇到异常。 当我删除格林威治标准时间以外的字符时,它会起作用。 将它们转换为日期时间后,我需要将它们转换为长毫秒。 请指教。谢谢

让解析器逐字接受字符串的低效方法是:

String[] timezones = {"UTC", "BST", "CET", "PST", ...};

StringBuilder sb = new StringBuilder(timezones.length * 8 + 38);
sb.append("E MMM dd yyyy HH:mm:ss' GMT'Z' ('");
for(String timezone : timezones)
    sb.append("['").append(timezone).append("']");
sb.append("')'");

DateTimeFormatter dtf = DateTimeFormatter.ofPattern(sb.toString());
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
ZonedDateTime convertedDate = ZonedDateTime.parse(time, dtf);
System.out.println(convertedDate);

我也改为 ZonedDateTime 因为否则它会丢弃时区并且总是 returns 12:12:13 无论 GMT+ 之后是什么。

但它很快变得笨拙,因为可能的列表无穷无尽time zone abbreviations

更好的方法是预处理字符串:

String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";

String preprocessed = time.replaceAll("(.*) GMT([+-][0-9]{4}).*", "");
System.out.println(preprocessed);
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ssZ");
ZonedDateTime convertedDate = ZonedDateTime.parse(preprocessed, dtf);
System.out.println(convertedDate);

然后在广泛的 java.time API 中找到到毫秒的转换有点棘手,但最终它变得如此简单:

convertedDate.toInstant().toEpochMilli()

您可以使用 DateTimeFormatterBuilder:

构建这样的模式
static final DateTimeFormatter DF = new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ss"))
    .appendLiteral(" GMT")
    .appendOffset("+HHmm", "+0000")
    .optionalStart()
    .appendLiteral(" (")
    .appendZoneId()
    .appendLiteral(')')
    .optionalEnd()
    .toFormatter()
    .withLocale(Locale.US);

然后,只需:

String date = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
long ms = OffsetDateTime.parse(date, DF).toInstant().toEpochMilli();  // 1385122333000
    String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ss 'GMT'xx (zzz)", Locale.ENGLISH);
    ZonedDateTime zdt = ZonedDateTime.parse(time, dtf);
    OffsetDateTime odt = OffsetDateTime.parse(time, dtf);
    boolean offsetAgrees = zdt.getOffset().equals(odt.getOffset());
    if (offsetAgrees) {
        long millisecondsSinceEpoch = odt.toInstant().toEpochMilli();
        System.out.println("Milliseoncds: " + millisecondsSinceEpoch);
    } else {
        System.out.println("Offset " + odt.getOffset() + " does not agree with time zone " + zdt.getZone());
    }

输出:

Milliseoncds: 1385122333000

我将 GMT 解析为文字,将 +0000 解析为偏移量,将 UTC 解析为时区缩写。对于偏移量,我们可以使用 xxZZZ。由于 FriNov 是英文的,我们需要指定英语语言环境。

与您的代码相比,我添加了一点复杂性,因为我们要验证偏移量和时区是否一致。 OffsetDateTime.parse 直接使用偏移量 (+0000),而 ZonedDateTime.parse 从时区 (UTC) 导出偏移量。我的支票非常简单,可以扩展以接受从夏令时 (DST) 和多个时区共享相同缩写的过渡中的两个可能的偏移量。

PS 不要使用 LocalDateTime。这种类型既不能保存偏移量也不能保存时区,因此您不能再将日期和时间附加到时间线上的特定点,您需要这样做才能获得自纪元以来的毫秒数。