【问题标题】:How to convert string with this format to Java 8 time and convert to long milliseconds如何将此格式的字符串转换为Java 8时间并转换为长毫秒
【发布时间】:2019-12-22 14:33:15
【问题描述】:

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

现在,我正在使用以下代码将其转换为 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) 开始时出现异常。 当我删除 GMT 以外的字符时,它可以工作。 将它们转换为日期时间后,我需要将它们转换为长毫秒。 请指教。谢谢。

【问题讨论】:

  • 如果您检测到字符串的结尾,将GMT+0000 (UTC) 替换为GMT 怎么样?如果这是唯一的边缘情况之一,请快速修复。
  • 你遇到了什么异常?
  • 嗨@cameron1024 感谢您的回复。我收到此异常:线程“main”中的异常 java.time.format.DateTimeParseException: Text 'Fri Nov 22 2013 12:12:13 GMT+000 (UTC)' could not be parsed at index 25. 我应该删除GMT 以外的字符串?在将它们转换为毫秒之前?但我不确定这是否是正确的方法。 :(

标签: java java-8 datetime-format java-time localdate


【解决方案1】:

您可以使用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

【讨论】:

  • 谢谢@Alex。我已经关注了这个构建器,因为它是最干净的方式。非常感谢,不胜感激。
【解决方案2】:

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

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,否则它会丢弃时区并始终返回 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}).*", "$1$2");
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()

【讨论】:

  • 区域偏移在开始时需要[+-],因此数字不会连接到秒数。
【解决方案3】:
    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());
    }

输出:

千亿:1385122333000

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

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

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

【讨论】:

  • 很抱歉我的回复来晚了。我仍然认为你的问题应该得到一个比其他两个简单一点的答案。
猜你喜欢
  • 2021-09-24
  • 2016-08-01
  • 2017-02-10
  • 2013-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-10
  • 2011-05-07
相关资源
最近更新 更多