【问题标题】:Java - parse date with AM/PM next to seconds (no space)Java - 用 AM/PM 解析日期,紧挨秒(没有空格)
【发布时间】:2018-06-14 13:46:47
【问题描述】:

我正在尝试解析以下类型的日期:Dec 12 2001 11:59:59PM

如果 AM/PM 不在秒旁边,我将使用以下模式:MMM dd yyyy HH:mm:ss a

但是,AM/PM 被 MMM dd yyyy HH:mm:ssa 模式忽略(因此总是被解释为 AM)。

我正在尝试使用SimpleDateFormat。我试图指定一个语言环境无济于事。

这可以使用SimpleDateFormat 还是我需要使用替代方法/外部处理? SimpleDateFormat 让我特别感兴趣,因为它在 @JsonFormat 注释的模式属性中使用。

谢谢。

【问题讨论】:

  • 您不能使用 SimpleDateFormat 来解析缺少任何时区或与 UTC 偏移量指示的输入。任何结果都将注入它不属于的时区。请参阅使用 LocalDateTime 类的 correct Answer
  • 我建议你避免使用SimpleDateFormat 类。它不仅过时了,而且出了名的麻烦。今天我们在java.time, the modern Java date and time API 和它的DateTimeFormatter 中有很多更好的东西。如果您尝试使用问题中的模式进行解析,DateTimeFormatter 也会通知您您的错误。

标签: java date parsing


【解决方案1】:

我想使用来自 Java8+ 的 java.time API,而不是旧的 Date :

String date = LocalDateTime.now().format(
        DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm:ssa", Locale.ENGLISH)
);

或:

DateTimeFormatter format = DateTimeFormatter.ofPattern(
        "MMM dd yyyy hh:mm:ssa", Locale.ENGLISH
);
String date = LocalDateTime.of(2001, Month.DECEMBER, 12, 11, 59, 59).format(format);

输出

Jun 14 2018 03:01:02PM
Dec 12 2001 11:59:59AM

【讨论】:

  • 您已经获得了正确的格式模式字符串,并且您正在使用好的和现代的类,这很好。但问题是关于解析的,你只演示格式化......
  • 这是正确的@OleV.V。 JoopEggen 的回答已经提到了真正的问题,在我的回答中我想展示使用 java.time API 的美好一面
【解决方案2】:

对于上午/下午,您需要 12 小时 hh 而不是 24 小时 HH

hh:mm:ss''a

由于k/K/h/H 也影响a,现在一切都可能适用于ssa

如果ssa 仍然存在问题(似乎是一个错误),请尝试用空的文字字符串(单引号)分隔字母。


以下作品:

hh:mm:ssa

【讨论】:

  • 谢谢,解决了。它确实可以直接与 ssa 一起使用,使用 hh。
  • 这是(唯一的)正确答案。 “ssa”解析正确。 “HH”是 24-我们的时钟时间覆盖 AM/PM 指示符。
【解决方案3】:

SimpleDateFormat 很可能实现,但您可能更喜欢使用现代 Java 日期和时间 API java.time

    DateTimeFormatter formatter
            = DateTimeFormatter.ofPattern("MMM dd uuuu hh:mm:ssa", Locale.ENGLISH);
    String dateTimeString = "Dec 12 2001 11:59:59PM";
    LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, formatter);
    System.out.println(dateTime);

输出:

2001-12-12T23:59:59

正如其他人所说,您的问题不在于秒和 AM/PM 标记之间缺少空格,而在于小时使用大写 HH。大写的 HH 是一天中从 00 到 23 的小时,你想要的是小写的 hh 代表上午或下午从 01 到 12 的小时。

正如其他人所说,使用SimpleDateFormat 及其朋友Date 存在问题:

  • 这些课程早已过时。
  • 这些类设计得很糟糕,尤其是SimpleDateFormat 以麻烦而闻名。您的经历很典型,当然也不是不寻常的。
  • SimpleDateFormat 获得正确的结果要求JVM 时区设置与字符串中理解的时区一致,或者将SimpleDateFormat 的时区设置为相关时区。前者很难保证,因为时区设置可以随时从程序的另一部分或在同一 JVM 中运行的其他程序更改。

这也意味着,如果您确实需要过时的 Date 类的实例(例如对于您现在不想更改的旧版 API),您将需要为转换。然后像这样转换:

    Instant inst = dateTime.atZone(ZoneId.of("America/Metlakatla")).toInstant();
    Date oldfashionedDate = Date.from(inst);
    System.out.println(oldfashionedDate);

我不愿向您展示输出,因为 Date 在这里显示了一个非常令人惊讶的行为。

2001 年 12 月 13 日星期四 08:59:59 CET

08:59? 12 月13?转换为您提供了正确的时间点。当我打印Date 时,它的toString 方法被调用。这反过来使用我的 JVM 的时区设置来生成字符串,因此输出与发生转换的时区完全不同。因此,显然,当 Metlakatla 的 23:59 时,哥本哈根的第二天已经是 08:59(我的时区;输出中的 CET 是中欧时间)。如果我的 JVM 的时区设置也是 America/Metlakatla,那么输出会更符合预期:

2001 年 12 月 12 日星期三 23:59:59 AKST

java.time 更有帮助

您要求SimpleDateFormat 做的是解析一个包含第 11 天和下午的时间的时间。这确实是自相矛盾的,因为 PM 仅在第 12 天的某个小时开始。因此,期望请求中的例外是合理的。带有标准设置的SimpleDateFormat 不会给你这个。 SimpleDateFOrmat 给你一个错误的结果并假装一切都很好,这是非常典型的。但是,让我们用您的格式模式字符串MMM dd yyyy HH:mm:ssa 尝试一下我的现代代码。然后我们得到:

线程“主”java.time.format.DateTimeParseException 中的异常: 无法解析文本“2001 年 12 月 12 日晚上 11:59:59”:发现冲突: 字段 AmPmOfDay 0 与 AmPmOfDay 1 不同,源自 11:59:59

我并没有声称我完全理解为什么会这样措辞,但它提到了 AM/PM 中的冲突,而这正是我们所拥有的。

PS

一开始我并没有想到我会提供答案,但最后我被一方面bohemian’s comment 激怒了,只有 Joop Eggen 的答案是正确的,另一方面是几个comments by Basil Bourque声称您无法使用 Joop Eggen 正在使用的 SimpleDateFormat。所以我想把事情弄清楚。

链接

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

【讨论】:

    【解决方案4】:

    Java 在内部使用构建器模式。这个是根据DateTimeFormatter.RFC_1123_DATE_TIME的源码稍微修改的:

    我不建议使用它来替代 DateTimeFormatter.ofPattern(),但它可以证明在您遇到 ofPattern 的限制的某些情况下更强大。

        Map<Long, String> moy = new HashMap<>();
        moy.put(1L, "Jan"); moy.put(2L, "Feb"); moy.put(3L, "Mar");
        moy.put(4L, "Apr"); moy.put(5L, "May"); moy.put(6L, "Jun");
        moy.put(7L, "Jul"); moy.put(8L, "Aug"); moy.put(9L, "Sep");
        moy.put(10L, "Oct"); moy.put(11L, "Nov"); moy.put(12L, "Dec");
        DateTimeFormatter format = new DateTimeFormatterBuilder()
                .appendText(MONTH_OF_YEAR, moy)
                .appendLiteral(' ')
                .appendValue(DAY_OF_MONTH, 2)
                .appendLiteral(' ')
                .appendValue(YEAR, 4, 4, EXCEEDS_PAD)
                .appendLiteral(' ')
                .appendValue(HOUR_OF_AMPM, 1, 2, NOT_NEGATIVE)
                .appendLiteral(':')
                .appendValue(MINUTE_OF_HOUR, 1, 2, NOT_NEGATIVE)
                .optionalStart()
                .appendLiteral(':')
                .appendValue(SECOND_OF_MINUTE, 1, 2, NOT_NEGATIVE)
                .optionalEnd()
                .appendText(AMPM_OF_DAY)
                .toFormatter();
    
        System.out.println(format.parse("Jun 14 2018 2:51:22AM")); // {},ISO resolved to 2018-06-14T02:51:22
        System.out.println(format.parse("Jun 14 2018 2:51:22PM")); // {},ISO resolved to 2018-06-14T14:51:22
    

    【讨论】:

    • 这不是比需要的复杂吗?使用java.time 是个好主意。
    【解决方案5】:

    注意

    很遗憾,以下代码不准确。永远不会读取 AM/PM 标记。根据当地时间,假定为上午或下午。

    原答案

    使用以下格式字符串“MMM dd yyyy HH:mm:ssaaa”。

    String str = "Jun 14 2018 13:53:19PM";
    DateFormat df = new SimpleDateFormat("MMM dd yyyy HH:mm:ssaaa");
    try {
      Date date = df.parse(str);
      System.out.print(df.format(date));
    } catch (ParseException e) {
      e.printStackTrace();
    }
    

    【讨论】:

    • 您能否提供更多关于您投反对票的信息?知道代码正在运行并回答问题?
    • 也许他们的反对票是因为您使用了非常麻烦的旧日期时间类,这些类在几年前已被 Java 内置的优秀行业领先的 java.time 类所取代8 及以后。也可以通过 ThreeTen-Backport 项目在 Java 6 和 7 中使用。所以没有理由使用那些遗留类。建议在 2018 年使用它们确实是糟糕的建议。当你的车道上有一辆本田思域时,为什么还要开Yugo
    • 我相信我准确地回答了这个问题(我引用“SimpleDateFormat 特别让我感兴趣”),而没有涉及我的个人意见。
    • 嗨,我没有投反对票,但恐怕这个解决方案不起作用 - 至少在我的机器上不起作用 - 很可能是由于我的原因相同:资本 HH 压制AM/PM 读数。应该是hh。也因为您实际上是在回答问题而不是告诉我不要使用已弃用的东西而投反对票,这将是最愚蠢的事情。 “我们现在可以做得更好”应该只是对实际答案的旁注。使用不推荐使用的东西是我的问题 - 你不知道是否出于某种原因我不能这样做。
    • 为了保留答案:如果您想保留 1 次赞成和 1 次反对的声誉点,这是公平的。也许你可以改进答案?我不知道你是否愿意。正如我所说,一些读者可能会从答案下的讨论中学到东西。删除:答案确实有提到的问题。我会留给你的。我自己犹豫是否要删除被否决的答案,但经常在答案中包含 cmets(使用the edit link),以便读者知道是否存在问题。我认为这是风格问题,而不是义务。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    相关资源
    最近更新 更多