Skip to content

Commit

Permalink
[WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
vkhrystiuk-ks committed Dec 6, 2024
1 parent 6efbeb6 commit 47f5f06
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 20 deletions.
84 changes: 67 additions & 17 deletions src/main/java/liqp/filters/date/FuzzyDateParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public class FuzzyDateParser extends BasicDateParser {

@Override
public ZonedDateTime parse(String valAsString, Locale locale, ZoneId defaultZone) {
if (locale == null) {
locale = Locale.ROOT;
}
if (defaultZone == null) {
defaultZone = ZoneId.systemDefault();
}
String normalized = valAsString.toLowerCase();
ZonedDateTime zonedDateTime = parseUsingCachedPatterns(normalized, locale, defaultZone);
if (zonedDateTime != null) {
Expand Down Expand Up @@ -66,6 +72,20 @@ String guessPattern(String normalized, Locale locale) {

private List<Part> parsePart(List<Part> parts, DateParseContext ctx) {

if (notSet(ctx.weekDay)) {
LookupResult result = lookup(parts, fullWeekdaysExtractor(ctx.locale));
if (result.found) {
ctx.weekDay = true;
return result.parts;
}
result = lookup(parts, shortWeekdaysExtractor(ctx.locale));
if (result.found) {
ctx.weekDay = true;
return result.parts;
}
ctx.weekDay = false;
}

if (notSet(ctx.hasYear)) {
LookupResult result = lookup(parts, yearWithEraExtractor);
if (result.found) {
Expand Down Expand Up @@ -127,9 +147,13 @@ static class DateParseContext {
private final Locale locale;
Boolean hasYear;
Boolean hasMonthName;
Boolean weekDay;
Boolean hasTime;

public DateParseContext(Locale locale) {
if (locale == null) {
locale = Locale.ROOT;
}
this.locale = locale;
}
}
Expand Down Expand Up @@ -213,43 +237,52 @@ public PartExtractorResult extract(String source) {
return delegate.extract(source);
}
}
static class FullMonthExtractor extends PartExtractorDelegate {
public FullMonthExtractor(Locale locale, String formatterPattern) {
static abstract class EnumExtractor extends PartExtractorDelegate {
public EnumExtractor(Locale locale, String formatterPattern) {
if (locale == null || Locale.ROOT.equals(locale)) {
locale = Locale.US;
}
String[] months = withoutNulls(getMonthsNamesFromLocale(locale));
String monthPattern = String.join("|", months);
super.delegate = new RegexPartExtractor(".*\\b?(" + monthPattern + ")\\b?.*", formatterPattern);
String[] values = withoutNulls(getEnumValues(locale));
String valuesPattern = String.join("|", values);
super.delegate = new RegexPartExtractor(".*\\b?(" + valuesPattern + ")\\b?.*", formatterPattern);
}

protected String[] getMonthsNamesFromLocale(Locale locale) {
return new DateFormatSymbols(locale).getMonths();
}
abstract protected String[] getEnumValues(Locale locale);

protected String[] withoutNulls(String[] shortMonths) {
return Arrays.stream(shortMonths)
.filter(month -> month != null && !month.isEmpty())
protected String[] withoutNulls(String[] enumValues) {
return Arrays.stream(enumValues)
.filter(val -> val != null && !val.isEmpty())
.map(Pattern::quote)
.map(this::convertMonthName)
.map(this::convertValName)
.toArray(String[]::new);
}
protected String convertMonthName(String monthName) {
return monthName;
protected String convertValName(String val) {
return val;
}
}

static class FullMonthExtractor extends EnumExtractor {
public FullMonthExtractor(Locale locale) {
super(locale, "MMMM");
}

@Override
protected String[] getEnumValues(Locale locale) {
return new DateFormatSymbols(locale).getMonths();
}
}

private PartExtractor fullMonthExtractor(Locale locale) {
return new FullMonthExtractor(locale, "MMMM");
return new FullMonthExtractor(locale);
}

static class ShortMonthExtractor extends FullMonthExtractor {
static class ShortMonthExtractor extends EnumExtractor {
public ShortMonthExtractor(Locale locale) {
super(locale, "MMM");
}

@Override
protected String[] getMonthsNamesFromLocale(Locale locale) {
protected String[] getEnumValues(Locale locale) {
return new DateFormatSymbols(locale).getShortMonths();
}
}
Expand Down Expand Up @@ -329,6 +362,23 @@ public PartExtractorResult extract(String source) {

static PartExtractor regularTimeExtractor = new RegularTimeExtractor();

PartExtractor shortWeekdaysExtractor(Locale locale) {
return new EnumExtractor(locale, "EEE") {
@Override
protected String[] getEnumValues(Locale locale) {
return new DateFormatSymbols(locale).getShortWeekdays();
}
};
}
PartExtractor fullWeekdaysExtractor(Locale locale) {
return new EnumExtractor(locale, "EEEE") {
@Override
protected String[] getEnumValues(Locale locale) {
return new DateFormatSymbols(locale).getWeekdays();
}
};
}

static class LookupResult {
final List<Part> parts;
final boolean found;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,32 @@ public static Collection<Object[]> data() {
{null, " 12 Anno Domini ", " yyyy GGGG "},
{null, " 12345 Before Christ ", " yyyy GGGG "},
{null, " 0 BC ", " yyyy GG "},
{null, "12 January", "12 MMMM"},
{null, " 12 January ", " 12 MMMM "},
{null, "12 Jan", "12 MMM"},
{null, " 12 Jan ", " 12 MMM "},

{null, " 12 BC 12 Jan 01:23:45.678 ", " yyyy GG 12 MMM HH:mm:ss.SSS "},
{null, "12 Jan 01:23:45.678 12 Anno Domini", "12 MMM HH:mm:ss.SSS yyyy GGGG"},
{null, "Monday", "EEEE"},
{null, " Monday ", " EEEE "},
{null, "Monday ", "EEEE "},
{null, " Monday", " EEEE"},
{null, "Mon", "EEE"},
{null, " Mon ", " EEE "},
{null, " Mon", " EEE"},
{null, "Mon ", "EEE "},
{Locale.GERMAN, "Montag", "EEEE"},
{Locale.GERMAN, " Montag ", " EEEE "},
{Locale.GERMAN, "Montag ", "EEEE "},
{Locale.GERMAN, " Montag", " EEEE"},
FuzzyDateParser.CLDR_LOADED ?
new Object[]{
Locale.GERMAN, "Mo.", "EEE"}
: new Object[]{
Locale.GERMAN, "Mo", "EEE"}
,
{null, "Monday 17th September 1999 BC at 12:34:56.000 AM", "EEEE 17th MMMM yyyy GG at h:mm:ss.SSS a"},
});
}

Expand Down
33 changes: 30 additions & 3 deletions src/test/java/liqp/filters/date/FuzzyDateParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Callable;
import liqp.filters.date.FuzzyDateParser.PartExtractor;
import liqp.filters.date.FuzzyDateParser.PartExtractorResult;
import org.junit.Test;
Expand All @@ -17,6 +18,32 @@ public void testTimeRegexp() {
assertTrue(result.found);
assertEquals( 1, result.start);
assertEquals( 6, result.end);
assertEquals(result.formatterPattern, "HH:mm");
assertEquals("HH:mm", result.formatterPattern);
}

@Test
public void testSelfCorrectLowToBig() {
unchecked(() -> Class.forName(FuzzyDateParser.class.getName()));
final FuzzyDateParser parser = new FuzzyDateParser();
ZonedDateTime parsed = parser.parse("1:00 am", null, null);
assertEquals("01:00:00", parsed.format(DateTimeFormatter.ISO_LOCAL_TIME));
parsed = parser.parse("01:00 am", null, null);
assertEquals("01:00:00", parsed.format(DateTimeFormatter.ISO_LOCAL_TIME));
}

@Test
public void testSelfCorrectBigToLow() {
unchecked(() -> Class.forName(FuzzyDateParser.class.getName()));
final FuzzyDateParser parser = new FuzzyDateParser();
ZonedDateTime parsed = parser.parse("01:00 am", null, null);
assertEquals("01:00:00", parsed.format(DateTimeFormatter.ISO_LOCAL_TIME));
parsed = parser.parse("1:00 am", null, null);
assertEquals("01:00:00", parsed.format(DateTimeFormatter.ISO_LOCAL_TIME));
}

static void unchecked(@SuppressWarnings("rawtypes") Callable f) {
try {
f.call();
} catch (Exception ignored) { }
}
}

0 comments on commit 47f5f06

Please sign in to comment.