@ -65,8 +65,8 @@ public class DateDetection {
private static final TimeZone UTC_TIMEZONE = TimeZone . getTimeZone ( "UTC" ) ;
private static final String CONPATT = "uuuu/MM/dd" ;
private static final DateTimeFormatter CONFORM = DateTimeFormatter . ofPattern ( CONPATT ) . withLocale ( Locale . US )
. withZone ( ZoneOffset . UTC ) ;
private static final DateTimeFormatter CONFORM = DateTimeFormatter . ofPattern ( CONPATT ) . withLocale ( Locale . US )
. withZone ( ZoneOffset . UTC ) ;
private static final LinkedHashMap < Language , String [ ] > Weekdays = new LinkedHashMap < > ( ) ;
private static final LinkedHashMap < Language , String [ ] > Months = new LinkedHashMap < > ( ) ;
private static final int [ ] MaxDaysInMonth = new int [ ] { 31 , 29 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 } ;
@ -154,25 +154,24 @@ public class DateDetection {
public static Map < Pattern , Date [ ] > HolidayPattern = new HashMap < > ( ) ;
static {
Holidays . putAll ( getHolidays ( CURRENT_YEAR ) ) ;
Holidays . putAll ( getHolidays ( CURRENT_YEAR ) ) ;
for ( Map . Entry < String , Date [ ] > holiday : Holidays . entrySet ( ) ) {
HolidayPattern . put ( Pattern . compile ( BODNCG + holiday . getKey ( ) + EODNCG ) , holiday . getValue ( ) ) ;
}
}
/ * *
* @param currentYear
* the current year reference to use
* @return a new mapping from holiday names to arrays of
* three or four holiday dates starting from currentYear - 1. Each date time is 00 : 00 : 00 on UTC + 00 : 00 time zone .
* /
public static HolidayMap getHolidays ( final int currentYear ) {
final HolidayMap result = new HolidayMap ( ) ;
/* Date rules from icu4j library used here (SimpleDateRule and EasterRule) use internally the default time zone and this can not be modified (up to icu4j 60.1) */
final TimeZone dateRulesTimeZone = TimeZone . getDefault ( ) ;
/ * *
* @param currentYear
* the current year reference to use
* @return a new mapping from holiday names to arrays of
* three or four holiday dates starting from currentYear - 1. Each date time is 00 : 00 : 00 on UTC + 00 : 00 time zone .
* /
public static HolidayMap getHolidays ( final int currentYear ) {
final HolidayMap result = new HolidayMap ( ) ;
/* Date rules from icu4j library used here (SimpleDateRule and EasterRule) use internally the default time zone and this can not be modified (up to icu4j 60.1) */
final TimeZone dateRulesTimeZone = TimeZone . getDefault ( ) ;
// German
result . put ( "Neujahr" , sameDayEveryYear ( Calendar . JANUARY , 1 , currentYear ) ) ;
result . put ( "Heilige Drei Könige" , sameDayEveryYear ( Calendar . JANUARY , 6 , currentYear ) ) ;
@ -180,12 +179,10 @@ public class DateDetection {
/* Fat Thursday : Thursday (6 days) before Ash Wednesday (52 days before Easter Sunday) */
result . put ( "Weiberfastnacht" , holiDayEventRule ( new EasterHoliday ( - 52 , "Weiberfastnacht" ) . getRule ( ) , currentYear , dateRulesTimeZone ) ) ; // new Date[]{CONFORM.parse("2014/02/27"), CONFORM.parse("2015/02/12"), CONFORM.parse("2016/02/04")});
result . put ( "Weiberfasching" , result . get ( "Weiberfastnacht" ) ) ;
/* Rose Monday : Monday before Ash Wednesday (48 days before Easter Sunday) */
result . put ( "Rosenmontag" , holiDayEventRule ( new EasterHoliday ( - 48 , "Rosenmontag" ) . getRule ( ) , currentYear , dateRulesTimeZone ) ) ; // new Date[]{CONFORM.parse("2014/03/03"), CONFORM.parse("2015/03/16"), CONFORM.parse("2016/02/08")});
result . put ( "Faschingsdienstag" , holiDayEventRule ( EasterHoliday . SHROVE_TUESDAY . getRule ( ) , currentYear , dateRulesTimeZone ) ) ; // new Date[]{CONFORM.parse("2014/03/04"), CONFORM.parse("2015/03/17"), CONFORM.parse("2016/02/09")});
result . put ( "Fastnacht" , result . get ( "Faschingsdienstag" ) ) ; // new Date[]{CONFORM.parse("2014/03/04"), CONFORM.parse("2015/03/17"), CONFORM.parse("2016/02/09")});
result . put ( "Aschermittwoch" , holiDayEventRule ( EasterHoliday . ASH_WEDNESDAY . getRule ( ) , currentYear , dateRulesTimeZone ) ) ; // new Date[]{CONFORM.parse("2014/03/05"), CONFORM.parse("2015/03/18"), CONFORM.parse("2016/02/10")});
@ -200,7 +197,6 @@ public class DateDetection {
/* Include both Easter Sunday and Monday */
result . put ( "Ostern" , getOsternEventRule ( currentYear , dateRulesTimeZone ) ) ;
result . put ( "Walpurgisnacht" , sameDayEveryYear ( Calendar . APRIL , 30 , currentYear ) ) ;
result . put ( "Tag der Arbeit" , sameDayEveryYear ( Calendar . MAY , 1 , currentYear ) ) ;
@ -208,13 +204,12 @@ public class DateDetection {
final Date [ ] mothersDays = new Date [ 3 ] ;
int year = currentYear - 1 ;
for ( int i = 0 ; i < 3 ; i + + ) {
final LocalDate firstMay = LocalDate . of ( year , java . time . Month . MAY , 1 ) ;
final LocalDate mothersDay = firstMay . with ( TemporalAdjusters . firstInMonth ( DayOfWeek . SUNDAY ) ) . with ( TemporalAdjusters . next ( DayOfWeek . SUNDAY ) ) ;
mothersDays [ i ] = toMidnightUTCDate ( mothersDay ) ;
year + + ;
final LocalDate firstMay = LocalDate . of ( year , java . time . Month . MAY , 1 ) ;
final LocalDate mothersDay = firstMay . with ( TemporalAdjusters . firstInMonth ( DayOfWeek . SUNDAY ) ) . with ( TemporalAdjusters . next ( DayOfWeek . SUNDAY ) ) ;
mothersDays [ i ] = toMidnightUTCDate ( mothersDay ) ;
year + + ;
}
result . put ( "Muttertag" , mothersDays ) ;
result . put ( "Christi Himmelfahrt" , holiDayEventRule ( EasterHoliday . ASCENSION . getRule ( ) , currentYear , dateRulesTimeZone ) ) ; // new Date[]{CONFORM.parse("2014/05/29"), CONFORM.parse("2015/05/14"), CONFORM.parse("2016/05/05")});
result . put ( "Pfingstsonntag" , holiDayEventRule ( EasterHoliday . WHIT_SUNDAY . getRule ( ) , currentYear , dateRulesTimeZone ) ) ; // new Date[]{CONFORM.parse("2014/06/08"), CONFORM.parse("2015/05/24"), CONFORM.parse("2016/05/15")});
result . put ( "Pfingstmontag" , holiDayEventRule ( EasterHoliday . WHIT_MONDAY . getRule ( ) , currentYear , dateRulesTimeZone ) ) ; // new Date[]{CONFORM.parse("2014/06/09"), CONFORM.parse("2015/05/25"), CONFORM.parse("2016/05/16")});
@ -226,47 +221,45 @@ public class DateDetection {
result . put ( "Allerseelen" , sameDayEveryYear ( Calendar . NOVEMBER , 2 , currentYear ) ) ;
result . put ( "Martinstag" , sameDayEveryYear ( Calendar . NOVEMBER , 11 , currentYear ) ) ;
result . put ( "St. Martin" , result . get ( "Martinstag" ) ) ;
result . put ( "Buß- und Bettag" , holiDayEventRule ( new SimpleDateRule ( Calendar . NOVEMBER , 22 , Calendar . WEDNESDAY , true ) , currentYear , dateRulesTimeZone ) ) ; // new Date[]{CONFORM.parse("2014/11/19"), CONFORM.parse("2015/11/18"), CONFORM.parse("2016/11/16")});
result . put ( "Nikolaus" , sameDayEveryYear ( Calendar . DECEMBER , 6 , currentYear ) ) ;
result . put ( "Heiligabend" , sameDayEveryYear ( Calendar . DECEMBER , 24 , currentYear ) ) ;
result . put ( "1. Weihnachtsfeiertag" , sameDayEveryYear ( Calendar . DECEMBER , 25 , currentYear ) ) ;
result . put ( "2. Weihnachtsfeiertag" , sameDayEveryYear ( Calendar . DECEMBER , 26 , currentYear ) ) ;
/* Advent : four Sundays before Chritsmas */
final Date [ ] advents1 = new Date [ 3 ] , advents2 = new Date [ 3 ] , advents3 = new Date [ 3 ] , advents4 = new Date [ 3 ] ,
volkstrauertagen = new Date [ 3 ] , sundaysOfTheDead = new Date [ 3 ] ;
year = currentYear - 1 ;
final TemporalAdjuster prevSunday = TemporalAdjusters . previous ( DayOfWeek . SUNDAY ) ;
for ( int i = 0 ; i < 3 ; i + + ) {
final LocalDate christmas = LocalDate . of ( year , java . time . Month . DECEMBER , 25 ) ;
final LocalDate advent4 = christmas . with ( prevSunday ) ;
final LocalDate advent3 = advent4 . with ( prevSunday ) ;
final LocalDate advent2 = advent3 . with ( prevSunday ) ;
final LocalDate advent1 = advent2 . with ( prevSunday ) ;
final LocalDate sundayOfTheDead = advent1 . with ( prevSunday ) ;
final LocalDate volkstrauertag = sundayOfTheDead . with ( prevSunday ) ;
advents4 [ i ] = toMidnightUTCDate ( advent4 ) ;
advents3 [ i ] = toMidnightUTCDate ( advent3 ) ;
advents2 [ i ] = toMidnightUTCDate ( advent2 ) ;
advents1 [ i ] = toMidnightUTCDate ( advent1 ) ;
sundaysOfTheDead [ i ] = toMidnightUTCDate ( sundayOfTheDead ) ;
volkstrauertagen [ i ] = toMidnightUTCDate ( volkstrauertag ) ;
year + + ;
}
result . put ( "1. Advent" , advents1 ) ;
result . put ( "2. Advent" , advents2 ) ;
result . put ( "3. Advent" , advents3 ) ;
result . put ( "4. Advent" , advents4 ) ;
/* Sunday of the Dead (also called Eternity Sunday) : last Sunday before Advent */
/* Advent : four Sundays before Chritsmas */
final Date [ ] advents1 = new Date [ 3 ] , advents2 = new Date [ 3 ] , advents3 = new Date [ 3 ] , advents4 = new Date [ 3 ] ,
volkstrauertagen = new Date [ 3 ] , sundaysOfTheDead = new Date [ 3 ] ;
year = currentYear - 1 ;
final TemporalAdjuster prevSunday = TemporalAdjusters . previous ( DayOfWeek . SUNDAY ) ;
for ( int i = 0 ; i < 3 ; i + + ) {
final LocalDate christmas = LocalDate . of ( year , java . time . Month . DECEMBER , 25 ) ;
final LocalDate advent4 = christmas . with ( prevSunday ) ;
final LocalDate advent3 = advent4 . with ( prevSunday ) ;
final LocalDate advent2 = advent3 . with ( prevSunday ) ;
final LocalDate advent1 = advent2 . with ( prevSunday ) ;
final LocalDate sundayOfTheDead = advent1 . with ( prevSunday ) ;
final LocalDate volkstrauertag = sundayOfTheDead . with ( prevSunday ) ;
advents4 [ i ] = toMidnightUTCDate ( advent4 ) ;
advents3 [ i ] = toMidnightUTCDate ( advent3 ) ;
advents2 [ i ] = toMidnightUTCDate ( advent2 ) ;
advents1 [ i ] = toMidnightUTCDate ( advent1 ) ;
sundaysOfTheDead [ i ] = toMidnightUTCDate ( sundayOfTheDead ) ;
volkstrauertagen [ i ] = toMidnightUTCDate ( volkstrauertag ) ;
year + + ;
}
result . put ( "1. Advent" , advents1 ) ;
result . put ( "2. Advent" , advents2 ) ;
result . put ( "3. Advent" , advents3 ) ;
result . put ( "4. Advent" , advents4 ) ;
/* Sunday of the Dead (also called Eternity Sunday) : last Sunday before Advent */
result . put ( "Totensonntag" , sundaysOfTheDead ) ;
/* "people's day of mourning" : two Sundays before Advent */
result . put ( "Volkstrauertag" , volkstrauertagen ) ;
result . put ( "Volkstrauertag" , volkstrauertagen ) ;
result . put ( "Silvester" , sameDayEveryYear ( Calendar . DECEMBER , 31 , currentYear ) ) ;
@ -286,23 +279,23 @@ public class DateDetection {
result . put ( "Christmas Day" , result . get ( "1. Weihnachtsfeiertag" ) ) ;
result . put ( "Boxing Day" , result . get ( "2. Weihnachtsfeiertag" ) ) ;
result . put ( "New Year's Eve" , result . get ( "Silvester" ) ) ;
return result ;
}
/ * *
* Convert a date to an old style java . util . Date instance with time set at
* midnight on UTC time zone .
*
* @param localDate
* a simple date with year month and day without time zone
* @return a java . util . Date instance or null when localDate is null
* /
public static Date toMidnightUTCDate ( final LocalDate localDate ) {
if ( localDate = = null ) {
return null ;
}
return Date . from ( ZonedDateTime . of ( localDate , LocalTime . MIDNIGHT , UTC_TIMEZONE . toZoneId ( ) ) . toInstant ( ) ) ;
}
return result ;
}
/ * *
* Convert a date to an old style java . util . Date instance with time set at
* midnight on UTC time zone .
*
* @param localDate
* a simple date with year month and day without time zone
* @return a java . util . Date instance or null when localDate is null
* /
public static Date toMidnightUTCDate ( final LocalDate localDate ) {
if ( localDate = = null ) {
return null ;
}
return Date . from ( ZonedDateTime . of ( localDate , LocalTime . MIDNIGHT , UTC_TIMEZONE . toZoneId ( ) ) . toInstant ( ) ) ;
}
/ * *
* @param month value of month ( Calendar . month is 0 based )
@ -330,28 +323,28 @@ public class DateDetection {
* @return 3 years of same holiday starting in last year ( currentYear - 1 )
* /
private static Date [ ] holiDayEventRule ( final DateRule holidayrule , final int currentYear , final TimeZone ruleTimeZone ) {
final Date [ ] r = new Date [ 3 ] ;
final Calendar january1Calendar = new GregorianCalendar ( ruleTimeZone ) ;
/* Clear all fields to get a 00:00:00:000 time part */
january1Calendar . clear ( ) ;
/* Calendar using UTC time zone to produce date results */
final Calendar utcCalendar = new GregorianCalendar ( UTC_TIMEZONE ) ;
/* Calendar using the same time zone as in the holidayrule to extract year,month, and day fields */
final Calendar ruleCalendar = new GregorianCalendar ( ruleTimeZone ) ;
int year = currentYear - 1 ; // set previous year as start year
for ( int y = 0 ; y < 3 ; y + + ) {
january1Calendar . set ( year , Calendar . JANUARY , 1 ) ;
Date holiday = holidayrule . firstAfter ( january1Calendar . getTime ( ) ) ;
ruleCalendar . setTime ( holiday ) ;
utcCalendar . set ( ruleCalendar . get ( Calendar . YEAR ) , ruleCalendar . get ( Calendar . MONTH ) ,
ruleCalendar . get ( Calendar . DAY_OF_MONTH ) ) ;
r [ y ] = utcCalendar . getTime ( ) ;
year + + ;
}
return r ;
final Date [ ] r = new Date [ 3 ] ;
final Calendar january1Calendar = new GregorianCalendar ( ruleTimeZone ) ;
/* Clear all fields to get a 00:00:00:000 time part */
january1Calendar . clear ( ) ;
/* Calendar using UTC time zone to produce date results */
final Calendar utcCalendar = new GregorianCalendar ( UTC_TIMEZONE ) ;
/* Calendar using the same time zone as in the holidayrule to extract year,month, and day fields */
final Calendar ruleCalendar = new GregorianCalendar ( ruleTimeZone ) ;
int year = currentYear - 1 ; // set previous year as start year
for ( int y = 0 ; y < 3 ; y + + ) {
january1Calendar . set ( year , Calendar . JANUARY , 1 ) ;
Date holiday = holidayrule . firstAfter ( january1Calendar . getTime ( ) ) ;
ruleCalendar . setTime ( holiday ) ;
utcCalendar . set ( ruleCalendar . get ( Calendar . YEAR ) , ruleCalendar . get ( Calendar . MONTH ) ,
ruleCalendar . get ( Calendar . DAY_OF_MONTH ) ) ;
r [ y ] = utcCalendar . getTime ( ) ;
year + + ;
}
return r ;
}
/ * *
@ -360,10 +353,10 @@ public class DateDetection {
* @return Easter sunday and monday dates on three years starting from last year
* /
private static Date [ ] getOsternEventRule ( final int currentYear , final TimeZone ruleTimeZone ) {
ArrayList < Date > osternDates = new ArrayList < > ( ) ;
Collections . addAll ( osternDates , holiDayEventRule ( EasterHoliday . EASTER_SUNDAY . getRule ( ) , currentYear , ruleTimeZone ) ) ;
Collections . addAll ( osternDates , holiDayEventRule ( EasterHoliday . EASTER_MONDAY . getRule ( ) , currentYear , ruleTimeZone ) ) ;
return osternDates . toArray ( new Date [ osternDates . size ( ) ] ) ;
ArrayList < Date > osternDates = new ArrayList < > ( ) ;
Collections . addAll ( osternDates , holiDayEventRule ( EasterHoliday . EASTER_SUNDAY . getRule ( ) , currentYear , ruleTimeZone ) ) ;
Collections . addAll ( osternDates , holiDayEventRule ( EasterHoliday . EASTER_MONDAY . getRule ( ) , currentYear , ruleTimeZone ) ) ;
return osternDates . toArray ( new Date [ osternDates . size ( ) ] ) ;
}
/ * *
@ -552,10 +545,10 @@ public class DateDetection {
int month = this . firstEntity = = EntityType . MONTH ? i1 : this . secondEntity = = EntityType . MONTH ? i2 : i3 ;
if ( day > MaxDaysInMonth [ month - 1 ] ) continue ; // validity check of the day number
int year = this . firstEntity = = EntityType . YEAR ? i1 : this . secondEntity = = EntityType . YEAR ? i2 : i3 ;
final Date parsed = parseDateSafely (
year + "/" + ( month < 10 ? "0" : "" ) + month + "/" + ( day < 10 ? "0" : "" ) + day , CONFORM ) ;
final Date parsed = parseDateSafely (
year + "/" + ( month < 10 ? "0" : "" ) + month + "/" + ( day < 10 ? "0" : "" ) + day , CONFORM ) ;
if ( parsed ! = null ) {
dates . add ( parsed ) ;
dates . add ( parsed ) ;
}
if ( dates . size ( ) > 100 ) { dates . clear ( ) ; break ; } // that does not make sense
}
@ -564,29 +557,29 @@ public class DateDetection {
}
/ * *
* Safely parse the given string to an instant using the given formatter . Return
* null when the format can not be applied to the given string or when any
* parsing error occurred .
*
* @param str
* the string to parse
* @param formatter
* the formatter to use
* @return an Instant instance or null
* /
protected static Date parseDateSafely ( final String str , final DateTimeFormatter formatter ) {
Date res = null ;
if ( str ! = null & & ! str . isEmpty ( ) ) {
try {
if ( formatter ! = null ) {
res = Date . from ( LocalDate . parse ( str , formatter ) . atStartOfDay ( ) . toInstant ( ZoneOffset . UTC ) ) ;
}
} catch ( final RuntimeException ignored ) {
}
}
return res ;
}
/ * *
* Safely parse the given string to an instant using the given formatter . Return
* null when the format can not be applied to the given string or when any
* parsing error occurred .
*
* @param str
* the string to parse
* @param formatter
* the formatter to use
* @return an Instant instance or null
* /
protected static Date parseDateSafely ( final String str , final DateTimeFormatter formatter ) {
Date res = null ;
if ( str ! = null & & ! str . isEmpty ( ) ) {
try {
if ( formatter ! = null ) {
res = Date . from ( LocalDate . parse ( str , formatter ) . atStartOfDay ( ) . toInstant ( ZoneOffset . UTC ) ) ;
}
} catch ( final RuntimeException ignored ) {
}
}
return res ;
}
public static enum ShortStyle implements StyleParser {
MD_ENGLISH ( EntityType . MONTH , EntityType . DAY , // Big-endian (month, day), e.g. "from october 1st to september 13th"
@ -647,12 +640,12 @@ public class DateDetection {
final Date atThisYear = parseDateSafely ( thisyear + datestub , CONFORM ) ;
if ( atThisYear ! = null ) {
dates . add ( atThisYear ) ;
dates . add ( atThisYear ) ;
}
final Date atNextYear = parseDateSafely ( nextyear + datestub , CONFORM ) ;
if ( atNextYear ! = null ) {
dates . add ( atNextYear ) ;
dates . add ( atNextYear ) ;
}
//dates.add(atThisYear.after(TODAY) ? atThisYear : atNextYear); // we consider these kind of dates as given for the future
if ( dates . size ( ) > 100 ) { dates . clear ( ) ; break ; } // that does not make sense
@ -701,10 +694,10 @@ public class DateDetection {
Date d = parseDateSafely ( text , CONFORM ) ;
//if (d == null) try {d = GenericFormatter.FORMAT_SHORT_DAY.parse(text);} catch (ParseException e) {} // did not work well and fired for wrong formats; do not use
if ( d = = null ) {
d = parseDateSafely ( text , GenericFormatter . FORMAT_RFC1123_SHORT ) ;
d = parseDateSafely ( text , GenericFormatter . FORMAT_RFC1123_SHORT ) ;
}
if ( d = = null ) {
d = parseDateSafely ( text , GenericFormatter . FORMAT_ANSIC ) ;
d = parseDateSafely ( text , GenericFormatter . FORMAT_ANSIC ) ;
}
if ( d = = null ) {