LocalDate 解析抛出异常‽

Posted

技术标签:

【中文标题】LocalDate 解析抛出异常‽【英文标题】:LocalDate parse throws exceptions‽ 【发布时间】:2020-04-17 22:50:01 【问题描述】:

我正在做以下编程练习:Unlucky Days。声明是:

13 日星期五或黑色星期五被视为不吉利的日子。计算 一年中有多少不吉利的日子。

找出给定年份中的第 13 号星期五。

输入:整数形式的年份。

输出:一年中黑色星期五的数量,以整数表示。

例子:

unluckyDays(2015) == 3 unluckyDays(1986) == 1

首先我尝试了以下代码:

import java.time.*;
import java.time.format.*;
import java.util.Locale;

public class Kata 
  public static int unluckyDays/*????????*/(int year) 
    System.out.println("\nyear: "+year);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/dd");
    for(int i = 1; i <= 12; i++)
      LocalDate date = LocalDate.parse(String.format("%d/%d/13",year,i));
      DayOfWeek dow = date.getDayOfWeek();
      String output = dow.getDisplayName(TextStyle.FULL, Locale.US);
      System.out.println("\noutput: "+output);
    
    return 0;
  

我们得到以下异常:

java.time.format.DateTimeParseException: Text '2015/1/13' could not be parsed at index 4
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.LocalDate.parse(LocalDate.java:428)
    at java.base/java.time.LocalDate.parse(LocalDate.java:413)
    at Kata.unluckyDays(Kata.java:10)

正如我们所见,LocalDate.parse(String.format("%d/%d/13",year,i)); 所用字符串中索引 4 处的“/”是错误的。

然后我决定将格式更改为使用'-'而不是'/'

import java.time.*;
import java.time.format.*;
import java.util.Locale;

public class Kata 
  public static int unluckyDays/*????????*/(int year) 
    System.out.println("\nyear: "+year);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M-dd");
    for(int i = 1; i <= 12; i++)
      LocalDate date = LocalDate.parse(String.format("%d-%d-13",year,i));
      DayOfWeek dow = date.getDayOfWeek();
      String output = dow.getDisplayName(TextStyle.FULL, Locale.US);
      System.out.println("\noutput: "+output);
    
    return 0;
  

对于前面的代码,抛出的异常是:

java.time.format.DateTimeParseException: Text '2015-1-13' could not be parsed at index 5
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.LocalDate.parse(LocalDate.java:428)
    at java.base/java.time.LocalDate.parse(LocalDate.java:413)
    at Kata.unluckyDays(Kata.java:10)

因此,正如我们所见,一位数月份“1”存在困难。

为了继续,我将那些只有一位数字的月份用 0 填充:

import java.time.*;
import java.time.format.*;
import java.util.Locale;

public class Kata 
  public static int unluckyDays/*????????*/(int year) 
    System.out.println("\nyear: "+year);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M-dd");
    int count = 0;
    for(int i = 1; i <= 12; i++)
      LocalDate date = LocalDate.parse(String.format("%d-%s-13",year,String.valueOf(i).length() < 2 ? "0"+i : i));
      DayOfWeek dow = date.getDayOfWeek();
      String output = dow.getDisplayName(TextStyle.FULL, Locale.US);
      System.out.println("\noutput: "+output);
      if(output.equals("Friday"))
        count++;  
      
    
    return count;
  

在这种情况下它可以工作。但是,我们如何在 DateTimeFormatter.ofPattern("yyyy-M-dd"); 中指出月份只能有一位数字。

另外,我们应该怎么做才能使用不同的日期格式,例如:DateTimeFormatter.ofPattern("dd-M-yyyy");

因为我试过了:

import java.time.*;
import java.time.format.*;
import java.util.Locale;

public class Kata 
  public static int unluckyDays/*????????*/(int year) 
    System.out.println("\nyear: "+year);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-M-yyyy");
    int count = 0;
    for(int i = 1; i <= 12; i++)
      LocalDate date = LocalDate.parse(String.format("13-%s-%d",String.valueOf(i).length() < 2 ? "0"+i : i,year));
      DayOfWeek dow = date.getDayOfWeek();
      String output = dow.getDisplayName(TextStyle.FULL, Locale.US);
      System.out.println("\noutput: "+output);
      if(output.equals("Friday"))
        count++;  
      
    
    return count;
  

输出很有趣:

java.time.format.DateTimeParseException: Text '13-01-2015' could not be parsed at index 0
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.LocalDate.parse(LocalDate.java:428)
    at java.base/java.time.LocalDate.parse(LocalDate.java:413)
    at Kata.unluckyDays(Kata.java:11)

我们观察到“13”几天导致异常,索引为 0,我不明白。

另外我读过:

How to determine day of week by passing specific date? Calculating future occurrences of Friday the 13th https://www.geeksforgeeks.org/localdate-parse-method-in-java-with-examples/

我们怎么知道,为什么 LocalDate 解析会抛出异常‽

【问题讨论】:

令人印象深刻的研究工作。喜欢。 完全不使用字符串可以避免很多麻烦。 LocalDate.of(year, i, 13) 工作得很好。 【参考方案1】:

你离成功不远了。您有两种选择:

使用DateTimeFormatter 并将其传递给您的LocalDate.parseLocalDate date = LocalDate.parse("13/01/2019", new DateTimeFormatter("dd/MM/yyyy"); 查看此网站以获取更多信息: https://www.baeldung.com/java-string-to-date 或者,您可以使用方法LocalDate.of(year, month, day);:在您的情况下,这将是LocalDate date = LocalDate.of(year, i, 13);。如果您检查 javadoc,您会看到对于月份参数,1 是一月,12 是十二月。基本上,你很好。而且您不需要DateTimeFormatterString.format...

我试过你的代码,两种方法都可以。

编辑: 我想回答你的最后一个问题。每次你调用LocalDate.parse("anyStringRepresentingADate") 时,在后台会发生LocalDate 使用默认的DateTimeFormatter

public static LocalDate parse(CharSequence text) 
    return parse(text, DateTimeFormatter.ISO_LOCAL_DATE);

DateTimeFormatter.ISO_LOCAL_DATE'yyyy-MM-dd'。 当您执行LocalDate.parse("13/1/2020") 时,您的字符串与默认格式不匹配,因此出现异常。

【讨论】:

我会毫不犹豫地选择LocalDate.of(year, i, 13)。无需格式化字符串并将它们解析回来以进行练习。或者更优雅:for (m : Month.values()),然后是LocalDate.of(year, m, 13),使用重载的of 方法,该方法接受Month 枚举的实例作为第二个参数。【参考方案2】:

你必须传递 formatter 作为第二个参数

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-M-yyyy");
LocalDate date = LocalDate.parse("01-1-2019", formatter);

【讨论】:

【参考方案3】:

流和 lambda

其他答案是正确的。为了好玩,这里有几种使用 lambda 语法、流和谓词的方法。

月流

从comment by Ole V.V.,此解决方案使用Month 对象流,该对象通过Arrays 实用程序类从该枚举类中获取,1 月至 12 月。对于每个月,我们可以询问特定年份的每月 13 日是否是星期五。

我们正在即时实例化LocalDate,这是一个没有时间和时区的仅日期值。

int year = 2020 ;
long count = 
        Arrays                            // Utility class, `java.util.Arrays`.
        .stream(                          // Generate a stream from an array.
            Month.values()                // Generate an array from all the objects defined on this enum `Month`: An object for January, and February, and so on.
        )                                 // Returns a `Stream` object.
        .filter(                          // Applies a `Predicate` to test each object produced by the stream. Those that pass the test are fed into a new second stream.
            ( Month month ) ->            // For each `Month` enum object.
            LocalDate                     // Represent a date-only value, a year-month-day.
            .of( year , month , 13 )      // Instantiate a `LocalDate` from inputs for year number, `Month` enum object, and day number.
            .getDayOfWeek()               // Interrogate for the `DayOfWeek` enum object that represents the day-of-week for that particular date.
            .equals( DayOfWeek.FRIDAY )   // Ask if the day-of-week for this date is a Friday.
        )                                 // Returns a new 2nd `Stream` object.
        .count()                          // Returns the number of Friday-the-13th dates are being produced by this second stream. The year 2020 has two such dates.
;

看到这个code run live at IdeOne.com。

日期流

在另一个解决方案中,对于开始和停止之间的每个日期,我们得到一个 StreamLocalDate 对象。请注意,日期范围是半开放的,其中以 inclusive 开头,而在 exclusive 结尾。因此,一年从一年的第一天开始,一直持续到但不包括下一年的第一天。

对于流中的每个日期,我们使用 Predicate 进行测试,检查 (a) 月份中的某天是否为 13,以及 (b) DayOfWeek 是否为星期五。所有通过测试的LocalDate对象都被放入一个新生成的List中。

int input = 2020;
Year year = Year.of( input );
LocalDate start = year.atDay( 1 );
LocalDate stop = year.plusYears( 1 ).atDay( 1 );

List < LocalDate > fridayThe13ths =
        start
                .datesUntil( stop )
                .filter(
                        ( LocalDate localDate ) ->
                                ( localDate.getDayOfMonth() == 13 )
                                        &&
                                        localDate.getDayOfWeek().equals( DayOfWeek.FRIDAY )
                )
                .collect( Collectors.toList() )
;

转储到控制台。生成标准 ISO 8601 格式的文本。

System.out.println( "fridayThe13ths = " + fridayThe13ths );

fridayThe13ths = [2020-03-13, 2020-11-13]

为了验证这些结果确实是 13 日星期五,让我们生成表示其值的字符串。我们为包含星期几的自动本地化输出定义了一个格式化程序。

DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.FULL ).withLocale( Locale.US );
fridayThe13ths.forEach( ( LocalDate localDate ) -> System.out.println( localDate.format( f ) ) );

2020 年 3 月 13 日,星期五

2020 年 11 月 13 日,星期五

【讨论】:

这很有趣。我的版本是long count = Arrays.stream(Month.values()).filter(m -&gt; LocalDate.of(input, m, 13).getDayOfWeek().equals(DayOfWeek.FRIDAY)).count(); @OleV.V.谢谢。我添加了一个显示您的代码的部分。【参考方案4】:

这是一个计算黑色星期五的方法:

private static void unluckyDays(int year) 
    int blackFridays = 0;
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/dd");
    for (int i = 1; i <= 12; i++) 
        LocalDate date = LocalDate.parse(String.format("%d/%d/13", year, i), formatter);
        DayOfWeek dow = date.getDayOfWeek();
        if (dow.getValue() == 5) 
            blackFridays++;
        
    
    System.out.println(String.format("Year %s has %s black fridays", year, blackFridays));

【讨论】:

感谢您的贡献。这是正确的。我认为问题在于为什么问题中的代码总是抛出异常,而不是让我们为提问者解决练习。此外,我们仅从代码中学到的东西不多;对它如何工作的一些解释会很好。 PS请避免使用5的幻数;使用dow.equals(DayOfWeek.FRIDAY)dow == DayOfWeek.FRIDAY

以上是关于LocalDate 解析抛出异常‽的主要内容,如果未能解决你的问题,请参考以下文章

SAX XML 解析器抛出空指针异常

Emscripten 为未解析的 mcount 符号抛出异常

Java中编写代码出现异常,如何抛出异常,如何捕获异常

在全局线程中更改 NumberFormat 分隔符后,十进制解析抛出异常 [关闭]

如何在扩展中抛出异常?

Hive UDF 在选择中抛出未找到类异常