【问题标题】:LocalDateTime unable to parse "June"LocalDateTime 无法解析“六月”
【发布时间】:2021-05-20 09:00:40
【问题描述】:

我帮助设置了一个函数来获取两个字符串并将它们合并到一个日期对象中。 Java - Take two strings and combine into a single date object

在尝试解析 6 月 1 日之前一直运行良好,然后出现以下错误

Exception in thread "AWT-EventQueue-0" java.time.format.DateTimeParseException: Text '1st June' could not be parsed, unparsed text found at index 7
    at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1952)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
    at java.time.LocalDate.parse(LocalDate.java:400)
    at Timetable.ClassManager.parseDate(ClassManager.java:201)
    at Timetable.GoogleAPI.loadClasses(GoogleAPI.java:133)
    at Timetable.ClassManager.loadClasses(ClassManager.java:58)

函数的代码是

public LocalDateTime parseDate(String strDate, String strTime) {
    strTime = strTime + ":00";
    System.out.println("Date: " + strDate);
    System.out.println("Time: " + strTime);
    
    
    LocalDate date = LocalDate.now();
    DateTimeFormatter dtfForDate = new DateTimeFormatterBuilder()
                                    .parseCaseInsensitive()
                                    .parseDefaulting(ChronoField.YEAR, date.getYear())
                                    .appendPattern("d['th']['st']['rd']['nd'] MMM")
                                    .toFormatter(Locale.ENGLISH);

    DateTimeFormatter dtfForTime = DateTimeFormatter.ofPattern("H:m:s", Locale.ENGLISH);
    
    LocalDateTime ldt = LocalDate.parse(strDate, dtfForDate)
                                    .atTime(LocalTime.parse(strTime, dtfForTime));
    System.out.println("Local Date Time: " + ldt);
    
    return ldt;
}

这两张照片给了我

Date: 1st June

Time: 9:15:00

我需要能够同时处理完整的月份名称和月份缩写,即 March 设置为 Mar,April 设置为 Apr。

【问题讨论】:

  • 好吧,6 月 1 日不是时间的有效格式。这将是问题
  • 您可能只需要MMMM 来获取完整的月份名称。当您解析 5 月的日期时,它没有区别,因为完整的月份名称和月份缩写都是英文的 May,所以直到您发现 JunJune 之间的区别时才发现您的错误。
  • 如果需要接受MarAprMayJune,镀金方案是下半段序数已经用过的appendText(TemporalField, Map<Long, String>)方法答案。有了这个,您可以准确地控制哪些月份是完整的,哪些是缩写的。

标签: java java-time


【解决方案1】:

使用以下模式,这将同时满足三个字母缩写的月份名称以及完整的月份名称:

.appendPattern("d['th']['st']['rd']['nd'] [MMMM][MMM]")

演示:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        System.out.println(parseDate("1st June", "9:15:00"));
        System.out.println(parseDate("1st Jun", "9:15:00"));
    }

    public static LocalDateTime parseDate(String strDate, String strTime) {
        LocalDate date = LocalDate.now();
        DateTimeFormatter dtfForDate = new DateTimeFormatterBuilder()
                                        .parseCaseInsensitive()
                                        .parseDefaulting(ChronoField.YEAR, date.getYear())
                                        .appendPattern("d['th']['st']['rd']['nd'] [MMMM][MMM]")
                                        .toFormatter(Locale.ENGLISH);

        DateTimeFormatter dtfForTime = DateTimeFormatter.ofPattern("H:m:s", Locale.ENGLISH);

        LocalDateTime ldt = LocalDate.parse(strDate, dtfForDate).atTime(LocalTime.parse(strTime, dtfForTime));

        return ldt;
    }
}

输出:

2021-06-01T09:15
2021-06-01T09:15

更新

此更新解决了 Meno Hochschild 提出的以下问题:

就我个人而言,我不喜欢这里滥用可选部分。 反例:“1th Jun”或“2st Jun”也会成功 已解析但不应该。

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        System.out.println(parseDate("1st June", "9:15:00"));
        System.out.println(parseDate("1st Jun", "9:15:00"));
    }

    public static LocalDateTime parseDate(String strDate, String strTime) {
        LocalDate date = LocalDate.now();
        DateTimeFormatter dtfForDate = new DateTimeFormatterBuilder()
                                        .parseCaseInsensitive()
                                        .parseDefaulting(ChronoField.YEAR, date.getYear())
                                        .appendText(ChronoField.DAY_OF_MONTH, ordinalMap())
                                        .appendPattern(" [MMMM][MMM]")
                                        .toFormatter(Locale.ENGLISH);

        DateTimeFormatter dtfForTime = DateTimeFormatter.ofPattern("H:m:s", Locale.ENGLISH);

        LocalDateTime ldt = LocalDate.parse(strDate, dtfForDate).atTime(LocalTime.parse(strTime, dtfForTime));

        return ldt;
    }

    static Map<Long, String> ordinalMap() {
        String[] suffix = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" };
        Map<Long, String> map = new HashMap<>();
        
        for (int i = 1; i <= 31; i++) 
            map.put((long)i, String.valueOf(i) + suffix[(i > 3 && i < 21) ? 0 : (i % 10)]);
        
        return map;
    }
}

输出:

2021-06-01T09:15
2021-06-01T09:15

礼貌:构建Map 的逻辑基于this excellent answer

【讨论】:

  • 就个人而言,我不喜欢这里滥用可选部分。反例:“1th Jun”或“2st Jun”也可以成功解析,但不应该。
  • 这是一个公平的观点,@MenoHochschild。我真的认为它更像是对问题的评论,而不是对答案的评论从问题中接管相同的做法。任何想要验证你的负面例子的人都可以使用my answer here(这个问题是关于格式化的,但我提供的格式化程序也可以用于解析)。
  • @MenoHochschild - 我的解决方案专注于主要问题:如何满足三个字母的缩写月份名称以及完整月份名称。尽管如此,还是一如既往地感谢您提供的宝贵反馈。我已添加更新以解决您提出的问题。
  • @OleV.V. - 我知道使用Map 的想法,如this answer 中发布的那样。您构建Map 的解决方案也很好。但是,我更喜欢我在答案中提到的那个。
  • 有很多好方法可以做到这一点。我同意你选的那个也很好。没有理由犹豫使用它。
猜你喜欢
  • 1970-01-01
  • 2016-04-17
  • 1970-01-01
  • 2017-09-29
  • 1970-01-01
  • 2021-08-25
  • 2016-11-14
  • 2020-03-22
  • 2020-07-28
相关资源
最近更新 更多