Friday, April 15, 2016

Java 8 java.time LENIENT date parsing

I was porting some Java code that uses Joda Time to the new Java 8 Time package. I needed a way to parse a date of the format "YYYY-MM-DD" and be lenient, specifically on leap year date mistakes.

I also need this to be a hash of the day of the year, so that March 1, 2000 (a leap year) and March 1, 2001 (not a leap year) hash to the same value.

They key piece of code is this:
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT);
 
Notice this: ResolverStyle.LENIENT
 
 public class DateUtils  
 {  
   //This is a hash of the day of the year.  
   public static int hashDayOfYear(String stringDate)  
   {  
    DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT);  
    LocalDate date = LocalDate.parse(stringDate, formatter);  
    int dayOfYear = date.getDayOfYear();  
    //Leap year stuff  
    boolean isLeap = Year.isLeap(date.getYear());  
    //February 28th is the 59th day of the year.  
    if( isLeap )  
    {  
      if(dayOfYear == 60)  
      {  
       //This allows for exact matching of dates but interfears with range matching  
       dayOfYear = 366;  
      }  
      else if (dayOfYear > 59)  
      {  
       dayOfYear--;  
      }  
    }  
    return dayOfYear;  
   }  
 }  
 public class DateUtilsTest  
 {  
   @Test  
   public void testOne()  
   {  
    //Feb 28 of a leap year  
    String date = "2000-02-28"  
    int dayOfYear = DateUtils.hashDayOfYear(date);  
    assert dayOfYear == 59;  
    //Feb 29 of leap year maps to 366  
    date = "2000-02-29"  
    dayOfYear = DateUtils.hashDayOfYear(date);  
    assert dayOfYear == 366;  
    //Feb 29, but not leap year, needs to be lenient and map to March 1  
    date = "2001-02-29"  
    dayOfYear = DateUtils.hashDayOfYear(date);  
    assert dayOfYear == 60;  
    //Feb 30 of a leap year, needs to be lenient and map to March 1  
    date = "2000-2-30";  
    dayOfYear = DateUtils.hashDayOfYear(date);  
    assert dayOfYear == 60;  
   }  
 }