2013-04-18 23:35:02 +02:00
package net.osmand.util ;
2015-05-10 00:50:44 +02:00
/* Can be commented out in order to run the main function separately */
2013-04-18 23:35:02 +02:00
2015-08-21 12:13:51 +02:00
import java.io.Serializable ;
2016-02-12 16:50:27 +01:00
import java.text.DateFormatSymbols ;
2013-04-18 23:35:02 +02:00
import java.text.ParseException ;
import java.text.SimpleDateFormat ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Calendar ;
2018-01-25 16:53:38 +01:00
import java.util.HashMap ;
2016-08-27 16:10:20 +02:00
import java.util.HashSet ;
import java.util.List ;
2016-02-12 16:50:27 +01:00
import java.util.Locale ;
2018-01-25 16:53:38 +01:00
import java.util.Map ;
2016-08-27 16:10:20 +02:00
import java.util.Set ;
2013-04-18 23:35:02 +02:00
2015-12-02 16:48:33 +01:00
import gnu.trove.list.array.TIntArrayList ;
2013-04-18 23:35:02 +02:00
/ * *
* Class used to parse opening hours
2016-02-12 15:28:39 +01:00
* < p / >
2013-07-25 22:16:45 +02:00
* the method " parseOpenedHours " will parse an OSM opening_hours string and
* return an object of the type OpeningHours . That object can be used to check
2016-02-12 15:28:39 +01:00
* if the OSM feature is open at a certain time .
2013-04-18 23:35:02 +02:00
* /
public class OpeningHoursParser {
2016-02-12 16:50:27 +01:00
private static final String [ ] daysStr ;
private static final String [ ] localDaysStr ;
private static final String [ ] monthsStr ;
private static final String [ ] localMothsStr ;
2018-01-25 16:53:38 +01:00
private static final Map < String , String > additionalStrings = new HashMap < > ( ) ;
2016-02-12 16:50:27 +01:00
2017-12-08 17:32:52 +01:00
private static final int LOW_TIME_LIMIT = 120 ;
2017-12-11 10:24:19 +01:00
private static final int WITHOUT_TIME_LIMIT = - 1 ;
2018-01-22 12:40:43 +01:00
private static final int CURRENT_DAY_TIME_LIMIT = - 2 ;
2017-12-08 17:32:52 +01:00
2016-02-12 16:50:27 +01:00
static {
DateFormatSymbols dateFormatSymbols = DateFormatSymbols . getInstance ( Locale . US ) ;
monthsStr = dateFormatSymbols . getShortMonths ( ) ;
daysStr = getTwoLettersStringArray ( dateFormatSymbols . getShortWeekdays ( ) ) ;
dateFormatSymbols = DateFormatSymbols . getInstance ( ) ;
localMothsStr = dateFormatSymbols . getShortMonths ( ) ;
localDaysStr = getTwoLettersStringArray ( dateFormatSymbols . getShortWeekdays ( ) ) ;
2018-01-25 16:53:38 +01:00
additionalStrings . put ( " off " , " off " ) ;
additionalStrings . put ( " is_open " , " Open " ) ;
additionalStrings . put ( " is_open_24_7 " , " Open 24/7 " ) ;
additionalStrings . put ( " will_open_at " , " Will open at " ) ;
additionalStrings . put ( " open_from " , " Open from " ) ;
additionalStrings . put ( " will_close_at " , " Will close at " ) ;
additionalStrings . put ( " open_till " , " Open till " ) ;
additionalStrings . put ( " will_open_tomorrow_at " , " Will open tomorrow at " ) ;
additionalStrings . put ( " will_open_on " , " Will open on " ) ;
}
/ * *
* Set additional localized strings like " off " , etc .
* /
public static void setAdditionalString ( String key , String value ) {
additionalStrings . put ( key , value ) ;
2016-02-12 16:50:27 +01:00
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
2013-07-25 22:16:45 +02:00
* Default values for sunrise and sunset . Might be computed afterwards , not final .
2013-04-18 23:35:02 +02:00
* /
private static String sunrise = " 07:00 " , sunset = " 21:00 " ;
2013-07-25 22:16:45 +02:00
2013-04-18 23:35:02 +02:00
/ * *
2013-07-25 22:16:45 +02:00
* Hour of when you would expect a day to be ended .
2016-02-12 15:28:39 +01:00
* This is to be used when no end hour is known ( like pubs that open at a certain time ,
2013-04-18 23:35:02 +02:00
* but close at a variable time , depending on the number of clients ) .
* OsmAnd needs to show a value , so there is some arbitrary default value chosen .
* /
private static String endOfDay = " 24:00 " ;
2016-02-12 15:28:39 +01:00
2016-02-12 16:50:27 +01:00
private static String [ ] getTwoLettersStringArray ( String [ ] strings ) {
String [ ] newStrings = new String [ strings . length ] ;
for ( int i = 0 ; i < strings . length ; i + + ) {
if ( strings [ i ] ! = null ) {
if ( strings [ i ] . length ( ) > 2 ) {
2018-01-19 16:27:46 +01:00
newStrings [ i ] = Algorithms . capitalizeFirstLetter ( strings [ i ] . substring ( 0 , 2 ) ) ;
2016-02-12 16:50:27 +01:00
} else {
2018-01-19 16:27:46 +01:00
newStrings [ i ] = Algorithms . capitalizeFirstLetter ( strings [ i ] ) ;
2016-02-12 16:50:27 +01:00
}
}
}
return newStrings ;
}
private static int getDayIndex ( int i ) {
switch ( i ) {
case 0 : return Calendar . MONDAY ;
case 1 : return Calendar . TUESDAY ;
case 2 : return Calendar . WEDNESDAY ;
case 3 : return Calendar . THURSDAY ;
case 4 : return Calendar . FRIDAY ;
case 5 : return Calendar . SATURDAY ;
case 6 : return Calendar . SUNDAY ;
default : return - 1 ;
}
}
2013-04-18 23:35:02 +02:00
/ * *
2016-02-12 15:28:39 +01:00
* This class contains the entire OpeningHours schema and
2013-04-18 23:35:02 +02:00
* offers methods to check directly weather something is open
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @author sander
* /
2015-08-25 14:21:53 +02:00
public static class OpeningHours implements Serializable {
2016-02-12 15:28:39 +01:00
2018-01-25 16:53:38 +01:00
public static final int ALL_SEQUENCES = - 1 ;
2013-04-18 23:35:02 +02:00
/ * *
* list of the different rules
* /
private ArrayList < OpeningHoursRule > rules ;
2017-04-04 00:12:37 +02:00
private String original ;
2018-01-25 16:53:38 +01:00
private int sequenceCount ;
public static class Info {
private boolean opened ;
private boolean opened24_7 ;
private String openingTime ;
private String nearToOpeningTime ;
private String closingTime ;
private String nearToClosingTime ;
private String openingTomorrow ;
private String openingDay ;
private String ruleString ;
public boolean isOpened ( ) {
return opened ;
}
public boolean isOpened24_7 ( ) {
return opened24_7 ;
}
public String getInfo ( ) {
if ( isOpened24_7 ( ) ) {
if ( ! Algorithms . isEmpty ( ruleString ) ) {
return additionalStrings . get ( " is_open " ) + " " + ruleString ;
} else {
return additionalStrings . get ( " is_open_24_7 " ) ;
}
} else if ( ! Algorithms . isEmpty ( nearToOpeningTime ) ) {
return additionalStrings . get ( " will_open_at " ) + " " + nearToOpeningTime ;
} else if ( ! Algorithms . isEmpty ( openingTime ) ) {
return additionalStrings . get ( " open_from " ) + " " + openingTime ;
} else if ( ! Algorithms . isEmpty ( nearToClosingTime ) ) {
return additionalStrings . get ( " will_close_at " ) + " " + nearToClosingTime ;
} else if ( ! Algorithms . isEmpty ( closingTime ) ) {
return additionalStrings . get ( " open_till " ) + " " + closingTime ;
} else if ( ! Algorithms . isEmpty ( openingTomorrow ) ) {
return additionalStrings . get ( " will_open_tomorrow_at " ) + " " + openingTomorrow ;
} else if ( ! Algorithms . isEmpty ( openingDay ) ) {
return additionalStrings . get ( " will_open_on " ) + " " + openingDay + " . " ;
} else if ( ! Algorithms . isEmpty ( ruleString ) ) {
return ruleString ;
} else {
return " " ;
}
}
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* Constructor
2016-02-12 15:28:39 +01:00
*
* @param rules List of OpeningHoursRule to be given
2013-04-18 23:35:02 +02:00
* /
public OpeningHours ( ArrayList < OpeningHoursRule > rules ) {
this . rules = rules ;
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* Empty constructor
* /
2016-02-12 15:28:39 +01:00
public OpeningHours ( ) {
2013-04-18 23:35:02 +02:00
rules = new ArrayList < OpeningHoursRule > ( ) ;
}
2016-02-12 15:28:39 +01:00
2018-01-25 16:53:38 +01:00
public List < Info > getInfo ( ) {
return getInfo ( Calendar . getInstance ( ) ) ;
}
public List < Info > getInfo ( Calendar cal ) {
List < Info > res = new ArrayList < > ( ) ;
for ( int i = 0 ; i < sequenceCount ; i + + ) {
Info info = getInfo ( cal , i ) ;
res . add ( info ) ;
}
return res . isEmpty ( ) ? null : res ;
}
public Info getCombinedInfo ( ) {
return getCombinedInfo ( Calendar . getInstance ( ) ) ;
}
public Info getCombinedInfo ( Calendar cal ) {
return getInfo ( cal , ALL_SEQUENCES ) ;
}
private Info getInfo ( Calendar cal , int sequenceIndex ) {
Info info = new Info ( ) ;
boolean opened = isOpenedForTimeV2 ( cal , sequenceIndex ) ;
info . opened = opened ;
info . ruleString = getCurrentRuleTime ( cal , sequenceIndex ) ;
if ( opened ) {
info . opened24_7 = isOpened24_7 ( sequenceIndex ) ;
info . closingTime = getClosingTime ( cal , sequenceIndex ) ;
info . nearToClosingTime = getNearToClosingTime ( cal , sequenceIndex ) ;
} else {
info . openingTime = getOpeningTime ( cal , sequenceIndex ) ;
info . nearToOpeningTime = getNearToOpeningTime ( cal , sequenceIndex ) ;
info . openingTomorrow = getOpeningTomorrow ( cal , sequenceIndex ) ;
info . openingDay = getOpeningDay ( cal , sequenceIndex ) ;
}
return info ;
}
2013-04-18 23:35:02 +02:00
/ * *
* add a rule to the opening hours
2016-02-12 15:28:39 +01:00
*
* @param r rule to add
2013-04-18 23:35:02 +02:00
* /
2016-02-12 15:28:39 +01:00
public void addRule ( OpeningHoursRule r ) {
2013-04-18 23:35:02 +02:00
rules . add ( r ) ;
}
2016-02-12 15:28:39 +01:00
2018-01-22 16:32:32 +01:00
/ * *
* add rules to the opening hours
*
* @param rules to add
* /
2018-01-25 16:53:38 +01:00
public void addRules ( List < ? extends OpeningHoursRule > rules ) {
2018-01-22 16:32:32 +01:00
this . rules . addAll ( rules ) ;
}
2018-01-25 16:53:38 +01:00
public int getSequenceCount ( ) {
return sequenceCount ;
}
public void setSequenceCount ( int sequenceCount ) {
this . sequenceCount = sequenceCount ;
}
2013-04-18 23:35:02 +02:00
/ * *
* return the list of rules
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @return the rules
* /
2016-02-12 15:28:39 +01:00
public ArrayList < OpeningHoursRule > getRules ( ) {
2013-04-18 23:35:02 +02:00
return rules ;
}
2018-01-25 16:53:38 +01:00
public ArrayList < OpeningHoursRule > getRules ( int sequenceIndex ) {
if ( sequenceIndex = = ALL_SEQUENCES ) {
return rules ;
} else {
ArrayList < OpeningHoursRule > sequenceRules = new ArrayList < > ( ) ;
for ( OpeningHoursRule r : rules ) {
if ( r . getSequenceIndex ( ) = = sequenceIndex ) {
sequenceRules . add ( r ) ;
}
}
return sequenceRules ;
}
}
2016-08-27 16:10:20 +02:00
/ * *
* check if the feature is opened at time " cal "
*
* @param cal the time to check
* @return true if feature is open
* /
2018-01-25 16:53:38 +01:00
public boolean isOpenedForTimeV2 ( Calendar cal , int sequenceIndex ) {
2016-08-27 16:10:20 +02:00
// make exception for overlapping times i.e.
// (1) Mo 14:00-16:00; Tu off
// (2) Mo 14:00-02:00; Tu off
// in (2) we need to check first rule even though it is against specification
2018-01-25 16:53:38 +01:00
ArrayList < OpeningHoursRule > rules = getRules ( sequenceIndex ) ;
2016-08-27 16:10:20 +02:00
boolean overlap = false ;
2018-01-25 16:53:38 +01:00
for ( int i = rules . size ( ) - 1 ; i > = 0 ; i - - ) {
2016-08-27 16:10:20 +02:00
OpeningHoursRule r = rules . get ( i ) ;
2018-01-25 16:53:38 +01:00
if ( r . hasOverlapTimes ( ) ) {
2016-08-27 16:10:20 +02:00
overlap = true ;
break ;
}
}
// start from the most specific rule
2018-01-25 16:53:38 +01:00
for ( int i = rules . size ( ) - 1 ; i > = 0 ; i - - ) {
2018-02-17 15:05:36 +01:00
boolean checkNext = false ;
2018-02-17 14:33:03 +01:00
OpeningHoursRule rule = rules . get ( i ) ;
if ( rule . contains ( cal ) ) {
if ( i > 0 ) {
2018-02-17 15:05:36 +01:00
checkNext = ! rule . hasOverlapTimes ( cal , rules . get ( i - 1 ) ) ;
2018-02-17 14:33:03 +01:00
}
boolean open = rule . isOpenedForTime ( cal ) ;
2018-02-17 15:05:36 +01:00
if ( open | | ( ! overlap & & ! checkNext ) ) {
2016-08-27 16:10:20 +02:00
return open ;
}
}
}
return false ;
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* check if the feature is opened at time " cal "
2016-02-12 15:28:39 +01:00
*
* @param cal the time to check
2013-04-18 23:35:02 +02:00
* @return true if feature is open
* /
2016-02-12 15:28:39 +01:00
public boolean isOpenedForTime ( Calendar cal ) {
2018-01-25 16:53:38 +01:00
return isOpenedForTimeV2 ( cal , ALL_SEQUENCES ) ;
}
/ * *
* check if the feature is opened at time " cal "
*
* @param cal the time to check
* @param sequenceIndex the sequence index to check
* @return true if feature is open
* /
public boolean isOpenedForTime ( Calendar cal , int sequenceIndex ) {
2013-04-18 23:35:02 +02:00
/ *
* first check for rules that contain the current day
* afterwards check for rules that contain the previous
* day with overlapping times ( times after midnight )
* /
boolean isOpenDay = false ;
2018-01-25 16:53:38 +01:00
ArrayList < OpeningHoursRule > rules = getRules ( sequenceIndex ) ;
2016-02-12 15:28:39 +01:00
for ( OpeningHoursRule r : rules ) {
if ( r . containsDay ( cal ) & & r . containsMonth ( cal ) ) {
2013-04-18 23:35:02 +02:00
isOpenDay = r . isOpenedForTime ( cal , false ) ;
}
}
boolean isOpenPrevious = false ;
2016-02-12 15:28:39 +01:00
for ( OpeningHoursRule r : rules ) {
if ( r . containsPreviousDay ( cal ) & & r . containsMonth ( cal ) ) {
2013-04-18 23:35:02 +02:00
isOpenPrevious = r . isOpenedForTime ( cal , true ) ;
}
}
return isOpenDay | | isOpenPrevious ;
}
2016-02-12 15:28:39 +01:00
2018-01-25 16:53:38 +01:00
public boolean isOpened24_7 ( int sequenceIndex ) {
2017-12-08 13:25:34 +01:00
boolean opened24_7 = false ;
2018-01-25 16:53:38 +01:00
ArrayList < OpeningHoursRule > rules = getRules ( sequenceIndex ) ;
2017-12-07 12:12:20 +01:00
for ( OpeningHoursRule r : rules ) {
2017-12-08 13:25:34 +01:00
opened24_7 = r . isOpened24_7 ( ) ;
2017-12-07 12:12:20 +01:00
}
2017-12-08 13:25:34 +01:00
return opened24_7 ;
}
2018-01-25 16:53:38 +01:00
public String getNearToOpeningTime ( Calendar cal , int sequenceIndex ) {
return getTime ( cal , LOW_TIME_LIMIT , true , sequenceIndex ) ;
2017-12-08 13:25:34 +01:00
}
2018-01-25 16:53:38 +01:00
public String getOpeningTime ( Calendar cal , int sequenceIndex ) {
return getTime ( cal , CURRENT_DAY_TIME_LIMIT , true , sequenceIndex ) ;
2017-12-07 16:09:04 +01:00
}
2018-01-25 16:53:38 +01:00
public String getNearToClosingTime ( Calendar cal , int sequenceIndex ) {
return getTime ( cal , LOW_TIME_LIMIT , false , sequenceIndex ) ;
2017-12-07 17:45:20 +01:00
}
2018-01-25 16:53:38 +01:00
public String getClosingTime ( Calendar cal , int sequenceIndex ) {
return getTime ( cal , WITHOUT_TIME_LIMIT , false , sequenceIndex ) ;
2017-12-08 17:32:52 +01:00
}
2018-01-25 16:53:38 +01:00
public String getOpeningTomorrow ( Calendar calendar , int sequenceIndex ) {
2017-12-08 17:32:52 +01:00
Calendar cal = ( Calendar ) calendar . clone ( ) ;
String openingTime = " " ;
2018-01-25 16:53:38 +01:00
ArrayList < OpeningHoursRule > rules = getRules ( sequenceIndex ) ;
cal . add ( Calendar . DAY_OF_MONTH , 1 ) ;
for ( OpeningHoursRule r : rules ) {
if ( r . containsDay ( cal ) & & r . containsMonth ( cal ) ) {
openingTime = r . getTime ( cal , false , WITHOUT_TIME_LIMIT , true ) ;
}
}
return openingTime ;
}
public String getOpeningDay ( Calendar calendar , int sequenceIndex ) {
Calendar cal = ( Calendar ) calendar . clone ( ) ;
String openingTime = " " ;
ArrayList < OpeningHoursRule > rules = getRules ( sequenceIndex ) ;
2017-12-08 17:32:52 +01:00
for ( int i = 0 ; i < 7 ; i + + ) {
2017-12-08 17:41:10 +01:00
cal . add ( Calendar . DAY_OF_MONTH , 1 ) ;
2017-12-08 17:32:52 +01:00
for ( OpeningHoursRule r : rules ) {
if ( r . containsDay ( cal ) & & r . containsMonth ( cal ) ) {
2017-12-11 10:24:19 +01:00
openingTime = r . getTime ( cal , false , WITHOUT_TIME_LIMIT , true ) ;
2017-12-08 17:32:52 +01:00
}
}
if ( ! Algorithms . isEmpty ( openingTime ) ) {
openingTime + = " " + localDaysStr [ cal . get ( Calendar . DAY_OF_WEEK ) ] ;
break ;
}
}
return openingTime ;
2017-12-07 17:45:20 +01:00
}
2018-01-25 16:53:38 +01:00
private String getTime ( Calendar cal , int limit , boolean opening , int sequenceIndex ) {
String time = getTimeDay ( cal , limit , opening , sequenceIndex ) ;
2017-12-08 13:41:49 +01:00
if ( Algorithms . isEmpty ( time ) ) {
2018-01-25 16:53:38 +01:00
time = getTimeAnotherDay ( cal , limit , opening , sequenceIndex ) ;
2017-12-07 17:00:54 +01:00
}
2017-12-08 13:41:49 +01:00
return time ;
2017-12-07 17:00:54 +01:00
}
2018-01-25 16:53:38 +01:00
private String getTimeDay ( Calendar cal , int limit , boolean opening , int sequenceIndex ) {
2017-12-08 13:41:49 +01:00
String atTime = " " ;
2018-01-25 16:53:38 +01:00
ArrayList < OpeningHoursRule > rules = getRules ( sequenceIndex ) ;
2018-02-17 14:33:03 +01:00
OpeningHoursRule prevRule = null ;
2017-12-07 17:00:54 +01:00
for ( OpeningHoursRule r : rules ) {
if ( r . containsDay ( cal ) & & r . containsMonth ( cal ) ) {
2018-02-17 14:33:03 +01:00
if ( atTime . length ( ) > 0 & & prevRule ! = null & & ! r . hasOverlapTimes ( cal , prevRule ) ) {
return atTime ;
} else {
atTime = r . getTime ( cal , false , limit , opening ) ;
}
2017-12-07 17:00:54 +01:00
}
2018-02-17 14:33:03 +01:00
prevRule = r ;
2017-12-07 17:00:54 +01:00
}
2017-12-08 13:41:49 +01:00
return atTime ;
2017-12-07 17:00:54 +01:00
}
2018-01-25 16:53:38 +01:00
private String getTimeAnotherDay ( Calendar cal , int limit , boolean opening , int sequenceIndex ) {
2017-12-08 13:41:49 +01:00
String atTime = " " ;
2018-01-25 16:53:38 +01:00
ArrayList < OpeningHoursRule > rules = getRules ( sequenceIndex ) ;
2017-12-07 17:00:54 +01:00
for ( OpeningHoursRule r : rules ) {
2017-12-08 13:41:49 +01:00
if ( ( ( opening & & r . containsPreviousDay ( cal ) ) | | ( ! opening & & r . containsNextDay ( cal ) ) ) & & r . containsMonth ( cal ) ) {
2017-12-08 17:32:52 +01:00
atTime = r . getTime ( cal , true , limit , opening ) ;
2017-12-07 17:00:54 +01:00
}
}
2017-12-08 13:41:49 +01:00
return atTime ;
2017-12-07 17:00:54 +01:00
}
2015-04-07 00:34:11 +02:00
public String getCurrentRuleTime ( Calendar cal ) {
2018-01-25 16:53:38 +01:00
return getCurrentRuleTime ( cal , ALL_SEQUENCES ) ;
}
public String getCurrentRuleTime ( Calendar cal , int sequenceIndex ) {
2016-08-27 16:19:13 +02:00
// make exception for overlapping times i.e.
// (1) Mo 14:00-16:00; Tu off
// (2) Mo 14:00-02:00; Tu off
// in (2) we need to check first rule even though it is against specification
2018-01-25 16:53:38 +01:00
ArrayList < OpeningHoursRule > rules = getRules ( sequenceIndex ) ;
2016-08-27 16:19:13 +02:00
String ruleClosed = null ;
boolean overlap = false ;
for ( int i = rules . size ( ) - 1 ; i > = 0 ; i - - ) {
OpeningHoursRule r = rules . get ( i ) ;
if ( r . hasOverlapTimes ( ) ) {
overlap = true ;
break ;
}
}
// start from the most specific rule
for ( int i = rules . size ( ) - 1 ; i > = 0 ; i - - ) {
2018-02-17 15:05:36 +01:00
boolean checkNext = false ;
2018-02-17 14:33:03 +01:00
OpeningHoursRule rule = rules . get ( i ) ;
if ( rule . contains ( cal ) ) {
if ( i > 0 ) {
2018-02-17 15:05:36 +01:00
checkNext = ! rule . hasOverlapTimes ( cal , rules . get ( i - 1 ) ) ;
2018-02-17 14:33:03 +01:00
}
boolean open = rule . isOpenedForTime ( cal ) ;
2018-02-17 15:05:36 +01:00
if ( open | | ( ! overlap & & ! checkNext ) ) {
2018-02-17 14:33:03 +01:00
return rule . toLocalRuleString ( ) ;
2016-08-27 16:19:13 +02:00
} else {
2018-02-17 14:33:03 +01:00
ruleClosed = rule . toLocalRuleString ( ) ;
2016-08-27 16:19:13 +02:00
}
}
}
return ruleClosed ;
}
public String getCurrentRuleTimeV1 ( Calendar cal ) {
2015-04-07 00:34:11 +02:00
String ruleOpen = null ;
String ruleClosed = null ;
2016-02-12 15:28:39 +01:00
for ( OpeningHoursRule r : rules ) {
if ( r . containsPreviousDay ( cal ) & & r . containsMonth ( cal ) ) {
if ( r . isOpenedForTime ( cal , true ) ) {
2016-08-13 09:55:47 +02:00
ruleOpen = r . toLocalRuleString ( ) ;
2015-04-07 00:34:11 +02:00
} else {
2016-08-13 09:55:47 +02:00
ruleClosed = r . toLocalRuleString ( ) ;
2015-04-07 00:34:11 +02:00
}
}
}
2016-02-12 15:28:39 +01:00
for ( OpeningHoursRule r : rules ) {
if ( r . containsDay ( cal ) & & r . containsMonth ( cal ) ) {
if ( r . isOpenedForTime ( cal , false ) ) {
2016-08-13 09:55:47 +02:00
ruleOpen = r . toLocalRuleString ( ) ;
2015-04-07 00:34:11 +02:00
} else {
2016-08-13 09:55:47 +02:00
ruleClosed = r . toLocalRuleString ( ) ;
2015-04-07 00:34:11 +02:00
}
}
}
2016-02-12 15:28:39 +01:00
if ( ruleOpen ! = null ) {
2015-04-07 00:34:11 +02:00
return ruleOpen ;
}
2016-02-12 15:28:39 +01:00
return ruleClosed ;
2015-04-07 00:34:11 +02:00
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
@Override
2016-02-12 15:28:39 +01:00
public String toString ( ) {
2013-04-18 23:35:02 +02:00
StringBuilder s = new StringBuilder ( ) ;
if ( rules . isEmpty ( ) ) {
return " " ;
}
for ( OpeningHoursRule r : rules ) {
s . append ( r . toString ( ) ) . append ( " ; " ) ;
}
2016-02-12 15:28:39 +01:00
return s . substring ( 0 , s . length ( ) - 2 ) ;
2015-08-25 16:48:08 +02:00
}
2016-02-12 15:28:39 +01:00
2016-08-30 00:04:12 +02:00
public String toLocalString ( ) {
2016-02-12 16:50:27 +01:00
StringBuilder s = new StringBuilder ( ) ;
if ( rules . isEmpty ( ) ) {
return " " ;
}
for ( OpeningHoursRule r : rules ) {
2016-08-30 00:04:12 +02:00
s . append ( r . toLocalRuleString ( ) ) . append ( " ; " ) ;
2016-02-12 16:50:27 +01:00
}
return s . substring ( 0 , s . length ( ) - 2 ) ;
}
2017-04-04 00:12:37 +02:00
public void setOriginal ( String original ) {
this . original = original ;
}
public String getOriginal ( ) {
return original ;
}
2013-04-18 23:35:02 +02:00
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* Interface to represent a single rule
2016-02-12 15:28:39 +01:00
* < p / >
* A rule consist out of
* - a collection of days / dates
* - a time range
2013-04-18 23:35:02 +02:00
* /
2015-08-25 14:21:53 +02:00
public static interface OpeningHoursRule extends Serializable {
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* Check if , for this rule , the feature is opened for time " cal "
2016-02-12 15:28:39 +01:00
*
* @param cal the time to check
2013-04-18 23:35:02 +02:00
* @param checkPrevious only check for overflowing times ( after midnight ) or don ' t check for it
* @return true if the feature is open
* /
public boolean isOpenedForTime ( Calendar cal , boolean checkPrevious ) ;
2016-08-27 16:10:20 +02:00
/ * *
* Check if , for this rule , the feature is opened for time " cal "
* @param cal
* @return true if the feature is open
* /
public boolean isOpenedForTime ( Calendar cal ) ;
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* Check if the previous day before " cal " is part of this rule
2016-02-12 15:28:39 +01:00
*
* @param cal ; the time to check
2013-04-18 23:35:02 +02:00
* @return true if the previous day is part of the rule
* /
public boolean containsPreviousDay ( Calendar cal ) ;
/ * *
* Check if the day of " cal " is part of this rule
2016-02-12 15:28:39 +01:00
*
* @param cal the time to check
2013-04-18 23:35:02 +02:00
* @return true if the day is part of the rule
* /
public boolean containsDay ( Calendar cal ) ;
2016-02-12 15:28:39 +01:00
2017-12-07 17:00:54 +01:00
/ * *
* Check if the next day after " cal " is part of this rule
*
* @param cal the time to check
* @return true if the next day is part of the rule
* /
boolean containsNextDay ( Calendar cal ) ;
2013-06-25 12:11:58 +02:00
/ * *
* Check if the month of " cal " is part of this rule
2016-02-12 15:28:39 +01:00
*
2013-06-25 12:11:58 +02:00
* @param cal the time to check
* @return true if the month is part of the rule
* /
public boolean containsMonth ( Calendar cal ) ;
2016-08-27 16:10:20 +02:00
/ * *
* @return true if the rule overlap to the next day
* /
public boolean hasOverlapTimes ( ) ;
2018-02-17 14:33:03 +01:00
/ * *
* Check if r rule times overlap with this rule times at " cal " date .
*
* @param cal the date to check
* @param r the rule to check
* @return true if the this rule times overlap with r times
* /
public boolean hasOverlapTimes ( Calendar cal , OpeningHoursRule r ) ;
2016-08-27 16:10:20 +02:00
/ * *
* @param cal
* @return true if rule applies for current time
* /
public boolean contains ( Calendar cal ) ;
2016-02-12 15:28:39 +01:00
2018-01-25 16:53:38 +01:00
public int getSequenceIndex ( ) ;
2016-02-12 15:28:39 +01:00
2016-08-27 16:10:20 +02:00
public String toRuleString ( ) ;
2016-02-12 16:50:27 +01:00
public String toLocalRuleString ( ) ;
2017-12-07 12:12:20 +01:00
2017-12-08 10:31:25 +01:00
boolean isOpened24_7 ( ) ;
2017-12-07 16:09:04 +01:00
2017-12-08 17:32:52 +01:00
String getTime ( Calendar cal , boolean checkAnotherDay , int limit , boolean opening ) ;
2013-04-18 23:35:02 +02:00
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* implementation of the basic OpeningHoursRule
2016-02-12 15:28:39 +01:00
* < p / >
2013-06-25 12:11:58 +02:00
* This implementation only supports month , day of weeks and numeral times , or the value " off "
2013-04-18 23:35:02 +02:00
* /
2016-02-12 15:28:39 +01:00
public static class BasicOpeningHourRule implements OpeningHoursRule {
2013-04-18 23:35:02 +02:00
/ * *
* represents the list on which days it is open .
* Day number 0 is MONDAY
* /
private boolean [ ] days = new boolean [ 7 ] ;
2013-07-25 22:16:45 +02:00
2013-06-25 12:11:58 +02:00
/ * *
2013-07-25 22:16:45 +02:00
* represents the list on which month it is open .
2015-12-29 16:11:55 +01:00
* Day number 0 is JANUARY .
2013-06-25 12:11:58 +02:00
* /
private boolean [ ] months = new boolean [ 12 ] ;
2016-08-27 16:10:20 +02:00
/ * *
* represents the list on which day it is open .
* /
private boolean [ ] dayMonths = new boolean [ 31 ] ;
2013-07-25 22:16:45 +02:00
2013-04-18 23:35:02 +02:00
/ * *
* lists of equal size representing the start and end times
* /
2015-12-29 16:11:55 +01:00
private TIntArrayList startTimes = new TIntArrayList ( ) , endTimes = new TIntArrayList ( ) ;
2016-04-05 15:00:10 +02:00
private boolean publicHoliday = false ;
private boolean schoolHoliday = false ;
2016-08-27 16:15:44 +02:00
private boolean easter = false ;
2016-08-27 16:10:20 +02:00
/ * *
* Flag that means that time is off
* /
private boolean off = false ;
2016-02-12 15:28:39 +01:00
2018-01-25 16:53:38 +01:00
/ * *
2018-01-27 17:46:31 +01:00
* Additional information or limitation .
2018-01-25 16:53:38 +01:00
* https : //wiki.openstreetmap.org/wiki/Key:opening_hours/specification#explain:comment
* /
private String comment ;
private int sequenceIndex ;
public BasicOpeningHourRule ( ) {
this . sequenceIndex = 0 ;
}
public BasicOpeningHourRule ( int sequenceIndex ) {
this . sequenceIndex = sequenceIndex ;
}
public int getSequenceIndex ( ) {
return sequenceIndex ;
}
2013-04-18 23:35:02 +02:00
/ * *
* return an array representing the days of the rule
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @return the days of the rule
* /
public boolean [ ] getDays ( ) {
return days ;
}
2016-08-27 16:10:20 +02:00
/ * *
* @return the day months of the rule
* /
public boolean [ ] getDayMonths ( ) {
return dayMonths ;
}
2016-02-12 15:28:39 +01:00
2013-06-25 12:11:58 +02:00
/ * *
* return an array representing the months of the rule
2016-02-12 15:28:39 +01:00
*
2013-06-25 12:11:58 +02:00
* @return the months of the rule
* /
public boolean [ ] getMonths ( ) {
return months ;
}
2016-04-05 15:00:10 +02:00
public boolean appliesToPublicHolidays ( ) {
return publicHoliday ;
}
2016-08-27 16:15:44 +02:00
public boolean appliesEaster ( ) {
return easter ;
}
2016-04-05 15:00:10 +02:00
public boolean appliesToSchoolHolidays ( ) {
return schoolHoliday ;
}
2018-01-25 16:53:38 +01:00
public String getComment ( ) {
return comment ;
}
public void setComment ( String comment ) {
this . comment = comment ;
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* set a single start time , erase all previously added start times
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @param s startTime to set
* /
public void setStartTime ( int s ) {
2015-12-29 16:11:55 +01:00
setSingleValueForArrayList ( startTimes , s ) ;
2016-02-12 15:28:39 +01:00
if ( endTimes . size ( ) ! = 1 ) {
2015-12-29 16:11:55 +01:00
setSingleValueForArrayList ( endTimes , 0 ) ;
2013-04-18 23:35:02 +02:00
}
}
2015-12-29 16:11:55 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* set a single end time , erase all previously added end times
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @param e endTime to set
* /
public void setEndTime ( int e ) {
2015-12-29 16:11:55 +01:00
setSingleValueForArrayList ( endTimes , e ) ;
2016-02-12 15:28:39 +01:00
if ( startTimes . size ( ) ! = 1 ) {
2015-12-29 16:11:55 +01:00
setSingleValueForArrayList ( startTimes , 0 ) ;
2013-04-18 23:35:02 +02:00
}
}
2015-12-03 12:36:30 +01:00
/ * *
* Set single start time . If position exceeds index of last item by one
* then new value will be added .
* If value is between 0 and last index , then value in the position p will be overwritten
* with new one .
* Else exception will be thrown .
2016-02-12 15:28:39 +01:00
*
* @param s - value
2015-12-03 12:36:30 +01:00
* @param position - position to add
* /
public void setStartTime ( int s , int position ) {
2015-12-29 16:11:55 +01:00
if ( position = = startTimes . size ( ) ) {
startTimes . add ( s ) ;
endTimes . add ( 0 ) ;
2015-12-03 12:36:30 +01:00
} else {
2015-12-29 16:11:55 +01:00
startTimes . set ( position , s ) ;
2015-12-03 12:36:30 +01:00
}
}
/ * *
* Set single end time . If position exceeds index of last item by one
* then new value will be added .
* If value is between 0 and last index , then value in the position p will be overwritten
* with new one .
* Else exception will be thrown .
2016-02-12 15:28:39 +01:00
*
* @param s - value
2015-12-03 12:36:30 +01:00
* @param position - position to add
* /
public void setEndTime ( int s , int position ) {
2015-12-29 16:11:55 +01:00
if ( position = = startTimes . size ( ) ) {
endTimes . add ( s ) ;
startTimes . add ( 0 ) ;
2015-12-03 12:36:30 +01:00
} else {
2015-12-29 16:11:55 +01:00
endTimes . set ( position , s ) ;
2015-12-03 12:36:30 +01:00
}
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* get a single start time
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @return a single start time
* /
public int getStartTime ( ) {
2016-02-12 15:28:39 +01:00
if ( startTimes . size ( ) = = 0 ) {
2013-04-18 23:35:02 +02:00
return 0 ;
}
2015-12-29 16:11:55 +01:00
return startTimes . get ( 0 ) ;
2013-04-18 23:35:02 +02:00
}
2015-12-03 12:36:30 +01:00
/ * *
* get a single start time in position
2016-02-12 15:28:39 +01:00
*
2015-12-03 12:36:30 +01:00
* @param position position to get value from
* @return a single start time
* /
public int getStartTime ( int position ) {
2015-12-29 16:11:55 +01:00
return startTimes . get ( position ) ;
2015-12-03 12:36:30 +01:00
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* get a single end time
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @return a single end time
* /
public int getEndTime ( ) {
2016-02-12 15:28:39 +01:00
if ( endTimes . size ( ) = = 0 ) {
2013-04-18 23:35:02 +02:00
return 0 ;
}
2015-12-29 16:11:55 +01:00
return endTimes . get ( 0 ) ;
2013-04-18 23:35:02 +02:00
}
2015-12-02 16:48:33 +01:00
2015-12-03 12:36:30 +01:00
/ * *
* get a single end time in position
2016-02-12 15:28:39 +01:00
*
2015-12-03 12:36:30 +01:00
* @param position position to get value from
* @return a single end time
* /
public int getEndTime ( int position ) {
2015-12-29 16:11:55 +01:00
return endTimes . get ( position ) ;
2015-12-03 12:36:30 +01:00
}
2015-12-02 16:48:33 +01:00
/ * *
* get all start times as independent list
2016-02-12 15:28:39 +01:00
*
2015-12-02 16:48:33 +01:00
* @return all start times
* /
public TIntArrayList getStartTimes ( ) {
return new TIntArrayList ( startTimes ) ;
}
/ * *
* get all end times as independent list
2016-02-12 15:28:39 +01:00
*
2015-12-02 16:48:33 +01:00
* @return all end times
* /
public TIntArrayList getEndTimes ( ) {
return new TIntArrayList ( endTimes ) ;
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* Check if the weekday of time " cal " is part of this rule
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @param cal the time to check
* @return true if this day is part of the rule
* /
@Override
2016-02-12 15:28:39 +01:00
public boolean containsDay ( Calendar cal ) {
2013-04-18 23:35:02 +02:00
int i = cal . get ( Calendar . DAY_OF_WEEK ) ;
int d = ( i + 5 ) % 7 ;
2013-07-25 22:16:45 +02:00
if ( days [ d ] ) {
return true ;
2013-04-18 23:35:02 +02:00
}
return false ;
}
2016-02-12 15:28:39 +01:00
2017-12-07 17:00:54 +01:00
@Override
public boolean containsNextDay ( Calendar cal ) {
int i = cal . get ( Calendar . DAY_OF_WEEK ) ;
int p = ( i + 6 ) % 7 ;
if ( days [ p ] ) {
return true ;
}
return false ;
}
2013-04-18 23:35:02 +02:00
/ * *
* Check if the previous weekday of time " cal " is part of this rule
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @param cal the time to check
* @return true if the previous day is part of the rule
* /
@Override
2016-02-12 15:28:39 +01:00
public boolean containsPreviousDay ( Calendar cal ) {
2013-04-18 23:35:02 +02:00
int i = cal . get ( Calendar . DAY_OF_WEEK ) ;
int p = ( i + 4 ) % 7 ;
2013-07-30 21:28:19 +02:00
if ( days [ p ] ) {
return true ;
2013-04-18 23:35:02 +02:00
}
2013-07-30 21:28:19 +02:00
return false ;
2013-04-18 23:35:02 +02:00
}
2016-02-12 15:28:39 +01:00
2013-06-25 12:11:58 +02:00
/ * *
* Check if the month of " cal " is part of this rule
2016-02-12 15:28:39 +01:00
*
2013-06-25 12:11:58 +02:00
* @param cal the time to check
* @return true if the month is part of the rule
* /
@Override
public boolean containsMonth ( Calendar cal ) {
int i = cal . get ( Calendar . MONTH ) ;
if ( months [ i ] ) {
return true ;
}
return false ;
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* Check if this rule says the feature is open at time " cal "
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @param cal the time to check
* @return false in all other cases , also if only day is wrong
* /
@Override
public boolean isOpenedForTime ( Calendar cal , boolean checkPrevious ) {
2017-12-07 16:09:04 +01:00
int d = getCurrentDay ( cal ) ;
int p = getPreviousDay ( d ) ;
int time = getCurrentTimeInMinutes ( cal ) ; // Time in minutes
for ( int i = 0 ; i < startTimes . size ( ) ; i + + ) {
2015-12-29 16:11:55 +01:00
int startTime = this . startTimes . get ( i ) ;
int endTime = this . endTimes . get ( i ) ;
2013-04-18 23:35:02 +02:00
if ( startTime < endTime | | endTime = = - 1 ) {
2013-05-09 14:59:53 +02:00
// one day working like 10:00-20:00 (not 20:00-04:00)
2013-04-18 23:35:02 +02:00
if ( days [ d ] & & ! checkPrevious ) {
if ( time > = startTime & & ( endTime = = - 1 | | time < = endTime ) ) {
2016-08-27 16:10:20 +02:00
return ! off ;
2013-04-18 23:35:02 +02:00
}
}
} else {
2013-05-09 14:59:53 +02:00
// opening_hours includes day wrap like
// "We 20:00-03:00" or "We 07:00-07:00"
if ( time > = startTime & & days [ d ] & & ! checkPrevious ) {
2016-08-27 16:10:20 +02:00
return ! off ;
2013-05-09 14:59:53 +02:00
} else if ( time < endTime & & days [ p ] & & checkPrevious ) {
// check in previous day
2016-08-27 16:10:20 +02:00
return ! off ;
2013-04-18 23:35:02 +02:00
}
}
}
return false ;
}
2016-02-12 15:28:39 +01:00
2017-12-07 16:09:04 +01:00
private int getCurrentDay ( Calendar cal ) {
int i = cal . get ( Calendar . DAY_OF_WEEK ) ;
return ( i + 5 ) % 7 ;
}
private int getPreviousDay ( int currentDay ) {
int p = currentDay - 1 ;
if ( p < 0 ) {
p + = 7 ;
}
return p ;
}
2017-12-07 17:00:54 +01:00
private int getNextDay ( int currentDay ) {
int n = currentDay + 1 ;
if ( n > 6 ) {
n - = 7 ;
}
return n ;
}
2017-12-07 16:09:04 +01:00
private int getCurrentTimeInMinutes ( Calendar cal ) {
return cal . get ( Calendar . HOUR_OF_DAY ) * 60 + cal . get ( Calendar . MINUTE ) ;
}
2013-04-18 23:35:02 +02:00
@Override
2016-08-27 16:10:20 +02:00
public String toRuleString ( ) {
return toRuleString ( daysStr , monthsStr ) ;
2016-02-12 16:50:27 +01:00
}
2016-08-27 16:10:20 +02:00
private String toRuleString ( String [ ] dayNames , String [ ] monthNames ) {
2013-04-18 23:35:02 +02:00
StringBuilder b = new StringBuilder ( 25 ) ;
2016-08-27 16:10:20 +02:00
boolean allMonths = true ;
for ( int i = 0 ; i < months . length ; i + + ) {
if ( ! months [ i ] ) {
allMonths = false ;
break ;
2013-07-30 21:28:19 +02:00
}
2016-08-27 16:10:20 +02:00
}
// Month
if ( ! allMonths ) {
addArray ( months , monthNames , b ) ;
}
boolean allDays = true ;
for ( int i = 0 ; i < dayMonths . length ; i + + ) {
if ( ! dayMonths [ i ] ) {
allDays = false ;
break ;
2015-04-07 00:47:02 +02:00
}
2013-07-30 21:28:19 +02:00
}
2016-08-27 16:10:20 +02:00
if ( ! allDays ) {
addArray ( dayMonths , null , b ) ;
}
2013-07-30 21:28:19 +02:00
// Day
2016-02-12 16:50:27 +01:00
appendDaysString ( b , dayNames ) ;
2013-06-25 12:11:58 +02:00
// Time
2016-02-12 15:28:39 +01:00
if ( startTimes = = null | | startTimes . size ( ) = = 0 ) {
2017-12-08 10:31:25 +01:00
if ( isOpened24_7 ( ) ) {
2018-01-25 16:53:38 +01:00
b . setLength ( 0 ) ;
b . append ( " 24/7 " ) ;
2018-01-25 17:11:45 +01:00
}
if ( off ) {
2018-01-25 16:53:38 +01:00
b . append ( additionalStrings . get ( " off " ) ) ;
2017-12-07 12:12:20 +01:00
}
2018-01-25 16:53:38 +01:00
} else {
if ( isOpened24_7 ( ) ) {
b . setLength ( 0 ) ;
b . append ( " 24/7 " ) ;
} else {
for ( int i = 0 ; i < startTimes . size ( ) ; i + + ) {
int startTime = startTimes . get ( i ) ;
int endTime = endTimes . get ( i ) ;
if ( i > 0 ) {
b . append ( " , " ) ;
}
int stHour = startTime / 60 ;
int stTime = startTime - stHour * 60 ;
int enHour = endTime / 60 ;
int enTime = endTime - enHour * 60 ;
formatTime ( stHour , stTime , b ) ;
b . append ( " - " ) ; //$NON-NLS-1$
formatTime ( enHour , enTime , b ) ;
}
if ( off ) {
b . append ( " " ) . append ( additionalStrings . get ( " off " ) ) ;
2016-08-27 16:10:20 +02:00
}
2017-05-09 00:22:49 +02:00
}
2018-01-25 16:53:38 +01:00
}
if ( ! Algorithms . isEmpty ( comment ) ) {
if ( b . length ( ) > 0 ) {
if ( b . charAt ( b . length ( ) - 1 ) ! = ' ' ) {
b . append ( " " ) ;
}
2018-02-02 12:46:49 +01:00
b . append ( " - " ) . append ( comment ) ;
2018-01-25 16:53:38 +01:00
} else {
b . append ( comment ) ;
2013-04-18 23:35:02 +02:00
}
}
2016-08-27 16:10:20 +02:00
return b . toString ( ) ;
}
private void addArray ( boolean [ ] array , String [ ] arrayNames , StringBuilder b ) {
boolean dash = false ;
boolean first = true ;
for ( int i = 0 ; i < array . length ; i + + ) {
if ( array [ i ] ) {
if ( i > 0 & & array [ i - 1 ] & & i < array . length - 1 & & array [ i + 1 ] ) {
if ( ! dash ) {
dash = true ;
b . append ( " - " ) ; //$NON-NLS-1$
}
continue ;
}
if ( first ) {
first = false ;
} else if ( ! dash ) {
b . append ( " , " ) ; //$NON-NLS-1$
}
b . append ( arrayNames = = null ? ( i + 1 ) : arrayNames [ i ] ) ;
dash = false ;
}
}
if ( ! first ) {
b . append ( " " ) ;
}
2013-04-18 23:35:02 +02:00
}
2015-08-25 14:21:53 +02:00
2016-02-12 16:50:27 +01:00
@Override
public String toLocalRuleString ( ) {
2016-08-27 16:10:20 +02:00
return toRuleString ( localDaysStr , localMothsStr ) ;
2016-02-12 16:50:27 +01:00
}
2017-12-07 12:12:20 +01:00
@Override
2017-12-08 10:31:25 +01:00
public boolean isOpened24_7 ( ) {
boolean opened24_7 = true ;
2017-12-07 12:12:20 +01:00
for ( int i = 0 ; i < 7 ; i + + ) {
if ( ! days [ i ] ) {
2017-12-08 10:31:25 +01:00
opened24_7 = false ;
2017-12-07 12:12:20 +01:00
break ;
}
}
2018-01-25 16:53:38 +01:00
if ( opened24_7 ) {
if ( startTimes ! = null & & startTimes . size ( ) > 0 ) {
for ( int i = 0 ; i < startTimes . size ( ) ; i + + ) {
int startTime = startTimes . get ( i ) ;
int endTime = endTimes . get ( i ) ;
if ( startTime = = 0 & & endTime / 60 = = 24 ) {
return true ;
}
2017-12-07 12:12:20 +01:00
}
2018-01-25 16:53:38 +01:00
} else {
return true ;
2017-12-07 12:12:20 +01:00
}
}
return false ;
}
2017-12-07 16:09:04 +01:00
@Override
2017-12-08 17:32:52 +01:00
public String getTime ( Calendar cal , boolean checkAnotherDay , int limit , boolean opening ) {
2017-12-07 16:09:04 +01:00
StringBuilder sb = new StringBuilder ( ) ;
int d = getCurrentDay ( cal ) ;
2017-12-08 13:25:34 +01:00
int ad = opening ? getNextDay ( d ) : getPreviousDay ( d ) ;
2017-12-07 16:09:04 +01:00
int time = getCurrentTimeInMinutes ( cal ) ;
for ( int i = 0 ; i < startTimes . size ( ) ; i + + ) {
int startTime = startTimes . get ( i ) ;
int endTime = endTimes . get ( i ) ;
2018-01-22 12:40:43 +01:00
if ( opening ! = off ) {
2017-12-08 13:25:34 +01:00
if ( startTime < endTime | | endTime = = - 1 ) {
if ( days [ d ] & & ! checkAnotherDay ) {
int diff = startTime - time ;
2018-01-22 12:40:43 +01:00
if ( limit = = WITHOUT_TIME_LIMIT | | ( time < = startTime & & ( diff < = limit | | limit = = CURRENT_DAY_TIME_LIMIT ) ) ) {
2017-12-08 13:25:34 +01:00
formatTime ( startTime , sb ) ;
break ;
}
}
} else {
int diff = - 1 ;
if ( time < = startTime & & days [ d ] & & ! checkAnotherDay ) {
diff = startTime - time ;
} else if ( time > endTime & & days [ ad ] & & checkAnotherDay ) {
diff = 24 * 60 - endTime + time ;
}
2018-01-27 17:46:31 +01:00
if ( limit = = WITHOUT_TIME_LIMIT | | ( ( diff ! = - 1 & & diff < = limit ) | | limit = = CURRENT_DAY_TIME_LIMIT ) ) {
2017-12-07 17:00:54 +01:00
formatTime ( startTime , sb ) ;
2017-12-07 16:09:04 +01:00
break ;
}
}
} else {
2017-12-08 13:25:34 +01:00
if ( startTime < endTime & & endTime ! = - 1 ) {
if ( days [ d ] & & ! checkAnotherDay ) {
int diff = endTime - time ;
2018-01-22 12:40:43 +01:00
if ( ( limit = = WITHOUT_TIME_LIMIT & & diff > = 0 ) | | ( time < = endTime & & diff < = limit ) ) {
2017-12-08 13:25:34 +01:00
formatTime ( endTime , sb ) ;
break ;
}
2017-12-07 17:00:54 +01:00
}
2017-12-08 13:25:34 +01:00
} else {
int diff = - 1 ;
if ( time < = endTime & & days [ d ] & & ! checkAnotherDay ) {
diff = 24 * 60 - time + endTime ;
} else if ( time < endTime & & days [ ad ] & & checkAnotherDay ) {
2018-01-22 12:40:43 +01:00
diff = endTime - time ;
2017-12-07 17:00:54 +01:00
}
2018-01-22 12:40:43 +01:00
if ( limit = = WITHOUT_TIME_LIMIT | | ( diff ! = - 1 & & diff < = limit ) ) {
2017-12-07 17:00:54 +01:00
formatTime ( endTime , sb ) ;
2017-12-07 16:09:04 +01:00
break ;
}
}
}
}
2018-01-25 16:53:38 +01:00
String res = sb . toString ( ) ;
if ( res . length ( ) > 0 & & ! Algorithms . isEmpty ( comment ) ) {
2018-02-02 12:46:49 +01:00
res + = " - " + comment ;
2018-01-25 16:53:38 +01:00
}
return res ;
2017-12-07 16:09:04 +01:00
}
2013-04-18 23:35:02 +02:00
@Override
public String toString ( ) {
2016-08-27 16:10:20 +02:00
return toRuleString ( ) ;
2013-04-18 23:35:02 +02:00
}
2015-08-25 14:21:53 +02:00
public void appendDaysString ( StringBuilder builder ) {
2016-02-12 16:50:27 +01:00
appendDaysString ( builder , daysStr ) ;
}
public void appendDaysString ( StringBuilder builder , String [ ] daysNames ) {
2016-02-12 15:28:39 +01:00
boolean dash = false ;
2015-08-25 14:21:53 +02:00
boolean first = true ;
for ( int i = 0 ; i < 7 ; i + + ) {
if ( days [ i ] ) {
if ( i > 0 & & days [ i - 1 ] & & i < 6 & & days [ i + 1 ] ) {
if ( ! dash ) {
dash = true ;
builder . append ( " - " ) ; //$NON-NLS-1$
}
continue ;
}
if ( first ) {
first = false ;
} else if ( ! dash ) {
builder . append ( " , " ) ; //$NON-NLS-1$
}
2016-02-12 16:50:27 +01:00
builder . append ( daysNames [ getDayIndex ( i ) ] ) ;
2015-08-25 14:21:53 +02:00
dash = false ;
}
}
2016-04-05 15:00:10 +02:00
if ( publicHoliday ) {
if ( ! first ) {
builder . append ( " , " ) ;
}
builder . append ( " PH " ) ;
first = false ;
}
if ( schoolHoliday ) {
if ( ! first ) {
builder . append ( " , " ) ;
}
builder . append ( " SH " ) ;
first = false ;
}
2016-08-27 16:15:44 +02:00
if ( easter ) {
if ( ! first ) {
builder . append ( " , " ) ;
}
builder . append ( " Easter " ) ;
first = false ;
}
2016-08-27 16:10:20 +02:00
if ( ! first ) {
builder . append ( " " ) ;
}
2015-08-25 14:21:53 +02:00
}
2013-04-18 23:35:02 +02:00
/ * *
* Add a time range ( startTime - endTime ) to this rule
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @param startTime startTime to add
2016-02-12 15:28:39 +01:00
* @param endTime endTime to add
2013-04-18 23:35:02 +02:00
* /
public void addTimeRange ( int startTime , int endTime ) {
2015-12-29 16:11:55 +01:00
startTimes . add ( startTime ) ;
endTimes . add ( endTime ) ;
}
2013-04-18 23:35:02 +02:00
2015-12-30 17:29:29 +01:00
public int timesSize ( ) {
return startTimes . size ( ) ;
}
2015-12-29 16:11:55 +01:00
public void deleteTimeRange ( int position ) {
startTimes . removeAt ( position ) ;
endTimes . removeAt ( position ) ;
2013-04-18 23:35:02 +02:00
}
2015-12-03 12:36:30 +01:00
2015-12-29 16:11:55 +01:00
private static void setSingleValueForArrayList ( TIntArrayList arrayList , int s ) {
if ( arrayList . size ( ) > 0 ) {
arrayList . remove ( 0 , arrayList . size ( ) ) ;
}
arrayList . add ( s ) ;
2015-12-03 12:36:30 +01:00
}
2016-08-27 16:10:20 +02:00
@Override
public boolean isOpenedForTime ( Calendar cal ) {
int c = calculate ( cal ) ;
return c > 0 ;
}
@Override
public boolean contains ( Calendar cal ) {
int c = calculate ( cal ) ;
return c ! = 0 ;
}
2018-01-25 16:53:38 +01:00
2016-08-27 16:10:20 +02:00
@Override
public boolean hasOverlapTimes ( ) {
2018-02-17 15:05:36 +01:00
for ( int i = 0 ; i < this . startTimes . size ( ) ; i + + ) {
2016-08-27 16:10:20 +02:00
int startTime = this . startTimes . get ( i ) ;
int endTime = this . endTimes . get ( i ) ;
if ( startTime > = endTime & & endTime ! = - 1 ) {
return true ;
}
}
return false ;
}
2018-02-17 14:33:03 +01:00
@Override
public boolean hasOverlapTimes ( Calendar cal , OpeningHoursRule r ) {
if ( off ) {
return true ;
}
if ( r ! = null & & r . contains ( cal ) & & r instanceof BasicOpeningHourRule ) {
BasicOpeningHourRule rule = ( BasicOpeningHourRule ) r ;
if ( startTimes . size ( ) > 0 & & rule . startTimes . size ( ) > 0 ) {
2018-02-17 15:05:36 +01:00
for ( int i = 0 ; i < this . startTimes . size ( ) ; i + + ) {
int startTime = this . startTimes . get ( i ) ;
int endTime = this . endTimes . get ( i ) ;
if ( endTime = = - 1 ) {
endTime = 24 * 60 ;
} else if ( startTime > = endTime ) {
endTime = 24 * 60 + endTime ;
}
for ( int k = 0 ; k < rule . startTimes . size ( ) ; k + + ) {
int rStartTime = rule . startTimes . get ( k ) ;
int rEndTime = rule . endTimes . get ( k ) ;
if ( rEndTime = = - 1 ) {
rEndTime = 24 * 60 ;
} else if ( rStartTime > = rEndTime ) {
rEndTime = 24 * 60 + rEndTime ;
}
if ( ( rStartTime > = startTime & & rStartTime < endTime )
| | ( startTime > = rStartTime & & startTime < rEndTime ) ) {
return true ;
}
}
}
2018-02-17 14:33:03 +01:00
}
}
return false ;
}
2016-08-27 16:10:20 +02:00
private int calculate ( Calendar cal ) {
int month = cal . get ( Calendar . MONTH ) ;
if ( ! months [ month ] ) {
return 0 ;
}
int dmonth = cal . get ( Calendar . DAY_OF_MONTH ) - 1 ;
int i = cal . get ( Calendar . DAY_OF_WEEK ) ;
int day = ( i + 5 ) % 7 ;
int previous = ( day + 6 ) % 7 ;
boolean thisDay = days [ day ] | | dayMonths [ dmonth ] ;
// potential error for Dec 31 12:00-01:00
boolean previousDay = days [ previous ] | | ( dmonth > 0 & & dayMonths [ dmonth - 1 ] ) ;
if ( ! thisDay & & ! previousDay ) {
return 0 ;
}
int time = cal . get ( Calendar . HOUR_OF_DAY ) * 60 + cal . get ( Calendar . MINUTE ) ; // Time in minutes
for ( i = 0 ; i < startTimes . size ( ) ; i + + ) {
int startTime = this . startTimes . get ( i ) ;
int endTime = this . endTimes . get ( i ) ;
if ( startTime < endTime | | endTime = = - 1 ) {
// one day working like 10:00-20:00 (not 20:00-04:00)
if ( time > = startTime & & ( endTime = = - 1 | | time < = endTime ) & & thisDay ) {
return off ? - 1 : 1 ;
}
} else {
// opening_hours includes day wrap like
// "We 20:00-03:00" or "We 07:00-07:00"
if ( time > = startTime & & thisDay ) {
return off ? - 1 : 1 ;
} else if ( time < endTime & & previousDay ) {
return off ? - 1 : 1 ;
}
}
}
2018-01-25 16:53:38 +01:00
if ( thisDay & & ( startTimes = = null | | startTimes . isEmpty ( ) ) & & ! off ) {
return 1 ;
} else if ( thisDay & & ( startTimes = = null | | startTimes . isEmpty ( ) | | ! off ) ) {
2016-08-27 16:10:20 +02:00
return - 1 ;
}
return 0 ;
}
2013-04-18 23:35:02 +02:00
}
2015-09-30 18:27:37 +02:00
2018-01-25 16:53:38 +01:00
public static class UnparseableRule implements OpeningHoursRule {
2015-09-30 18:27:37 +02:00
private String ruleString ;
public UnparseableRule ( String ruleString ) {
this . ruleString = ruleString ;
}
@Override
public boolean isOpenedForTime ( Calendar cal , boolean checkPrevious ) {
return false ;
}
@Override
public boolean containsPreviousDay ( Calendar cal ) {
return false ;
}
2016-08-27 16:10:20 +02:00
@Override
public boolean hasOverlapTimes ( ) {
return false ;
}
2015-09-30 18:27:37 +02:00
2018-02-17 14:33:03 +01:00
@Override
public boolean hasOverlapTimes ( Calendar cal , OpeningHoursRule r ) {
return false ;
}
2015-09-30 18:27:37 +02:00
@Override
public boolean containsDay ( Calendar cal ) {
return false ;
}
2017-12-07 17:00:54 +01:00
@Override
public boolean containsNextDay ( Calendar cal ) {
return false ;
}
2015-09-30 18:27:37 +02:00
@Override
public boolean containsMonth ( Calendar cal ) {
return false ;
}
@Override
2016-08-27 16:10:20 +02:00
public String toRuleString ( ) {
2015-09-30 18:27:37 +02:00
return ruleString ;
}
2016-01-05 12:18:03 +01:00
2016-02-12 16:50:27 +01:00
@Override
public String toLocalRuleString ( ) {
2016-08-27 16:10:20 +02:00
return toRuleString ( ) ;
2016-02-12 16:50:27 +01:00
}
2017-12-07 12:12:20 +01:00
@Override
2017-12-08 10:31:25 +01:00
public boolean isOpened24_7 ( ) {
2017-12-07 12:12:20 +01:00
return false ;
}
2017-12-07 16:09:04 +01:00
@Override
2017-12-08 17:32:52 +01:00
public String getTime ( Calendar cal , boolean checkAnotherDay , int limit , boolean opening ) {
2017-12-07 17:00:54 +01:00
return " " ;
}
2016-01-05 12:18:03 +01:00
@Override
public String toString ( ) {
2016-08-27 16:10:20 +02:00
return toRuleString ( ) ;
}
@Override
public boolean isOpenedForTime ( Calendar cal ) {
return false ;
}
@Override
public boolean contains ( Calendar cal ) {
return false ;
}
2018-01-25 16:53:38 +01:00
@Override
public int getSequenceIndex ( ) {
return 0 ;
}
2016-08-27 16:10:20 +02:00
}
private enum TokenType {
TOKEN_UNKNOWN ( 0 ) ,
TOKEN_COLON ( 1 ) ,
TOKEN_COMMA ( 2 ) ,
TOKEN_DASH ( 3 ) ,
// order is important
TOKEN_MONTH ( 4 ) ,
TOKEN_DAY_MONTH ( 5 ) ,
TOKEN_HOLIDAY ( 6 ) ,
TOKEN_DAY_WEEK ( 6 ) ,
TOKEN_HOUR_MINUTES ( 7 ) ,
TOKEN_OFF_ON ( 8 ) ;
public final int ord ;
private TokenType ( int ord ) {
this . ord = ord ;
}
public int ord ( ) {
return ord ;
}
}
private static class Token {
public Token ( TokenType tokenType , String string ) {
type = tokenType ;
text = string ;
try {
mainNumber = Integer . parseInt ( string ) ;
} catch ( NumberFormatException e ) {
}
}
int mainNumber = - 1 ;
TokenType type ;
String text ;
@Override
public String toString ( ) {
return text + " [ " + type + " ] " ;
}
}
2018-01-25 16:53:38 +01:00
public static void parseRuleV2 ( String r , int sequenceIndex , List < OpeningHoursRule > rules ) {
String comment = null ;
int q1Index = r . indexOf ( '"' ) ;
if ( q1Index > = 0 ) {
int q2Index = r . indexOf ( '"' , q1Index + 1 ) ;
if ( q2Index > = 0 ) {
comment = r . substring ( q1Index + 1 , q2Index ) ;
String a = r . substring ( 0 , q1Index ) ;
String b = " " ;
if ( r . length ( ) > q2Index + 1 ) {
b = r . substring ( q2Index + 1 ) ;
}
r = a + b ;
}
}
r = r . toLowerCase ( ) . trim ( ) ;
2016-08-29 00:19:19 +02:00
2016-08-27 16:10:20 +02:00
final String [ ] daysStr = new String [ ] { " mo " , " tu " , " we " , " th " , " fr " , " sa " , " su " } ;
final String [ ] monthsStr = new String [ ] { " jan " , " feb " , " mar " , " apr " , " may " , " jun " , " jul " , " aug " , " sep " , " oct " , " nov " , " dec " } ;
2016-08-27 16:15:44 +02:00
final String [ ] holidayStr = new String [ ] { " ph " , " sh " , " easter " } ;
2016-08-27 16:10:20 +02:00
String sunrise = " 07:00 " ;
String sunset = " 21:00 " ;
String endOfDay = " 24:00 " ;
2016-08-29 00:19:19 +02:00
r = r . replace ( '(' , ' ' ) ; // avoid "(mo-su 17:00-20:00"
r = r . replace ( ')' , ' ' ) ;
2016-08-27 16:10:20 +02:00
String localRuleString = r . replaceAll ( " sunset " , sunset ) . replaceAll ( " sunrise " , sunrise )
. replaceAll ( " \\ + " , " - " + endOfDay ) ;
2018-01-25 16:53:38 +01:00
BasicOpeningHourRule basic = new BasicOpeningHourRule ( sequenceIndex ) ;
basic . setComment ( comment ) ;
2016-08-27 16:10:20 +02:00
boolean [ ] days = basic . getDays ( ) ;
boolean [ ] months = basic . getMonths ( ) ;
boolean [ ] dayMonths = basic . getDayMonths ( ) ;
if ( " 24/7 " . equals ( localRuleString ) ) {
Arrays . fill ( days , true ) ;
Arrays . fill ( months , true ) ;
basic . addTimeRange ( 0 , 24 * 60 ) ;
2018-01-22 16:32:32 +01:00
rules . add ( basic ) ;
return ;
2016-08-27 16:10:20 +02:00
}
List < Token > tokens = new ArrayList < > ( ) ;
int startWord = 0 ;
2018-01-25 16:53:38 +01:00
for ( int i = 0 ; i < = localRuleString . length ( ) ; i + + ) {
2016-08-27 16:10:20 +02:00
char ch = i = = localRuleString . length ( ) ? ' ' : localRuleString . charAt ( i ) ;
boolean delimiter = false ;
Token del = null ;
2018-01-25 16:53:38 +01:00
if ( Character . isWhitespace ( ch ) ) {
2016-08-27 16:10:20 +02:00
delimiter = true ;
2018-01-25 16:53:38 +01:00
} else if ( ch = = ':' ) {
2016-08-27 16:10:20 +02:00
del = new Token ( TokenType . TOKEN_COLON , " : " ) ;
2018-01-25 16:53:38 +01:00
} else if ( ch = = '-' ) {
2016-08-27 16:10:20 +02:00
del = new Token ( TokenType . TOKEN_DASH , " - " ) ;
2018-01-25 16:53:38 +01:00
} else if ( ch = = ',' ) {
2016-08-27 16:10:20 +02:00
del = new Token ( TokenType . TOKEN_COMMA , " , " ) ;
}
2018-01-25 16:53:38 +01:00
if ( delimiter | | del ! = null ) {
2016-08-27 16:10:20 +02:00
String wrd = localRuleString . substring ( startWord , i ) . trim ( ) ;
if ( wrd . length ( ) > 0 ) {
tokens . add ( new Token ( TokenType . TOKEN_UNKNOWN , wrd ) ) ;
}
startWord = i + 1 ;
2018-01-25 16:53:38 +01:00
if ( del ! = null ) {
2016-08-27 16:10:20 +02:00
tokens . add ( del ) ;
}
}
}
// recognize day of week
2018-01-25 16:53:38 +01:00
for ( Token t : tokens ) {
2016-08-27 16:10:20 +02:00
if ( t . type = = TokenType . TOKEN_UNKNOWN ) {
findInArray ( t , daysStr , TokenType . TOKEN_DAY_WEEK ) ;
}
if ( t . type = = TokenType . TOKEN_UNKNOWN ) {
findInArray ( t , monthsStr , TokenType . TOKEN_MONTH ) ;
}
if ( t . type = = TokenType . TOKEN_UNKNOWN ) {
findInArray ( t , holidayStr , TokenType . TOKEN_HOLIDAY ) ;
}
2017-05-09 00:22:49 +02:00
if ( t . type = = TokenType . TOKEN_UNKNOWN & & ( " off " . equals ( t . text ) | | " closed " . equals ( t . text ) ) ) {
2016-08-27 16:10:20 +02:00
t . type = TokenType . TOKEN_OFF_ON ;
t . mainNumber = 0 ;
}
if ( t . type = = TokenType . TOKEN_UNKNOWN & & ( " 24/7 " . equals ( t . text ) | | " open " . equals ( t . text ) ) ) {
t . type = TokenType . TOKEN_OFF_ON ;
t . mainNumber = 1 ;
}
}
// recognize hours minutes ( Dec 25: 08:30-20:00)
for ( int i = tokens . size ( ) - 1 ; i > = 0 ; i - - ) {
if ( tokens . get ( i ) . type = = TokenType . TOKEN_COLON ) {
if ( i > 0 & & i < tokens . size ( ) - 1 ) {
if ( tokens . get ( i - 1 ) . type = = TokenType . TOKEN_UNKNOWN & & tokens . get ( i - 1 ) . mainNumber ! = - 1 & &
tokens . get ( i + 1 ) . type = = TokenType . TOKEN_UNKNOWN & & tokens . get ( i + 1 ) . mainNumber ! = - 1 ) {
tokens . get ( i ) . mainNumber = 60 * tokens . get ( i - 1 ) . mainNumber + tokens . get ( i + 1 ) . mainNumber ;
tokens . get ( i ) . type = TokenType . TOKEN_HOUR_MINUTES ;
tokens . remove ( i + 1 ) ;
tokens . remove ( i - 1 ) ;
}
}
}
}
// recognize other numbers
// if there is no on/off and minutes/hours
boolean hoursSpecified = false ;
for ( int i = 0 ; i < tokens . size ( ) ; i + + ) {
if ( tokens . get ( i ) . type = = TokenType . TOKEN_HOUR_MINUTES | |
tokens . get ( i ) . type = = TokenType . TOKEN_OFF_ON ) {
hoursSpecified = true ;
2017-05-09 00:22:49 +02:00
break ;
2016-08-27 16:10:20 +02:00
}
}
for ( int i = 0 ; i < tokens . size ( ) ; i + + ) {
if ( tokens . get ( i ) . type = = TokenType . TOKEN_UNKNOWN & & tokens . get ( i ) . mainNumber > = 0 ) {
tokens . get ( i ) . type = hoursSpecified ? TokenType . TOKEN_DAY_MONTH : TokenType . TOKEN_HOUR_MINUTES ;
if ( tokens . get ( i ) . type = = TokenType . TOKEN_HOUR_MINUTES ) {
tokens . get ( i ) . mainNumber = tokens . get ( i ) . mainNumber * 60 ;
} else {
tokens . get ( i ) . mainNumber = tokens . get ( i ) . mainNumber - 1 ;
}
}
}
2018-01-22 16:32:32 +01:00
buildRule ( basic , tokens , rules ) ;
}
private static void buildRule ( BasicOpeningHourRule basic , List < Token > tokens , List < OpeningHoursRule > rules ) {
2016-08-27 16:10:20 +02:00
// order MONTH MONTH_DAY DAY_WEEK HOUR_MINUTE OPEN_OFF
TokenType currentParse = TokenType . TOKEN_UNKNOWN ;
List < Token [ ] > listOfPairs = new ArrayList < > ( ) ;
Set < TokenType > presentTokens = new HashSet < > ( ) ;
Token [ ] currentPair = new Token [ 2 ] ;
listOfPairs . add ( currentPair ) ;
int indexP = 0 ;
for ( int i = 0 ; i < = tokens . size ( ) ; i + + ) {
Token t = i = = tokens . size ( ) ? null : tokens . get ( i ) ;
if ( t = = null | | t . type . ord ( ) > currentParse . ord ( ) ) {
presentTokens . add ( currentParse ) ;
if ( currentParse = = TokenType . TOKEN_MONTH | | currentParse = = TokenType . TOKEN_DAY_MONTH
| | currentParse = = TokenType . TOKEN_DAY_WEEK | | currentParse = = TokenType . TOKEN_HOLIDAY ) {
2018-01-22 16:32:32 +01:00
2016-08-27 16:10:20 +02:00
boolean [ ] array = ( currentParse = = TokenType . TOKEN_MONTH ) ? basic . getMonths ( )
: ( currentParse = = TokenType . TOKEN_DAY_MONTH ) ? basic . getDayMonths ( ) : basic . getDays ( ) ;
for ( Token [ ] pair : listOfPairs ) {
if ( pair [ 0 ] ! = null & & pair [ 1 ] ! = null ) {
if ( pair [ 0 ] . mainNumber < = pair [ 1 ] . mainNumber ) {
2016-10-30 16:28:02 +01:00
for ( int j = pair [ 0 ] . mainNumber ; j < = pair [ 1 ] . mainNumber & & j < array . length ; j + + ) {
2016-08-27 16:10:20 +02:00
array [ j ] = true ;
}
} else {
// overflow
for ( int j = pair [ 0 ] . mainNumber ; j < array . length ; j + + ) {
array [ j ] = true ;
}
for ( int j = 0 ; j < = pair [ 1 ] . mainNumber ; j + + ) {
array [ j ] = true ;
}
}
} else if ( pair [ 0 ] ! = null ) {
2018-01-22 16:32:32 +01:00
if ( pair [ 0 ] . type = = TokenType . TOKEN_HOLIDAY ) {
if ( pair [ 0 ] . mainNumber = = 0 ) {
2016-08-27 16:10:20 +02:00
basic . publicHoliday = true ;
2018-01-22 16:32:32 +01:00
} else if ( pair [ 0 ] . mainNumber = = 1 ) {
2016-08-27 16:10:20 +02:00
basic . schoolHoliday = true ;
2018-01-22 16:32:32 +01:00
} else if ( pair [ 0 ] . mainNumber = = 2 ) {
2016-08-27 16:15:44 +02:00
basic . easter = true ;
2016-08-27 16:10:20 +02:00
}
2018-01-22 16:32:32 +01:00
} else if ( pair [ 0 ] . mainNumber > = 0 ) {
2016-08-27 16:10:20 +02:00
array [ pair [ 0 ] . mainNumber ] = true ;
}
}
}
} else if ( currentParse = = TokenType . TOKEN_HOUR_MINUTES ) {
for ( Token [ ] pair : listOfPairs ) {
2018-01-22 16:32:32 +01:00
if ( pair [ 0 ] ! = null & & pair [ 1 ] ! = null ) {
2016-08-27 16:10:20 +02:00
basic . addTimeRange ( pair [ 0 ] . mainNumber , pair [ 1 ] . mainNumber ) ;
}
}
} else if ( currentParse = = TokenType . TOKEN_OFF_ON ) {
Token [ ] l = listOfPairs . get ( 0 ) ;
2018-01-22 16:32:32 +01:00
if ( l [ 0 ] ! = null & & l [ 0 ] . mainNumber = = 0 ) {
2016-08-27 16:10:20 +02:00
basic . off = true ;
}
}
listOfPairs . clear ( ) ;
currentPair = new Token [ 2 ] ;
indexP = 0 ;
listOfPairs . add ( currentPair ) ;
currentPair [ indexP + + ] = t ;
2018-01-22 16:32:32 +01:00
if ( t ! = null ) {
2016-08-27 16:10:20 +02:00
currentParse = t . type ;
}
2018-01-22 16:47:33 +01:00
} else if ( t . type . ord ( ) < currentParse . ord ( ) & & indexP = = 0 & & tokens . size ( ) > i ) {
2018-01-25 16:53:38 +01:00
BasicOpeningHourRule newRule = new BasicOpeningHourRule ( basic . getSequenceIndex ( ) ) ;
newRule . setComment ( basic . getComment ( ) ) ;
buildRule ( newRule , tokens . subList ( i , tokens . size ( ) ) , rules ) ;
2018-01-22 16:32:32 +01:00
tokens = tokens . subList ( 0 , i + 1 ) ;
} else if ( t . type = = TokenType . TOKEN_COMMA ) {
if ( tokens . size ( ) > i + 1 & & tokens . get ( i + 1 ) ! = null & & tokens . get ( i + 1 ) . type . ord ( ) < currentParse . ord ( ) ) {
indexP = 0 ;
} else {
currentPair = new Token [ 2 ] ;
indexP = 0 ;
listOfPairs . add ( currentPair ) ;
}
} else if ( t . type = = TokenType . TOKEN_DASH ) {
} else if ( t . type . ord ( ) = = currentParse . ord ( ) ) {
2016-08-27 16:10:20 +02:00
if ( indexP < 2 ) {
currentPair [ indexP + + ] = t ;
}
}
}
if ( ! presentTokens . contains ( TokenType . TOKEN_MONTH ) ) {
Arrays . fill ( basic . getMonths ( ) , true ) ;
}
// if(!presentTokens.contains(TokenType.TOKEN_DAY_MONTH)) {
// Arrays.fill(basic.getDayMonths(), true);
// }
2018-01-22 16:32:32 +01:00
if ( ! presentTokens . contains ( TokenType . TOKEN_DAY_WEEK ) & & ! presentTokens . contains ( TokenType . TOKEN_HOLIDAY ) & &
2016-08-27 16:10:20 +02:00
! presentTokens . contains ( TokenType . TOKEN_DAY_MONTH ) ) {
Arrays . fill ( basic . getDays ( ) , true ) ;
2016-01-05 12:18:03 +01:00
}
2016-08-27 16:10:20 +02:00
// if(!presentTokens.contains(TokenType.TOKEN_HOUR_MINUTES)) {
// basic.addTimeRange(0, 24 * 60);
// }
// System.out.println(r + " " + tokens);
2018-01-22 16:32:32 +01:00
rules . add ( 0 , basic ) ;
2015-09-30 18:27:37 +02:00
}
2016-08-27 16:10:20 +02:00
private static void findInArray ( Token t , String [ ] list , TokenType tokenType ) {
for ( int i = 0 ; i < list . length ; i + + ) {
if ( list [ i ] . equals ( t . text ) ) {
t . type = tokenType ;
t . mainNumber = i ;
break ;
}
}
}
2015-09-30 18:27:37 +02:00
2018-01-25 16:53:38 +01:00
private static List < List < String > > splitSequences ( String format ) {
if ( format = = null ) {
return null ;
}
List < List < String > > res = new ArrayList < > ( ) ;
String [ ] sequences = format . split ( " \\ | \\ | " ) ;
for ( String seq : sequences ) {
seq = seq . trim ( ) ;
if ( seq . length ( ) = = 0 ) {
continue ;
}
List < String > rules = new ArrayList < > ( ) ;
boolean comment = false ;
StringBuilder sb = new StringBuilder ( ) ;
for ( int i = 0 ; i < seq . length ( ) ; i + + ) {
char c = seq . charAt ( i ) ;
if ( c = = '"' ) {
comment = ! comment ;
sb . append ( c ) ;
} else if ( c = = ';' & & ! comment ) {
if ( sb . length ( ) > 0 ) {
String s = sb . toString ( ) . trim ( ) ;
if ( s . length ( ) > 0 ) {
rules . add ( s ) ;
}
sb . setLength ( 0 ) ;
}
} else {
sb . append ( c ) ;
}
}
if ( sb . length ( ) > 0 ) {
rules . add ( sb . toString ( ) ) ;
sb . setLength ( 0 ) ;
}
res . add ( rules ) ;
}
return res ;
}
2013-04-18 23:35:02 +02:00
/ * *
* Parse an opening_hours string from OSM to an OpeningHours object which can be used to check
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @param r the string to parse
2015-09-30 18:27:37 +02:00
* @return BasicRule if the String is successfully parsed and UnparseableRule otherwise
2013-04-18 23:35:02 +02:00
* /
2018-01-25 16:53:38 +01:00
public static void parseRules ( String r , int sequenceIndex , List < OpeningHoursRule > rules ) {
parseRuleV2 ( r , sequenceIndex , rules ) ;
2013-04-18 23:35:02 +02:00
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
/ * *
* parse OSM opening_hours string to an OpeningHours object
2016-02-12 15:28:39 +01:00
*
2013-04-18 23:35:02 +02:00
* @param format the string to parse
* @return null when parsing was unsuccessful
* /
2016-02-12 15:28:39 +01:00
public static OpeningHours parseOpenedHours ( String format ) {
if ( format = = null ) {
2015-04-10 01:34:46 +02:00
return null ;
}
2013-04-18 23:35:02 +02:00
OpeningHours rs = new OpeningHours ( ) ;
2017-04-04 00:12:37 +02:00
rs . setOriginal ( format ) ;
2018-01-25 16:53:38 +01:00
// split the OSM string in multiple rules
List < List < String > > sequences = splitSequences ( format ) ;
for ( int i = 0 ; i < sequences . size ( ) ; i + + ) {
List < String > rules = sequences . get ( i ) ;
List < BasicOpeningHourRule > basicRules = new ArrayList < > ( ) ;
for ( String r : rules ) {
// check if valid
List < OpeningHoursRule > rList = new ArrayList < > ( ) ;
parseRules ( r , i , rList ) ;
for ( OpeningHoursRule rule : rList ) {
if ( rule instanceof BasicOpeningHourRule ) {
basicRules . add ( ( BasicOpeningHourRule ) rule ) ;
}
}
2013-04-18 23:35:02 +02:00
}
2018-01-25 16:53:38 +01:00
String basicRuleComment = null ;
for ( BasicOpeningHourRule bRule : basicRules ) {
if ( ! Algorithms . isEmpty ( bRule . getComment ( ) ) ) {
basicRuleComment = bRule . getComment ( ) ;
break ;
2018-01-22 16:32:32 +01:00
}
2013-04-18 23:35:02 +02:00
}
2018-01-25 16:53:38 +01:00
if ( ! Algorithms . isEmpty ( basicRuleComment ) ) {
for ( BasicOpeningHourRule bRule : basicRules ) {
bRule . setComment ( basicRuleComment ) ;
}
}
rs . addRules ( basicRules ) ;
2015-09-30 18:27:37 +02:00
}
2018-01-25 16:53:38 +01:00
rs . setSequenceCount ( sequences . size ( ) ) ;
2015-09-30 18:27:37 +02:00
return rs ;
}
/ * *
* parse OSM opening_hours string to an OpeningHours object .
* Does not return null when parsing unsuccessful . When parsing rule is unsuccessful ,
* such rule is stored as UnparseableRule .
2016-02-12 15:28:39 +01:00
*
2015-09-30 18:27:37 +02:00
* @param format the string to parse
* @return the OpeningHours object
* /
2016-02-12 15:28:39 +01:00
public static OpeningHoursParser . OpeningHours parseOpenedHoursHandleErrors ( String format ) {
if ( format = = null ) {
2015-09-30 18:27:37 +02:00
return null ;
}
OpeningHoursParser . OpeningHours rs = new OpeningHoursParser . OpeningHours ( ) ;
2017-04-04 00:12:37 +02:00
rs . setOriginal ( format ) ;
2018-01-25 16:53:38 +01:00
List < List < String > > sequences = splitSequences ( format ) ;
for ( int i = sequences . size ( ) - 1 ; i > = 0 ; i - - ) {
List < String > rules = sequences . get ( i ) ;
for ( String r : rules ) {
r = r . trim ( ) ;
if ( r . length ( ) = = 0 ) {
continue ;
}
// check if valid
List < OpeningHoursRule > rList = new ArrayList < > ( ) ;
parseRules ( r , i , rList ) ;
rs . addRules ( rList ) ;
2015-09-30 18:27:37 +02:00
}
2013-04-18 23:35:02 +02:00
}
2018-01-25 16:53:38 +01:00
rs . setSequenceCount ( sequences . size ( ) ) ;
2013-04-18 23:35:02 +02:00
return rs ;
}
2016-02-12 15:28:39 +01:00
2018-01-25 16:53:38 +01:00
public static List < OpeningHours . Info > getInfo ( String format ) {
OpeningHours openingHours = OpeningHoursParser . parseOpenedHours ( format ) ;
if ( openingHours = = null ) {
return null ;
} else {
return openingHours . getInfo ( ) ;
}
}
2016-02-12 15:28:39 +01:00
private static void formatTime ( int h , int t , StringBuilder b ) {
2013-07-30 21:28:19 +02:00
if ( h < 10 ) {
2013-04-18 23:35:02 +02:00
b . append ( " 0 " ) ; //$NON-NLS-1$
2013-07-30 21:28:19 +02:00
}
2013-04-18 23:35:02 +02:00
b . append ( h ) . append ( " : " ) ; //$NON-NLS-1$
2013-07-30 21:28:19 +02:00
if ( t < 10 ) {
2013-04-18 23:35:02 +02:00
b . append ( " 0 " ) ; //$NON-NLS-1$
}
b . append ( t ) ;
}
2016-02-12 15:28:39 +01:00
2017-12-07 17:00:54 +01:00
private static void formatTime ( int minutes , StringBuilder sb ) {
int hour = minutes / 60 ;
int time = minutes - hour * 60 ;
formatTime ( hour , time , sb ) ;
}
2016-02-12 15:28:39 +01:00
2013-07-30 21:28:19 +02:00
/ * *
* test if the calculated opening hours are what you expect
2016-02-12 15:28:39 +01:00
*
* @param time the time to test in the format " dd.MM.yyyy HH:mm "
* @param hours the OpeningHours object
2013-07-30 21:28:19 +02:00
* @param expected the expected state
* /
2013-04-18 23:35:02 +02:00
private static void testOpened ( String time , OpeningHours hours , boolean expected ) throws ParseException {
Calendar cal = Calendar . getInstance ( ) ;
2016-02-12 16:50:27 +01:00
cal . setTime ( new SimpleDateFormat ( " dd.MM.yyyy HH:mm " , Locale . US ) . parse ( time ) ) ;
2018-01-25 16:53:38 +01:00
boolean calculated = hours . isOpenedForTimeV2 ( cal , OpeningHours . ALL_SEQUENCES ) ;
2015-09-10 00:30:31 +02:00
System . out . printf ( " %sok: Expected %s: %b = %b (rule %s) \ n " ,
2018-01-25 16:53:38 +01:00
( ( calculated ! = expected ) ? " NOT " : " " ) , time , expected , calculated , hours . getCurrentRuleTime ( cal , OpeningHours . ALL_SEQUENCES ) ) ;
2016-02-12 15:28:39 +01:00
if ( calculated ! = expected ) {
throw new IllegalArgumentException ( " BUG!!! " ) ;
}
2013-04-18 23:35:02 +02:00
}
2016-01-05 12:18:03 +01:00
2018-01-22 12:40:43 +01:00
/ * *
* test if the calculated opening hours are what you expect
*
* @param time the time to test in the format " dd.MM.yyyy HH:mm "
* @param hours the OpeningHours object
* @param expected the expected string in format :
* " Open from HH:mm " - open in 5 hours
* " Will open at HH:mm " - open in 2 hours
* " Open till HH:mm " - close in 5 hours
* " Will close at HH:mm " - close in 2 hours
* " Will open on HH:mm (Mo,Tu,We,Th,Fr,Sa,Su) " - open in > 5 hours
2018-01-25 16:53:38 +01:00
* " Will open tomorrow at HH:mm " - open in > 5 hours tomorrow
2018-01-22 12:40:43 +01:00
* " Open 24/7 " - open 24 / 7
* /
private static void testInfo ( String time , OpeningHours hours , String expected ) throws ParseException
2018-01-25 16:53:38 +01:00
{
testInfo ( time , hours , expected , OpeningHours . ALL_SEQUENCES ) ;
}
/ * *
* test if the calculated opening hours are what you expect
*
* @param time the time to test in the format " dd.MM.yyyy HH:mm "
* @param hours the OpeningHours object
* @param expected the expected string in format :
* " Open from HH:mm " - open in 5 hours
* " Will open at HH:mm " - open in 2 hours
* " Open till HH:mm " - close in 5 hours
* " Will close at HH:mm " - close in 2 hours
* " Will open on HH:mm (Mo,Tu,We,Th,Fr,Sa,Su) " - open in > 5 hours
* " Will open tomorrow at HH:mm " - open in > 5 hours tomorrow
* " Open 24/7 " - open 24 / 7
* @param sequenceIndex sequence index of rules separated by | |
* /
private static void testInfo ( String time , OpeningHours hours , String expected , int sequenceIndex ) throws ParseException
2018-01-22 12:40:43 +01:00
{
Calendar cal = Calendar . getInstance ( ) ;
cal . setTime ( new SimpleDateFormat ( " dd.MM.yyyy HH:mm " , Locale . US ) . parse ( time ) ) ;
2018-01-25 16:53:38 +01:00
String description ;
boolean result ;
if ( sequenceIndex = = OpeningHours . ALL_SEQUENCES ) {
OpeningHours . Info info = hours . getCombinedInfo ( cal ) ;
description = info . getInfo ( ) ;
result = expected . equalsIgnoreCase ( description ) ;
} else {
List < OpeningHours . Info > infos = hours . getInfo ( cal ) ;
OpeningHours . Info info = infos . get ( sequenceIndex ) ;
description = info . getInfo ( ) ;
result = expected . equalsIgnoreCase ( description ) ;
}
2018-01-22 12:40:43 +01:00
System . out . printf ( " %sok: Expected %s (%s): %s (rule %s) \ n " ,
2018-01-25 16:53:38 +01:00
( ! result ? " NOT " : " " ) , time , expected , description , hours . getCurrentRuleTime ( cal , sequenceIndex ) ) ;
2018-01-22 12:40:43 +01:00
if ( ! result )
throw new IllegalArgumentException ( " BUG!!! " ) ;
}
2016-01-05 12:18:03 +01:00
private static void testParsedAndAssembledCorrectly ( String timeString , OpeningHours hours ) {
2016-08-27 16:10:20 +02:00
String assembledString = hours . toString ( ) ;
2016-02-12 16:50:27 +01:00
boolean isCorrect = assembledString . equalsIgnoreCase ( timeString ) ;
2016-01-05 12:18:03 +01:00
System . out . printf ( " %sok: Expected: \" %s \" got: \" %s \" \ n " ,
2016-04-07 11:38:46 +02:00
( ! isCorrect ? " NOT " : " " ) , timeString , assembledString ) ;
2016-01-05 12:18:03 +01:00
if ( ! isCorrect ) {
throw new IllegalArgumentException ( " BUG!!! " ) ;
}
}
2016-02-12 15:28:39 +01:00
2013-04-18 23:35:02 +02:00
public static void main ( String [ ] args ) throws ParseException {
2017-05-09 00:22:49 +02:00
// 0. not supported MON DAY-MON DAY (only supported Feb 2-14 or Feb-Oct: 09:00-17:30)
2016-09-16 10:35:33 +02:00
// parseOpenedHours("Feb 16-Oct 15: 09:00-18:30; Oct 16-Nov 15: 09:00-17:30; Nov 16-Feb 15: 09:00-16:30");
2018-01-25 16:53:38 +01:00
// 1. not properly supported
2016-09-16 10:35:33 +02:00
// hours = parseOpenedHours("Mo-Su (sunrise-00:30)-(sunset+00:30)");
2018-01-25 16:53:38 +01:00
// test basic case
2016-02-12 15:28:39 +01:00
OpeningHours hours = parseOpenedHours ( " Mo-Fr 08:30-14:40 " ) ; //$NON-NLS-1$
2018-01-25 16:53:38 +01:00
System . out . println ( hours ) ;
2013-04-18 23:35:02 +02:00
testOpened ( " 09.08.2012 11:00 " , hours , true ) ;
testOpened ( " 09.08.2012 16:00 " , hours , false ) ;
2016-02-07 19:06:10 +01:00
hours = parseOpenedHours ( " mo-fr 07:00-19:00; sa 12:00-18:00 " ) ;
2016-02-12 15:28:39 +01:00
2016-04-05 15:00:10 +02:00
String string = " Mo-Fr 11:30-15:00, 17:30-23:00; Sa, Su, PH 11:30-23:00 " ;
hours = parseOpenedHours ( string ) ;
testParsedAndAssembledCorrectly ( string , hours ) ;
2015-09-10 00:30:31 +02:00
System . out . println ( hours ) ;
2016-08-27 16:10:20 +02:00
testOpened ( " 7.09.2015 14:54 " , hours , true ) ; // monday
2015-09-10 00:30:31 +02:00
testOpened ( " 7.09.2015 15:05 " , hours , false ) ;
2016-08-27 16:10:20 +02:00
testOpened ( " 6.09.2015 16:05 " , hours , true ) ;
2016-02-12 15:28:39 +01:00
2013-07-30 21:28:19 +02:00
2013-04-18 23:35:02 +02:00
// two time and date ranges
hours = parseOpenedHours ( " Mo-We, Fr 08:30-14:40,15:00-19:00 " ) ; //$NON-NLS-1$
System . out . println ( hours ) ;
testOpened ( " 08.08.2012 14:00 " , hours , true ) ;
testOpened ( " 08.08.2012 14:50 " , hours , false ) ;
2013-07-30 21:28:19 +02:00
testOpened ( " 10.08.2012 15:00 " , hours , true ) ;
2013-04-18 23:35:02 +02:00
// test exception on general schema
hours = parseOpenedHours ( " Mo-Sa 08:30-14:40; Tu 08:00 - 14:00 " ) ; //$NON-NLS-1$
System . out . println ( hours ) ;
testOpened ( " 07.08.2012 14:20 " , hours , false ) ;
2017-05-09 00:22:49 +02:00
testOpened ( " 07.08.2012 08:15 " , hours , true ) ; // Tuesday
2013-07-30 21:28:19 +02:00
2013-04-18 23:35:02 +02:00
// test off value
hours = parseOpenedHours ( " Mo-Sa 09:00-18:25; Th off " ) ; //$NON-NLS-1$
System . out . println ( hours ) ;
testOpened ( " 08.08.2012 12:00 " , hours , true ) ;
testOpened ( " 09.08.2012 12:00 " , hours , false ) ;
2013-07-30 21:28:19 +02:00
2016-08-27 16:10:20 +02:00
// test 24/7
2013-04-18 23:35:02 +02:00
hours = parseOpenedHours ( " 24/7 " ) ; //$NON-NLS-1$
System . out . println ( hours ) ;
testOpened ( " 08.08.2012 23:59 " , hours , true ) ;
2013-07-30 21:28:19 +02:00
testOpened ( " 08.08.2012 12:23 " , hours , true ) ;
testOpened ( " 08.08.2012 06:23 " , hours , true ) ;
2013-04-18 23:35:02 +02:00
// some people seem to use the following syntax:
hours = parseOpenedHours ( " Sa-Su 24/7 " ) ;
System . out . println ( hours ) ;
hours = parseOpenedHours ( " Mo-Fr 9-19 " ) ;
System . out . println ( hours ) ;
hours = parseOpenedHours ( " 09:00-17:00 " ) ;
System . out . println ( hours ) ;
hours = parseOpenedHours ( " sunrise-sunset " ) ;
System . out . println ( hours ) ;
hours = parseOpenedHours ( " 10:00+ " ) ;
System . out . println ( hours ) ;
hours = parseOpenedHours ( " Su-Th sunset-24:00, 04:00-sunrise; Fr-Sa sunset-sunrise " ) ;
System . out . println ( hours ) ;
testOpened ( " 12.08.2012 04:00 " , hours , true ) ;
testOpened ( " 12.08.2012 23:00 " , hours , true ) ;
testOpened ( " 08.08.2012 12:00 " , hours , false ) ;
testOpened ( " 08.08.2012 05:00 " , hours , true ) ;
2013-05-09 23:47:39 +02:00
// test simple day wrap
hours = parseOpenedHours ( " Mo 20:00-02:00 " ) ;
System . out . println ( hours ) ;
testOpened ( " 05.05.2013 10:30 " , hours , false ) ;
testOpened ( " 05.05.2013 23:59 " , hours , false ) ;
testOpened ( " 06.05.2013 10:30 " , hours , false ) ;
testOpened ( " 06.05.2013 20:30 " , hours , true ) ;
testOpened ( " 06.05.2013 23:59 " , hours , true ) ;
testOpened ( " 07.05.2013 00:00 " , hours , true ) ;
testOpened ( " 07.05.2013 00:30 " , hours , true ) ;
testOpened ( " 07.05.2013 01:59 " , hours , true ) ;
testOpened ( " 07.05.2013 20:30 " , hours , false ) ;
// test maximum day wrap
hours = parseOpenedHours ( " Su 10:00-10:00 " ) ;
System . out . println ( hours ) ;
testOpened ( " 05.05.2013 09:59 " , hours , false ) ;
testOpened ( " 05.05.2013 10:00 " , hours , true ) ;
testOpened ( " 05.05.2013 23:59 " , hours , true ) ;
testOpened ( " 06.05.2013 00:00 " , hours , true ) ;
testOpened ( " 06.05.2013 09:59 " , hours , true ) ;
testOpened ( " 06.05.2013 10:00 " , hours , false ) ;
// test day wrap as seen on OSM
hours = parseOpenedHours ( " Tu-Th 07:00-2:00; Fr 17:00-4:00; Sa 18:00-05:00; Su,Mo off " ) ;
System . out . println ( hours ) ;
2016-08-27 16:10:20 +02:00
testOpened ( " 05.05.2013 04:59 " , hours , true ) ; // sunday 05.05.2013
2013-05-09 23:47:39 +02:00
testOpened ( " 05.05.2013 05:00 " , hours , false ) ;
testOpened ( " 05.05.2013 12:30 " , hours , false ) ;
testOpened ( " 06.05.2013 10:30 " , hours , false ) ;
testOpened ( " 07.05.2013 01:00 " , hours , false ) ;
testOpened ( " 07.05.2013 20:25 " , hours , true ) ;
testOpened ( " 07.05.2013 23:59 " , hours , true ) ;
testOpened ( " 08.05.2013 00:00 " , hours , true ) ;
testOpened ( " 08.05.2013 02:00 " , hours , false ) ;
2013-06-25 12:11:58 +02:00
2015-05-10 00:50:44 +02:00
// test day wrap as seen on OSM
2015-05-10 01:25:23 +02:00
hours = parseOpenedHours ( " Mo-Th 09:00-03:00; Fr-Sa 09:00-04:00; Su off " ) ;
2015-05-10 00:50:44 +02:00
testOpened ( " 11.05.2015 08:59 " , hours , false ) ;
2015-05-10 01:25:23 +02:00
testOpened ( " 11.05.2015 09:01 " , hours , true ) ;
2015-05-10 00:50:44 +02:00
testOpened ( " 12.05.2015 02:59 " , hours , true ) ;
testOpened ( " 12.05.2015 03:00 " , hours , false ) ;
2016-02-12 15:28:39 +01:00
testOpened ( " 16.05.2015 03:59 " , hours , true ) ;
2015-05-10 01:25:23 +02:00
testOpened ( " 16.05.2015 04:01 " , hours , false ) ;
testOpened ( " 17.05.2015 01:00 " , hours , true ) ;
testOpened ( " 17.05.2015 04:01 " , hours , false ) ;
2015-05-10 00:50:44 +02:00
2015-05-10 01:25:23 +02:00
hours = parseOpenedHours ( " Tu-Th 07:00-2:00; Fr 17:00-4:00; Sa 18:00-05:00; Su,Mo off " ) ;
testOpened ( " 11.05.2015 08:59 " , hours , false ) ;
testOpened ( " 11.05.2015 09:01 " , hours , false ) ;
2016-08-27 16:10:20 +02:00
testOpened ( " 12.05.2015 01:59 " , hours , false ) ;
2015-05-10 01:25:23 +02:00
testOpened ( " 12.05.2015 02:59 " , hours , false ) ;
testOpened ( " 12.05.2015 03:00 " , hours , false ) ;
2016-08-27 16:10:20 +02:00
testOpened ( " 13.05.2015 01:59 " , hours , true ) ;
testOpened ( " 13.05.2015 02:59 " , hours , false ) ;
2016-02-12 15:28:39 +01:00
testOpened ( " 16.05.2015 03:59 " , hours , true ) ;
2015-05-10 01:25:23 +02:00
testOpened ( " 16.05.2015 04:01 " , hours , false ) ;
testOpened ( " 17.05.2015 01:00 " , hours , true ) ;
testOpened ( " 17.05.2015 05:01 " , hours , false ) ;
2015-05-10 00:50:44 +02:00
2013-06-25 12:11:58 +02:00
// tests single month value
hours = parseOpenedHours ( " May: 07:00-19:00 " ) ;
System . out . println ( hours ) ;
testOpened ( " 05.05.2013 12:00 " , hours , true ) ;
testOpened ( " 05.05.2013 05:00 " , hours , false ) ;
testOpened ( " 05.05.2013 21:00 " , hours , false ) ;
testOpened ( " 05.01.2013 12:00 " , hours , false ) ;
testOpened ( " 05.01.2013 05:00 " , hours , false ) ;
// tests multi month value
2016-08-27 16:10:20 +02:00
hours = parseOpenedHours ( " Apr-Sep 8:00-22:00; Oct-Mar 10:00-18:00 " ) ;
2013-06-25 12:11:58 +02:00
System . out . println ( hours ) ;
testOpened ( " 05.03.2013 15:00 " , hours , true ) ;
testOpened ( " 05.03.2013 20:00 " , hours , false ) ;
testOpened ( " 05.05.2013 20:00 " , hours , true ) ;
testOpened ( " 05.05.2013 23:00 " , hours , false ) ;
testOpened ( " 05.10.2013 15:00 " , hours , true ) ;
testOpened ( " 05.10.2013 20:00 " , hours , false ) ;
2015-12-02 16:48:33 +01:00
// Test time with breaks
hours = parseOpenedHours ( " Mo-Fr: 9:00-13:00, 14:00-18:00 " ) ;
System . out . println ( hours ) ;
testOpened ( " 02.12.2015 12:00 " , hours , true ) ;
testOpened ( " 02.12.2015 13:30 " , hours , false ) ;
testOpened ( " 02.12.2015 16:00 " , hours , true ) ;
testOpened ( " 05.12.2015 16:00 " , hours , false ) ;
2016-08-27 16:10:20 +02:00
hours = parseOpenedHours ( " Mo-Su 07:00-23:00; Dec 25 08:00-20:00 " ) ;
System . out . println ( hours ) ;
testOpened ( " 25.12.2015 07:00 " , hours , false ) ;
testOpened ( " 24.12.2015 07:00 " , hours , true ) ;
testOpened ( " 24.12.2015 22:00 " , hours , true ) ;
testOpened ( " 25.12.2015 08:00 " , hours , true ) ;
testOpened ( " 25.12.2015 22:00 " , hours , false ) ;
2016-08-27 16:15:44 +02:00
hours = parseOpenedHours ( " Mo-Su 07:00-23:00; Dec 25 off " ) ;
System . out . println ( hours ) ;
testOpened ( " 25.12.2015 14:00 " , hours , false ) ;
testOpened ( " 24.12.2015 08:00 " , hours , true ) ;
// easter itself as public holiday is not supported
hours = parseOpenedHours ( " Mo-Su 07:00-23:00; Easter off; Dec 25 off " ) ;
System . out . println ( hours ) ;
testOpened ( " 25.12.2015 14:00 " , hours , false ) ;
testOpened ( " 24.12.2015 08:00 " , hours , true ) ;
2016-08-27 16:10:20 +02:00
2017-05-09 00:22:49 +02:00
// test time off (not days
hours = parseOpenedHours ( " Mo-Fr 08:30-17:00; 12:00-12:40 off; " ) ;
System . out . println ( hours ) ;
testOpened ( " 07.05.2017 14:00 " , hours , false ) ; // Sunday
testOpened ( " 06.05.2017 12:15 " , hours , false ) ; // Saturday
testOpened ( " 05.05.2017 14:00 " , hours , true ) ; // Friday
testOpened ( " 05.05.2017 12:15 " , hours , false ) ;
testOpened ( " 05.05.2017 12:00 " , hours , false ) ;
testOpened ( " 05.05.2017 11:45 " , hours , true ) ;
2016-09-06 00:43:11 +02:00
2016-01-05 12:18:03 +01:00
// Test holidays
2016-04-05 15:00:10 +02:00
String hoursString = " mo-fr 11:00-21:00; PH off " ;
2016-01-05 12:18:03 +01:00
hours = parseOpenedHoursHandleErrors ( hoursString ) ;
testParsedAndAssembledCorrectly ( hoursString , hours ) ;
2016-09-16 10:35:33 +02:00
2018-01-22 12:40:43 +01:00
// test open from/till
hours = parseOpenedHours ( " Mo-Fr 08:30-17:00; 12:00-12:40 off; " ) ;
System . out . println ( hours ) ;
testInfo ( " 15.01.2018 09:00 " , hours , " Open till 12:00 " ) ;
testInfo ( " 15.01.2018 11:00 " , hours , " Will close at 12:00 " ) ;
testInfo ( " 15.01.2018 12:00 " , hours , " Will open at 12:40 " ) ;
hours = parseOpenedHours ( " Mo-Fr: 9:00-13:00, 14:00-18:00 " ) ;
System . out . println ( hours ) ;
testInfo ( " 15.01.2018 08:00 " , hours , " Will open at 09:00 " ) ;
testInfo ( " 15.01.2018 09:00 " , hours , " Open till 13:00 " ) ;
testInfo ( " 15.01.2018 12:00 " , hours , " Will close at 13:00 " ) ;
testInfo ( " 15.01.2018 13:10 " , hours , " Will open at 14:00 " ) ;
testInfo ( " 15.01.2018 14:00 " , hours , " Open till 18:00 " ) ;
testInfo ( " 15.01.2018 16:00 " , hours , " Will close at 18:00 " ) ;
2018-01-25 16:53:38 +01:00
testInfo ( " 15.01.2018 18:10 " , hours , " Will open tomorrow at 09:00 " ) ;
2017-05-09 00:22:49 +02:00
2018-01-22 12:40:43 +01:00
hours = parseOpenedHours ( " Mo-Sa 02:00-10:00; Th off " ) ;
System . out . println ( hours ) ;
2018-01-25 16:53:38 +01:00
testInfo ( " 15.01.2018 23:00 " , hours , " Will open tomorrow at 02:00 " ) ;
2018-01-22 12:40:43 +01:00
hours = parseOpenedHours ( " Mo-Sa 23:00-02:00; Th off " ) ;
System . out . println ( hours ) ;
testInfo ( " 15.01.2018 22:00 " , hours , " Will open at 23:00 " ) ;
testInfo ( " 15.01.2018 23:00 " , hours , " Open till 02:00 " ) ;
testInfo ( " 16.01.2018 00:30 " , hours , " Will close at 02:00 " ) ;
testInfo ( " 16.01.2018 02:00 " , hours , " Open from 23:00 " ) ;
hours = parseOpenedHours ( " Mo-Sa 08:30-17:00; Th off " ) ;
System . out . println ( hours ) ;
2018-01-25 16:53:38 +01:00
testInfo ( " 17.01.2018 20:00 " , hours , " Will open on 08:30 Fr. " ) ;
testInfo ( " 18.01.2018 05:00 " , hours , " Will open tomorrow at 08:30 " ) ;
2018-01-22 12:40:43 +01:00
testInfo ( " 20.01.2018 05:00 " , hours , " Open from 08:30 " ) ;
2018-01-25 16:53:38 +01:00
testInfo ( " 21.01.2018 05:00 " , hours , " Will open tomorrow at 08:30 " ) ;
2018-01-22 12:40:43 +01:00
testInfo ( " 22.01.2018 02:00 " , hours , " Open from 08:30 " ) ;
testInfo ( " 22.01.2018 04:00 " , hours , " Open from 08:30 " ) ;
testInfo ( " 22.01.2018 07:00 " , hours , " Will open at 08:30 " ) ;
testInfo ( " 23.01.2018 10:00 " , hours , " Open till 17:00 " ) ;
testInfo ( " 23.01.2018 16:00 " , hours , " Will close at 17:00 " ) ;
hours = parseOpenedHours ( " 24/7 " ) ;
System . out . println ( hours ) ;
testInfo ( " 24.01.2018 02:00 " , hours , " Open 24/7 " ) ;
2018-01-22 16:32:32 +01:00
hours = parseOpenedHours ( " Mo-Su 07:00-23:00, Fr 08:00-20:00 " ) ;
System . out . println ( hours ) ;
testOpened ( " 15.01.2018 06:45 " , hours , false ) ;
testOpened ( " 15.01.2018 07:45 " , hours , true ) ;
testOpened ( " 15.01.2018 23:45 " , hours , false ) ;
testOpened ( " 19.01.2018 07:45 " , hours , false ) ;
testOpened ( " 19.01.2018 08:45 " , hours , true ) ;
testOpened ( " 19.01.2018 20:45 " , hours , false ) ;
2018-01-25 16:53:38 +01:00
// test fallback case
hours = parseOpenedHours ( " 07:00-01:00 open \" Restaurant \" || Mo 00:00-04:00,07:00-04:00; Tu-Th 07:00-04:00; Fr 07:00-24:00; Sa,Su 00:00-24:00 open \" McDrive \" " ) ;
System . out . println ( hours ) ;
testOpened ( " 22.01.2018 00:30 " , hours , true ) ;
testOpened ( " 22.01.2018 08:00 " , hours , true ) ;
testOpened ( " 22.01.2018 03:30 " , hours , true ) ;
testOpened ( " 22.01.2018 05:00 " , hours , false ) ;
testOpened ( " 23.01.2018 05:00 " , hours , false ) ;
testOpened ( " 27.01.2018 05:00 " , hours , true ) ;
testOpened ( " 28.01.2018 05:00 " , hours , true ) ;
2018-02-17 14:33:03 +01:00
testInfo ( " 22.01.2018 05:00 " , hours , " Will open at 07:00 - Restaurant " , 0 ) ;
testInfo ( " 26.01.2018 00:00 " , hours , " Will close at 01:00 - Restaurant " , 0 ) ;
testInfo ( " 22.01.2018 05:00 " , hours , " Will open at 07:00 - McDrive " , 1 ) ;
testInfo ( " 22.01.2018 00:00 " , hours , " Open till 04:00 - McDrive " , 1 ) ;
testInfo ( " 22.01.2018 02:00 " , hours , " Will close at 04:00 - McDrive " , 1 ) ;
testInfo ( " 27.01.2018 02:00 " , hours , " Open till 24:00 - McDrive " , 1 ) ;
2018-01-25 16:53:38 +01:00
hours = parseOpenedHours ( " 07:00-03:00 open \" Restaurant \" || 24/7 open \" McDrive \" " ) ;
System . out . println ( hours ) ;
testOpened ( " 22.01.2018 02:00 " , hours , true ) ;
testOpened ( " 22.01.2018 17:00 " , hours , true ) ;
2018-02-17 14:33:03 +01:00
testInfo ( " 22.01.2018 05:00 " , hours , " Will open at 07:00 - Restaurant " , 0 ) ;
testInfo ( " 22.01.2018 04:00 " , hours , " Open 24/7 - McDrive " , 1 ) ;
2018-01-25 16:53:38 +01:00
2018-02-17 14:33:03 +01:00
hours = parseOpenedHours ( " Mo-Fr 12:00-15:00, Tu-Fr 17:00-23:00, Sa 12:00-23:00, Su 14:00-23:00 " ) ;
System . out . println ( hours ) ;
testOpened ( " 16.02.2018 14:00 " , hours , true ) ;
testOpened ( " 16.02.2018 16:00 " , hours , false ) ;
testOpened ( " 16.02.2018 17:00 " , hours , true ) ;
testInfo ( " 16.02.2018 9:45 " , hours , " Open from 12:00 " ) ;
testInfo ( " 16.02.2018 12:00 " , hours , " Open till 15:00 " ) ;
testInfo ( " 16.02.2018 14:00 " , hours , " Will close at 15:00 " ) ;
testInfo ( " 16.02.2018 16:00 " , hours , " Will open at 17:00 " ) ;
testInfo ( " 16.02.2018 18:00 " , hours , " Open till 23:00 " ) ;
2013-04-18 23:35:02 +02:00
}
}