【问题标题】:Error parsing single digit hour time: 3:00 AM解析个位数小时时间时出错:凌晨 3:00
【发布时间】:2019-11-11 06:18:38
【问题描述】:

所以我一直在尝试将字符串 Hour 从 12 小时格式转换为 24 小时格式,代码如下:

public String convertAM(String checkinCheckoutTime) {
String withoutAM = checkinCheckoutTime.replaceAll("AM", "").trim();
if (withoutAM.length() == 1 || withoutAM.length() == 2) {
  return LocalTime.parse(
      withoutAM + ":00 AM", DateTimeFormatter.ofPattern("hh:mm a", Locale.US))
      .format(DateTimeFormatter.ofPattern("HH:mm"));
} else {
  return LocalTime.parse(
      checkinCheckoutTime, DateTimeFormatter.ofPattern("hh:mm a", Locale.US))
      .format(DateTimeFormatter.ofPattern("HH:mm"));
}

}

我在这次测试中获得了绿色:

    @Test
  public void convertCheckinCheckoutTime12AMWithSpace() {
    String checkOutTime = "12 AM";
    String expectedCheckOutTime = "00:00";

    String result = this.service.convertAM(checkOutTime);

    Assert.assertEquals(expectedCheckOutTime, result);
  }

但是在这个测试中我得到了错误:

    @Test
  public void convertCheckinCheckoutTimeAMNoRangeWithSpace() {
    String checkOutTime = "3 AM";
    String expectedCheckOutTime = "3:00";

    String result = this.service.convertAM(checkOutTime);

    Assert.assertEquals(expectedCheckOutTime, result);
  }

错误是:

java.time.format.DateTimeParseException: Text '3:00 AM' could not be parsed at index 0

我可以知道凌晨 3 点出了什么问题吗?提前谢谢你

【问题讨论】:

  • 在格式化程序中将hh 替换为h
  • 谢谢,它适用于个位数小时

标签: java time datetime-parsing java-time


【解决方案1】:

原来我需要更改单位或两位数小时的格式,如果 12 格式是 hh:mm,如果 3 格式是 h:mm

【讨论】:

  • 不,没有必要,我认为它过于复杂。请参阅 Zabuza 和我自己的答案。
【解决方案2】:

说明

您正在使用模式hh 来表示小时数。这需要两位数的小时数,例如1203。但是您的输入是一个数字,3 用于小时字段。


解决方案

要么将您的输入调整为两位数,因此 03 而不是 3。或者使用单个数字即可的模式,即 h

来自official documentation

h,时钟-小时-of-am-pm (1-12),数字,12

数字:如果字母数为 1,则使用最小位数输出值且不进行填充。否则,count 将用作输出字段的 width,并根据需要使用 zero-padded 值。以下模式字母对字母数量有限制。只能指定一个字母“c”和“F”。最多可以指定两个字母“d”、“H”、“h”、“K”、“k”、“m”和“s”。最多可以指定三个字母“D”。


注意事项

您可以通过移出唯一不同的部分(即输入字符串)来简化代码并减少重复。而且由于您返回if,因此else 是不必要的。您还可以通过拆分一些嵌套语句并将它们放入变量中来进一步简化并使代码更具可读性。您还应该添加一个快速评论来解释发生了什么:

DateTimeFormatter inputFormatter =  DateTimeFormatter.ofPattern("hh:mm a", Locale.US);
DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("HH:mm", Locale.US);

// Patch time without minutes, 3 AM to 3:00 AM
String withoutAM = checkinCheckoutTime.replaceAll("AM", "").trim();
boolean hasOnlyHours = withoutAM.length() == 1 || withoutAM.length() == 2;
String timeInput = hasOnlyHours ? withoutAM + ":00 AM" : checkinCheckoutTime;

return LocalTime.parse(timeInput, inputFormatter)
      .format(outputFormatter);

请注意,您的代码不适用于3 PM 之类的输入,因为您只删除了AM。您可以简单地添加另一个调用来删除 PM 或将两者都放入一个正则表达式中,因为您使用的是正则表达式替换 replaceAll 而不是非正则表达式 replace 反正:

String withoutSuffix = checkinCheckoutTime.replaceAll("(AM|PM)", "").trim();

长度检查可以简化为<= 2(空输入无论如何都会在解析阶段失败)。

【讨论】:

    【解决方案3】:

    TL;DR 只需使用格式模式字符串h[:mm] a

    我希望你能告诉我们你原来的时间字符串是什么样子的。对于这个答案,我假设:

    • 上午或下午的小时可以是 1 位或 2 位数字。
    • 以冒号分隔的两位数分钟可能存在或不存在。
    • 以空格分隔的 AM 或 PM 始终存在。

    所有这些都可以通过一个格式模式字符串来处理,因此您无需在解析之前修改字符串,而且我发现不这样做更简单。技巧是:

    • 一个模式字母h 匹配一个或两个 位小时,例如30312
    • 方括号包含格式的可选部分。所以[:mm] 匹配可选的冒号和分钟。

    查看实际效果:

        DateTimeFormatter timeFormatter
                = DateTimeFormatter.ofPattern("h[:mm] a", Locale.US);
    
        String[] timeStrings = {
                "3 AM", "12 AM", "3:00 AM", "3:40 AM", "12 PM", "12:20 PM", "4 PM", "04 PM"
        };
        for (String ts : timeStrings) {
            LocalTime time = LocalTime.parse(ts, timeFormatter);
            System.out.format(Locale.ENGLISH, "%-8s is parsed into %s%n", ts, time);
        }
    

    输出是:

    3 AM     is parsed into 03:00
    12 AM    is parsed into 00:00
    3:00 AM  is parsed into 03:00
    3:40 AM  is parsed into 03:40
    12 PM    is parsed into 12:00
    12:20 PM is parsed into 12:20
    4 PM     is parsed into 16:00
    04 PM    is parsed into 16:00
    

    这说明我对你的整个方法感到不舒服。您应该将 12 小时格式的时间字符串转换为 24 小时格式的时间字符串的情况很少见。通常,您应该将时间保存在 LocalTime 对象中,而不是字符串中。接受输入字符串后,立即将其解析为LocalTime。只有当您需要提供字符串输出时,才将您的LocalTime 格式化为字符串。

    【讨论】:

      【解决方案4】:

      凌晨 3 点应该是03 AM。您可以像这样更新您的代码:

      ...
      if (withoutAM.length() == 1 || withoutAM.length() == 2) {
          if(withoutAM.length()==1){
              withoutAM = "0"+withoutAM;
          }
      ...
      

      convertAM函数中

      【讨论】:

      • 不用手动添加0,只需将模式调整为也支持h而不是hh的单个数字。
      • 哦是的...那是正确的...我正在更新我的答案...并且我的代码实现了相同的输出...
      【解决方案5】:

      而不是发送 3 AM 尝试发送 03 AM 它将正常工作。但是在使用 PM 时,你应该用这个替换你的方法。

      public static String convertAM(String checkinCheckoutTime) {
                  String withoutAM = checkinCheckoutTime.replaceAll("AM", "").trim();
                  String withoutPM = checkinCheckoutTime.replaceAll("PM", "").trim();
                  System.out.println(withoutAM);
                  System.out.println(withoutPM);
                  if (withoutAM.length() == 1 || withoutAM.length() == 2) {
                      System.out.println("length below 2");
                      return LocalTime.parse(withoutAM + ":00 AM", DateTimeFormatter.ofPattern("hh:mm a", Locale.US))
                              .format(DateTimeFormatter.ofPattern("HH:mm"));
                  } else {
                      System.out.println("lengh high");
                      return LocalTime.parse(withoutPM + ":00 PM", DateTimeFormatter.ofPattern("hh:mm a", Locale.US))
                              .format(DateTimeFormatter.ofPattern("HH:mm"));
                  }
              }
      

      【讨论】:

      • 这并没有解决单个数字的问题。它只会去掉 AM/PM。
      猜你喜欢
      • 1970-01-01
      • 2021-10-02
      • 2020-06-15
      • 2017-01-29
      • 2018-03-16
      • 1970-01-01
      • 2021-07-12
      • 1970-01-01
      • 2017-04-22
      相关资源
      最近更新 更多