LocalDate解析会引发异常‽

时间:2019-12-28 09:35:37

标签: java string date time int

我正在执行以下编程练习: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发生异常,我不理解。

此外,我已阅读:

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

4 个答案:

答案 0 :(得分:4)

您已接近成功。您在这里有两个选择:

  • 使用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,您将看到month参数,1是1月,12是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")时,您的字符串与默认格式不匹配,因此是例外。

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

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

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));
}

答案 3 :(得分:0)

流和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

日期流

在另一个解决方案中,对于开始和停止之间的每个日期,我们得到Stream个对象中的LocalDate个。请注意,日期范围是半开放式,其中开始于 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日,星期五