Added year to opening hours parser

This commit is contained in:
crimean 2019-07-25 13:44:23 +03:00
parent 678f9f7bc0
commit 6282cf520f
2 changed files with 226 additions and 30 deletions

View file

@ -593,7 +593,15 @@ public class OpeningHoursParser {
* @return true if the month is part of the rule * @return true if the month is part of the rule
*/ */
public boolean containsMonth(Calendar cal); public boolean containsMonth(Calendar cal);
/**
* Check if the year of "cal" is part of this rule
*
* @param cal the time to check
* @return true if the year is part of the rule
*/
public boolean containsYear(Calendar cal);
/** /**
* @return true if the rule overlap to the next day * @return true if the rule overlap to the next day
*/ */
@ -643,7 +651,15 @@ public class OpeningHoursParser {
* Day number 0 is JANUARY. * Day number 0 is JANUARY.
*/ */
private boolean[] months = new boolean[12]; private boolean[] months = new boolean[12];
/**
* represents the list on which year / month it is open.
*/
private int[] firstYearMonths = null;
private int[] lastYearMonths = null;
private int fullYears = 0;
private int year = 0;
/** /**
* represents the list on which day it is open. * represents the list on which day it is open.
*/ */
@ -713,7 +729,7 @@ public class OpeningHoursParser {
public boolean[] getMonths() { public boolean[] getMonths() {
return months; return months;
} }
public boolean appliesToPublicHolidays() { public boolean appliesToPublicHolidays() {
return publicHoliday; return publicHoliday;
} }
@ -908,10 +924,29 @@ public class OpeningHoursParser {
*/ */
@Override @Override
public boolean containsMonth(Calendar cal) { public boolean containsMonth(Calendar cal) {
int i = cal.get(Calendar.MONTH); int month = cal.get(Calendar.MONTH);
if (months[i]) { int year = cal.get(Calendar.YEAR);
return containsYear(cal) && months[month];
}
public boolean containsYear(Calendar cal) {
if (year == 0 && firstYearMonths == null) {
return true; return true;
} }
int month = cal.get(Calendar.MONTH);
int year = cal.get(Calendar.YEAR);
if (firstYearMonths != null && firstYearMonths[month] == year ||
lastYearMonths != null && lastYearMonths[month] == year ||
firstYearMonths == null && lastYearMonths == null && this.year == year) {
return true;
}
if (fullYears > 0 && this.year > 0) {
for (int i = 1; i <= fullYears; i++) {
if (this.year + i == year) {
return true;
}
}
}
return false; return false;
} }
@ -994,6 +1029,7 @@ public class OpeningHoursParser {
boolean dash = false; boolean dash = false;
boolean first = true; boolean first = true;
int monthAdded = -1; int monthAdded = -1;
int dayAdded = -1;
int excludedMonthEnd = -1; int excludedMonthEnd = -1;
int excludedDayEnd = -1; int excludedDayEnd = -1;
int excludedMonthStart = -1; int excludedMonthStart = -1;
@ -1036,6 +1072,7 @@ public class OpeningHoursParser {
} }
} }
} }
boolean yearAdded = false;
for (int month = 0; month < dayMonths.length; month++) { for (int month = 0; month < dayMonths.length; month++) {
for (int day = 0; day < dayMonths[month].length; day++) { for (int day = 0; day < dayMonths[month].length; day++) {
if (excludedDayStart != -1 && excludedDayEnd != -1) { if (excludedDayStart != -1 && excludedDayEnd != -1) {
@ -1046,7 +1083,7 @@ public class OpeningHoursParser {
} }
} }
if (dayMonths[month][day]) { if (dayMonths[month][day]) {
if (day == 0 && dash) { if (day == 0 && dash && dayMonths[month][1]) {
continue; continue;
} }
if (day > 0 && dayMonths[month][day - 1] if (day > 0 && dayMonths[month][day - 1]
@ -1065,11 +1102,13 @@ public class OpeningHoursParser {
b.append(", "); b.append(", ");
monthAdded = -1; monthAdded = -1;
} }
if (monthAdded != month) { yearAdded = appendYearString(b, dash ? lastYearMonths : firstYearMonths, month);
if (monthAdded != month || yearAdded) {
b.append(monthNames[month]).append(" "); b.append(monthNames[month]).append(" ");
monthAdded = month; monthAdded = month;
} }
b.append(day + 1); dayAdded = day + 1;
b.append(dayAdded);
dash = false; dash = false;
} }
} }
@ -1080,9 +1119,18 @@ public class OpeningHoursParser {
} else if (!dash) { } else if (!dash) {
b.append(", "); b.append(", ");
} }
appendYearString(b, firstYearMonths, excludedMonthStart);
b.append(monthNames[excludedMonthStart]).append(" ").append(excludedDayStart + 1) b.append(monthNames[excludedMonthStart]).append(" ").append(excludedDayStart + 1)
.append("-") .append("-");
.append(monthNames[excludedMonthEnd]).append(" ").append(excludedDayEnd + 1); appendYearString(b, lastYearMonths, excludedMonthEnd);
b.append(monthNames[excludedMonthEnd]).append(" ").append(excludedDayEnd + 1);
} else if (yearAdded && !dash && monthAdded != -1 && lastYearMonths != null) {
b.append("-");
appendYearString(b, lastYearMonths, monthAdded);
b.append(monthNames[monthAdded]);
if (dayAdded != -1) {
b.append(" ").append(dayAdded);
}
} }
if (!first) { if (!first) {
b.append(" "); b.append(" ");
@ -1139,6 +1187,17 @@ public class OpeningHoursParser {
return b.toString(); return b.toString();
} }
private boolean appendYearString(StringBuilder b, int[] yearMonths, int month) {
if (yearMonths != null && yearMonths[month] > 0) {
b.append(yearMonths[month]).append(" ");
return true;
} else if (year > 0) {
b.append(year).append(" ");
return true;
}
return false;
}
private void addArray(boolean[] array, String[] arrayNames, StringBuilder b) { private void addArray(boolean[] array, String[] arrayNames, StringBuilder b) {
boolean dash = false; boolean dash = false;
boolean first = true; boolean first = true;
@ -1405,7 +1464,7 @@ public class OpeningHoursParser {
private int calculate(Calendar cal) { private int calculate(Calendar cal) {
int month = cal.get(Calendar.MONTH); int month = cal.get(Calendar.MONTH);
if (!months[month]) { if (!containsMonth(cal)) {
return 0; return 0;
} }
int dmonth = cal.get(Calendar.DAY_OF_MONTH) - 1; int dmonth = cal.get(Calendar.DAY_OF_MONTH) - 1;
@ -1500,6 +1559,11 @@ public class OpeningHoursParser {
return false; return false;
} }
@Override
public boolean containsYear(Calendar cal) {
return false;
}
@Override @Override
public String toRuleString() { public String toRuleString() {
return ruleString; return ruleString;
@ -1547,12 +1611,14 @@ public class OpeningHoursParser {
TOKEN_COMMA(2), TOKEN_COMMA(2),
TOKEN_DASH(3), TOKEN_DASH(3),
// order is important // order is important
TOKEN_MONTH(4), TOKEN_YEAR(4),
TOKEN_DAY_MONTH(5), TOKEN_MONTH(5),
TOKEN_HOLIDAY(6), TOKEN_DAY_MONTH(6),
TOKEN_DAY_WEEK(6), TOKEN_HOLIDAY(7),
TOKEN_HOUR_MINUTES (7), TOKEN_DAY_WEEK(7),
TOKEN_OFF_ON(8); TOKEN_HOUR_MINUTES (8),
TOKEN_OFF_ON(9);
public final int ord; public final int ord;
private TokenType(int ord) { private TokenType(int ord) {
@ -1696,22 +1762,21 @@ public class OpeningHoursParser {
} }
} }
// recognize other numbers // recognize other numbers
// if there is no on/off and minutes/hours boolean monthSpecified = false;
boolean hoursSpecified = false; for (Token t : tokens) {
for(int i = 0; i < tokens.size(); i ++) { if (t.type == TokenType.TOKEN_MONTH) {
if(tokens.get(i).type == TokenType.TOKEN_HOUR_MINUTES || monthSpecified = true;
tokens.get(i).type == TokenType.TOKEN_OFF_ON) {
hoursSpecified = true;
break; break;
} }
} }
for(int i = 0; i < tokens.size(); i ++) { for(int i = 0; i < tokens.size(); i ++) {
if(tokens.get(i).type == TokenType.TOKEN_UNKNOWN && tokens.get(i).mainNumber >= 0) { Token t = tokens.get(i);
tokens.get(i).type = hoursSpecified ? TokenType.TOKEN_DAY_MONTH : TokenType.TOKEN_HOUR_MINUTES; if (t.type == TokenType.TOKEN_UNKNOWN && t.mainNumber >= 0) {
if(tokens.get(i).type == TokenType.TOKEN_HOUR_MINUTES) { if (monthSpecified && t.mainNumber <= 31) {
tokens.get(i).mainNumber = tokens.get(i).mainNumber * 60; t.type = TokenType.TOKEN_DAY_MONTH;
} else { t.mainNumber = t.mainNumber - 1;
tokens.get(i).mainNumber = tokens.get(i).mainNumber - 1; } else if (t.mainNumber > 1000) {
t.type = TokenType.TOKEN_YEAR;
} }
} }
} }
@ -1727,6 +1792,7 @@ public class OpeningHoursParser {
Token[] currentPair = new Token[2]; Token[] currentPair = new Token[2];
listOfPairs.add(currentPair); listOfPairs.add(currentPair);
Token prevToken = null; Token prevToken = null;
Token prevYearToken = null;
int indexP = 0; int indexP = 0;
for(int i = 0; i <= tokens.size(); i++) { for(int i = 0; i <= tokens.size(); i++) {
Token t = i == tokens.size() ? null : tokens.get(i); Token t = i == tokens.size() ? null : tokens.get(i);
@ -1782,6 +1848,40 @@ public class OpeningHoursParser {
} else if (array != null) { } else if (array != null) {
fillRuleArray(array, pair); fillRuleArray(array, pair);
} }
int ruleYear = basic.year;
if ((ruleYear > 0 || prevYearToken != null) && firstMonthToken != null && lastMonthToken != null) {
int length = lastMonthToken.mainNumber > firstMonthToken.mainNumber ?
lastMonthToken.mainNumber - firstMonthToken.mainNumber : 12 - firstMonthToken.mainNumber + lastMonthToken.mainNumber;
int month = firstMonthToken.mainNumber;
int endYear = prevYearToken != null ? prevYearToken.mainNumber : ruleYear;
int startYear = ruleYear > 0 ? ruleYear : endYear;
int year = startYear;
if (basic.firstYearMonths == null) {
basic.firstYearMonths = new int[12];
}
int[] yearMonths = basic.firstYearMonths;
int k = 0;
while (k <= length) {
yearMonths[month++] = year;
if (month > 11) {
month = 0;
year = endYear;
if (basic.lastYearMonths == null) {
basic.lastYearMonths = new int[12];
}
yearMonths = basic.lastYearMonths;
}
k++;
}
if (endYear - startYear > 1) {
basic.fullYears = endYear - startYear - 1;
}
if (endYear > startYear && firstMonthToken.mainNumber >= lastMonthToken.mainNumber) {
//basic.dayMonths = null;
Arrays.fill(basic.months, true);
}
}
} else if (pair[0] != null) { } else if (pair[0] != null) {
if (pair[0].type == TokenType.TOKEN_HOLIDAY) { if (pair[0].type == TokenType.TOKEN_HOLIDAY) {
if (pair[0].mainNumber == 0) { if (pair[0].mainNumber == 0) {
@ -1798,6 +1898,9 @@ public class OpeningHoursParser {
} }
if (array != null) { if (array != null) {
array[pair[0].mainNumber] = true; array[pair[0].mainNumber] = true;
if (prevYearToken != null) {
basic.year = prevYearToken.mainNumber;
}
} }
} }
} }
@ -1813,6 +1916,11 @@ public class OpeningHoursParser {
if (l[0] != null && l[0].mainNumber == 0) { if (l[0] != null && l[0].mainNumber == 0) {
basic.off = true; basic.off = true;
} }
} else if (currentParse == TokenType.TOKEN_YEAR) {
Token[] l = listOfPairs.get(0);
if (l[0] != null && l[0].mainNumber > 1000) {
prevYearToken = l[0];
}
} }
listOfPairs.clear(); listOfPairs.clear();
currentPair = new Token[2]; currentPair = new Token[2];
@ -1842,6 +1950,8 @@ public class OpeningHoursParser {
} }
} else if (t.type == TokenType.TOKEN_DASH) { } else if (t.type == TokenType.TOKEN_DASH) {
} else if (t.type == TokenType.TOKEN_YEAR) {
prevYearToken = t;
} else if (t.type.ord() == currentParse.ord()) { } else if (t.type.ord() == currentParse.ord()) {
if (indexP < 2) { if (indexP < 2) {
currentPair[indexP++] = t; currentPair[indexP++] = t;

View file

@ -116,7 +116,93 @@ public class OpeningHoursParserTest {
Locale locale = Locale.getDefault(); Locale locale = Locale.getDefault();
try { try {
Locale.setDefault(Locale.forLanguageTag("en-US")); Locale.setDefault(Locale.forLanguageTag("en-US"));
OpeningHours hours = parseOpenedHours("Apr 05-Oct 24: Fr 08:00-16:00");
OpeningHours hours = parseOpenedHours("2019 Apr 1 - 2020 Apr 1");
System.out.println(hours);
testOpened("01.04.2018 15:00", hours, false);
testOpened("01.04.2019 15:00", hours, true);
testOpened("01.04.2020 15:00", hours, true);
hours = parseOpenedHours("2019 Apr 15 - 2020 Mar 1");
System.out.println(hours);
testOpened("01.04.2018 15:00", hours, false);
testOpened("01.04.2019 15:00", hours, false);
testOpened("15.04.2019 15:00", hours, true);
testOpened("15.09.2019 15:00", hours, true);
testOpened("15.02.2020 15:00", hours, true);
testOpened("15.03.2020 15:00", hours, false);
testOpened("15.04.2020 15:00", hours, false);
hours = parseOpenedHours("2019 Jul 23 05:00-24:00; 2019 Jul 24-2019 Jul 26 00:00-24:00; 2019 Jul 27 00:00-18:00");
System.out.println(hours);
testOpened("23.07.2018 15:00", hours, false);
testOpened("23.07.2019 15:00", hours, true);
testOpened("23.07.2019 04:00", hours, false);
testOpened("23.07.2020 15:00", hours, false);
testOpened("25.07.2018 15:00", hours, false);
testOpened("24.07.2019 15:00", hours, true);
testOpened("25.07.2019 04:00", hours, true);
testOpened("26.07.2019 15:00", hours, true);
testOpened("25.07.2020 15:00", hours, false);
testOpened("27.07.2018 15:00", hours, false);
testOpened("27.07.2019 15:00", hours, true);
testOpened("27.07.2019 19:00", hours, false);
testOpened("27.07.2020 15:00", hours, false);
hours = parseOpenedHours("2019 Sep 1 - 2022 Apr 1");
System.out.println(hours);
testOpened("01.02.2018 15:00", hours, false);
testOpened("29.05.2019 15:00", hours, false);
testOpened("05.09.2019 11:00", hours, true);
testOpened("05.02.2020 11:00", hours, true);
testOpened("03.06.2020 11:00", hours, false);
testOpened("05.02.2021 11:00", hours, true);
testOpened("05.02.2022 11:00", hours, true);
testOpened("05.02.2023 11:00", hours, false);
hours = parseOpenedHours("2019 Apr 15 - 2019 Sep 1: Mo-Fr 00:00-24:00");
System.out.println(hours);
testOpened("06.04.2019 15:00", hours, false);
testOpened("29.05.2019 15:00", hours, true);
testOpened("25.07.2019 11:00", hours, true);
testOpened("12.07.2018 11:00", hours, false);
testOpened("18.07.2020 11:00", hours, false);
testOpened("28.07.2021 11:00", hours, false);
hours = parseOpenedHours("2019 Sep 1 - 2020 Apr 1");
System.out.println(hours);
testOpened("01.04.2019 15:00", hours, false);
testOpened("29.05.2019 15:00", hours, false);
testOpened("05.09.2019 11:00", hours, true);
testOpened("05.02.2020 11:00", hours, true);
testOpened("05.06.2020 11:00", hours, false);
testOpened("05.02.2021 11:00", hours, false);
hours = parseOpenedHours("2019 Apr 15 - 2019 Sep 1");
System.out.println(hours);
testOpened("01.04.2019 15:00", hours, false);
testOpened("29.05.2019 15:00", hours, true);
testOpened("27.07.2019 15:00", hours, true);
testOpened("05.09.2019 11:00", hours, false);
testOpened("05.06.2018 11:00", hours, false);
testOpened("05.06.2020 11:00", hours, false);
hours = parseOpenedHours("Apr 15 - Sep 1");
System.out.println(hours);
testOpened("01.04.2019 15:00", hours, false);
testOpened("29.05.2019 15:00", hours, true);
testOpened("27.07.2019 15:00", hours, true);
testOpened("05.09.2019 11:00", hours, false);
hours = parseOpenedHours("Apr 15 - Sep 1: Mo-Fr 00:00-24:00");
System.out.println(hours);
testOpened("01.04.2019 15:00", hours, false);
testOpened("29.05.2019 15:00", hours, true);
testOpened("24.07.2019 15:00", hours, true);
testOpened("27.07.2019 15:00", hours, false);
testOpened("05.09.2019 11:00", hours, false);
hours = parseOpenedHours("Apr 05-Oct 24: Fr 08:00-16:00");
System.out.println(hours); System.out.println(hours);
testOpened("26.08.2018 15:00", hours, false); testOpened("26.08.2018 15:00", hours, false);
testOpened("29.03.2019 15:00", hours, false); testOpened("29.03.2019 15:00", hours, false);