Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support four digit times in non-strict mode #63

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
This is a very simplistic parser for string values according to the [OSM opening hours specification][opening-hours-specification]. It is used in a number of OpenStreetMap projects, for example
in [Vespucci](https://github.com/MarcusWolschon/osmeditor4android). As the opening hours specification is currently reasonably stable you shouldn't expect lots of activity in this repository.

It parses 147'189 (91%) of 161'268 unique test strings in non-strict mode. The remaining 14'079 are likely valid errors, spot checking shows that they have obvious issues. In strict mode a further 15'993 fail (total 30'072).
It parses 147'628 (92%) of 161'268 unique test strings in non-strict mode. The remaining 13'640 are likely valid errors, spot checking shows that they have obvious issues. In strict mode a further 16'432 fail (total 30'072).

Deviations from the grammar as of [this version of the opening hours specification][opening-hours-grammar-specification] in all modes:

Expand All @@ -24,6 +24,7 @@ In non-strict mode the following further differences are allowed:
* ignore spaces and more than one leading zeros in minutes
* "." and "h" as minutes separators
* AM and PM time specifications are allowed (plus A.M. and P.M.)
* 4 digit times in some circumstances
* holidays in weekday range
* superfluous ":" after weekday range
* 24/7 rules with preceding selectors are corrected to 00:00-24:00 time spans
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ updateTranslations.description = 'Update translations by executing the transifex
task showDifferences(type: JavaExec) {
main = "ch.poole.openinghoursparser.Diff"
classpath = sourceSets.test.runtimeClasspath
args('test-data/oh.txt-result', 'test-data/oh.txt-result-temp', 'test-data/oh.txt', 'test-data/diff.txt')
args('test-data/oh.txt-result.ocs', 'test-data/oh.txt-result-temp', 'test-data/oh.txt', 'test-data/diff.txt')
}
showDifferences.group = 'verification'
showDifferences.description = "Extract differences between reference test results and current results"

task showDifferencesStrict(type: JavaExec) {
main = "ch.poole.openinghoursparser.Diff"
classpath = sourceSets.test.runtimeClasspath
args('test-data/oh.txt-result-strict', 'test-data/oh.txt-result-strict-temp', 'test-data/oh.txt', 'test-data/diff-strict.txt')
args('test-data/oh.txt-result-strict.ocs', 'test-data/oh.txt-result-strict-temp', 'test-data/oh.txt', 'test-data/diff-strict.txt')
}
showDifferencesStrict.group = 'verification'
showDifferencesStrict.description = "Extract differences between reference test results and current results in strict mode"
Expand Down
71 changes: 46 additions & 25 deletions src/main/java/ch/poole/openinghoursparser/OpeningHoursParser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Locale;
import java.io.ByteArrayInputStream;
import java.io.IOException;

import ch.poole.openinghoursparser.Util;

import static ch.poole.openinghoursparser.I18n.tr;

Expand Down Expand Up @@ -390,30 +386,42 @@ int time() :
{
Token h = null;
Token m = null;
Token t = null;
int result = 0;
AMPM ampm = null;
}
{
(
h = hours()
// 4 digit times: years have to be >= 1900, so we can avoid conflicts by only fixing up things that are smaller than that LOOKAHEAD({ getToken(1).image.length() == 4 && getToken(1).kind == NUMBER && Util.between(getToken(1).image, 0, 1859) && Util.between(getToken(1).image.substring(2), 0, 59)})
t = < NUMBER >
|
(
(
h = hours()
)
(
LOOKAHEAD(minutes(), { h!=null })
m = minutes()
)?
)
)
(
LOOKAHEAD(minutes(), { h!=null })
m = minutes()
)?
(
ampm = ampm()
)?
{
if (strict && m == null) {
ParseException pex = new OpeningHoursParseException(tr("hours_without_minutes"));
if (strict && (t != null || m == null)) {
ParseException pex = new OpeningHoursParseException(tr(t == null ? "hours_without_minutes" : "four_digit_time_value"));
pex.currentToken = token;
throw pex;
}
if (m != null) {
result = Integer.parseInt(m.image);
if (t != null) {
result = Integer.parseInt(t.image.substring(2)) + Integer.parseInt(t.image.substring(0,2)) * 60;
} else {
if (m != null) {
result = Integer.parseInt(m.image);
}
result = result + Integer.parseInt(h.image) * 60;
}
result = result + Integer.parseInt(h.image) * 60;
if (ampm == AMPM.PM && result < TWELVEHOURS) { // only use add 12h if the time is less than 12 h
result = result + TWELVEHOURS; // 12:01 pm to 12:59 pm are already correct 24h times
} else if (ampm == AMPM.AM && result >= TWELVEHOURS && result < TWELVEHOURS + 60) {
Expand Down Expand Up @@ -458,31 +466,44 @@ int extendedtime() :
{
Token h = null;
Token m = null;
Token t = null;
int result = 0;
AMPM ampm = null;
}
{
(
LOOKAHEAD({ getToken(1).kind == NUMBER && Util.between(getToken(1).image, 0, 48) })
h = < NUMBER >
// 4 digit time value
LOOKAHEAD({ getToken(1).image.length() == 4 && getToken(1).kind == NUMBER && Util.between(getToken(1).image, 0, 4800) && Util.between(getToken(1).image.substring(2), 0, 59)})
t = < NUMBER >
|
(
(
LOOKAHEAD({ getToken(1).kind == NUMBER && Util.between(getToken(1).image, 0, 48) })
h = < NUMBER >
)
(
LOOKAHEAD(minutes(), { h!=null })
m = minutes()
)?
)
)
(
LOOKAHEAD(minutes(), { h!=null })
m = minutes()
)?
(
ampm = ampm()
)?
{
if (strict && m == null) {
ParseException pex = new OpeningHoursParseException(tr("hours_without_minutes"));
if (strict && (t != null || m == null)) {
ParseException pex = new OpeningHoursParseException(tr(t == null ? "hours_without_minutes" : "four_digit_time_value"));
pex.currentToken = token;
throw pex;
}
if (m != null) {
result = Integer.parseInt(m.image);
if (t != null) {
result = Integer.parseInt(t.image.substring(2)) + Integer.parseInt(t.image.substring(0,2)) * 60;
} else {
if (m != null) {
result = Integer.parseInt(m.image);
}
result = result + Integer.parseInt(h.image) * 60;
}
result = result + Integer.parseInt(h.image) * 60;
if (ampm == AMPM.PM && result < TWELVEHOURS) {
result = result + TWELVEHOURS;
} else if (ampm == AMPM.AM && result >= TWELVEHOURS && result < TWELVEHOURS + 60) {
Expand Down
57 changes: 29 additions & 28 deletions src/main/resources/ch/poole/openinghoursparser/Messages.properties
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
24_7_with_other_selectors = 24/7 with other selectors
earlier_than = {0} is earlier than {1}
earlier_than_time = End time earlier than start time
exception_line_column = {0} at line {1}, column {2}
exception_encountered = Encountered: {0}
exception_expecting = Was expecting: {0}
found = {0} found
holiday_in_weekday_range = Holiday in weekday range
hours_without_minutes = Hours without minutes
interval_not_allowed_here = Interval not allowed here
invalid_event = {0} is not a valid event
invalid_minutes = Invalid minutes
invalid_modifier = {0} is not a valid modifier
invalid_month = {0} is not a valid month
invalid_month_day = {0} is not a valid month day number
invalid_occurrence = {0} is not a valid occurrence number
invalid_time = {0} is not a valid time
invalid_variable_date = {0} is not a valid variable date
invalid_week_day = {0} is not a valid week day
invalid_week_day_three = Three character weekday
invalid_week_day_german = German weekday abbreviation
invalid_week_number = {0} is outside of the 1-53 range
invalid_year_number = {0} is earlier than 1900
missing_day_for_February = Missing month day in date range for February
missing_day_in_range = Missing month day in date range {0} {1}
missing_month = Missing month
missing_month_start = Missing start month
to_instead_of_dash = `to` instead of `-`
24_7_with_other_selectors = 24/7 with other selectors
earlier_than = {0} is earlier than {1}
earlier_than_time = End time earlier than start time
exception_line_column = {0} at line {1}, column {2}
exception_encountered = Encountered: {0}
exception_expecting = Was expecting: {0}
found = {0} found
holiday_in_weekday_range = Holiday in weekday range
hours_without_minutes = Hours without minutes
four_digit_time_value = Four digit time value
interval_not_allowed_here = Interval not allowed here
invalid_event = {0} is not a valid event
invalid_minutes = Invalid minutes
invalid_modifier = {0} is not a valid modifier
invalid_month = {0} is not a valid month
invalid_month_day = {0} is not a valid month day number
invalid_occurrence = {0} is not a valid occurrence number
invalid_time = {0} is not a valid time
invalid_variable_date = {0} is not a valid variable date
invalid_week_day = {0} is not a valid week day
invalid_week_day_three = Three character weekday
invalid_week_day_german = German weekday abbreviation
invalid_week_number = {0} is outside of the 1-53 range
invalid_year_number = {0} is earlier than 1900
missing_day_for_February = Missing month day in date range for February
missing_day_in_range = Missing month day in date range {0} {1}
missing_month = Missing month
missing_month_start = Missing start month
to_instead_of_dash = `to` instead of `-`
37 changes: 31 additions & 6 deletions src/test/java/ch/poole/openinghoursparser/UnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,31 @@ public void timeTest() {
} catch (ParseException pex) {
assertEquals("Hours without minutes", pex.getMessage());
}
parser = new OpeningHoursParser(new ByteArrayInputStream("0400-1700".getBytes()));
try {
assertEquals("04:00-17:00", Util.rulesToOpeningHoursString(parser.rules(false)));
} catch (ParseException pex) {
fail(pex.getMessage());
}
parser = new OpeningHoursParser(new ByteArrayInputStream("0400-1700".getBytes()));
try {
assertEquals("04:00-17:00", Util.rulesToOpeningHoursString(parser.rules(true)));
fail("Should throw an exception");
} catch (ParseException pex) {
assertEquals("Four digit time value", pex.getMessage());
}
// this should not parse as a time
parser = new OpeningHoursParser(new ByteArrayInputStream("1900-1930".getBytes()));
try {
List<Rule> result = parser.rules(false);
assertEquals(1, result.size());
assertEquals(1, result.get(0).getYears().size());
YearRange yearRange = result.get(0).getYears().get(0);
assertEquals(1900, yearRange.getStartYear());
assertEquals(1930, yearRange.getEndYear());
} catch (ParseException pex) {
fail(pex.getMessage());
}
}

@Test
Expand Down Expand Up @@ -642,45 +667,45 @@ public void dateRangeWithOccurance() {
fail(pex.getMessage());
}
}

@Test
public void intervalMinutesOnly() {
OpeningHoursParser parser = new OpeningHoursParser(new ByteArrayInputStream("07:00-20:00/99".getBytes()));
try {
List<Rule> rules = parser.rules(true);
assertEquals(1, rules.size());
Rule r = rules.get(0);
assertEquals(1,r.times.size());
assertEquals(1, r.times.size());
TimeSpan ts = r.times.get(0);
assertEquals(99, ts.getInterval());
} catch (ParseException pex) {
fail(pex.getMessage());
}
}

@Test
public void intervalHourMinutes() {
OpeningHoursParser parser = new OpeningHoursParser(new ByteArrayInputStream("07:00-20:00/01:39".getBytes()));
try {
List<Rule> rules = parser.rules(true);
assertEquals(1, rules.size());
Rule r = rules.get(0);
assertEquals(1,r.times.size());
assertEquals(1, r.times.size());
TimeSpan ts = r.times.get(0);
assertEquals(99, ts.getInterval());
} catch (ParseException pex) {
fail(pex.getMessage());
}
}

@Test
public void intervalHourMinutesNoLeading0() {
OpeningHoursParser parser = new OpeningHoursParser(new ByteArrayInputStream("07:00-20:00/1:39".getBytes()));
try {
List<Rule> rules = parser.rules(true);
assertEquals(1, rules.size());
Rule r = rules.get(0);
assertEquals(1,r.times.size());
assertEquals(1, r.times.size());
TimeSpan ts = r.times.get(0);
assertEquals(99, ts.getInterval());
} catch (ParseException pex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exception_expecting = Erwartet wurde: {0}
found = {0} gefunden
holiday_in_weekday_range = Feiertag in Wochentagbereich
hours_without_minutes = Stunden ohne Minuten
four_digit_time_value = Vierstelliger Zeitwert
interval_not_allowed_here = Intervalle sind hier nicht erlaubt
invalid_event = {0} ist kein g�ltiger Ereigniswert
invalid_minutes = Ung�ltiger Minutenwert
Expand Down
Loading