Skip to content

Commit

Permalink
Merge pull request #312 from bkiers/309-date-bug
Browse files Browse the repository at this point in the history
Add more date patterns and changed to a more lenient day and month parsing
  • Loading branch information
bkiers authored Nov 18, 2024
2 parents a578665 + 9d38d38 commit 4f2a07a
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 65 deletions.
183 changes: 118 additions & 65 deletions src/main/java/liqp/filters/date/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.util.Locale;

import static java.time.temporal.ChronoField.*;
import static java.time.temporal.ChronoField.INSTANT_SECONDS;

public class Parser {

Expand All @@ -29,79 +28,133 @@ public class Parser {
*/
public static List<String> datePatterns = new ArrayList<>();

// Since Liquid supports dates like `March 1st`, this list will
// hold strings that will be removed from the input string.
private static final String[] toBeRemoved = new String[] { "st", "nd", "rd", "th" };

static {

datePatterns.add("EEE MMM dd hh:mm:ss yyyy");
datePatterns.add("EEE MMM dd hh:mm yyyy");
datePatterns.add("yyyy-MM-dd");
datePatterns.add("dd-MM-yyyy");
datePatterns.add("EEE MMM d hh:mm:ss yyyy");
datePatterns.add("EEE MMM d hh:mm yyyy");
datePatterns.add("yyyy-M-d");
datePatterns.add("d-M-yyyy");
datePatterns.add("d-M-yy");
datePatterns.add("yy-M-d");

datePatterns.add("d/M/yyyy");
datePatterns.add("yyyy/M/d");
datePatterns.add("d/M/yy");
datePatterns.add("yy/M/d");
datePatterns.add("M/yyyy");
datePatterns.add("yyyy/M");
datePatterns.add("M/d");
datePatterns.add("d/M");

// this is section without `T`, change here and do same change in section below with `T`
datePatterns.add("yyyy-MM-dd HH:mm");
datePatterns.add("yyyy-MM-dd HH:mm X");
datePatterns.add("yyyy-MM-dd HH:mm Z");
datePatterns.add("yyyy-MM-dd HH:mm z");
datePatterns.add("yyyy-MM-dd HH:mm'Z'");

datePatterns.add("yyyy-MM-dd HH:mm:ss");
datePatterns.add("yyyy-MM-dd HH:mm:ss X");
datePatterns.add("yyyy-MM-dd HH:mm:ss Z");
datePatterns.add("yyyy-MM-dd HH:mm:ss z");
datePatterns.add("yyyy-MM-dd HH:mm:ss'Z'");

datePatterns.add("yyyy-MM-dd HH:mm:ss.SSS");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSS X");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSS Z");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSS z");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSS'Z'");

datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSS");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSS X");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSS Z");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSS z");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSS'Z'");

datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSSSSS X");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSSSSS Z");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSSSSS z");
datePatterns.add("yyyy-MM-dd HH:mm:ss.SSSSSSSSS'Z'");
datePatterns.add("yyyy-M-d HH:mm");
datePatterns.add("yyyy-M-d HH:mm X");
datePatterns.add("yyyy-M-d HH:mm Z");
datePatterns.add("yyyy-M-d HH:mm z");
datePatterns.add("yyyy-M-d HH:mm'Z'");

datePatterns.add("yyyy-M-d HH:mm:ss");
datePatterns.add("yyyy-M-d HH:mm:ss X");
datePatterns.add("yyyy-M-d HH:mm:ss Z");
datePatterns.add("yyyy-M-d HH:mm:ss z");
datePatterns.add("yyyy-M-d HH:mm:ss'Z'");

datePatterns.add("yyyy-M-d HH:mm:ss.SSS");
datePatterns.add("yyyy-M-d HH:mm:ss.SSS X");
datePatterns.add("yyyy-M-d HH:mm:ss.SSS Z");
datePatterns.add("yyyy-M-d HH:mm:ss.SSS z");
datePatterns.add("yyyy-M-d HH:mm:ss.SSS'Z'");

datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSS");
datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSS X");
datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSS Z");
datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSS z");
datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSS'Z'");

datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSSSSS");
datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSSSSS X");
datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSSSSS Z");
datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSSSSS z");
datePatterns.add("yyyy-M-d HH:mm:ss.SSSSSSSSS'Z'");

// this is section with `T`
datePatterns.add("yyyy-MM-dd'T'HH:mm");
datePatterns.add("yyyy-MM-dd'T'HH:mm X");
datePatterns.add("yyyy-MM-dd'T'HH:mm Z");
datePatterns.add("yyyy-MM-dd'T'HH:mm z");
datePatterns.add("yyyy-MM-dd'T'HH:mm'Z'");

datePatterns.add("yyyy-MM-dd'T'HH:mm:ss");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss X");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss Z");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss z");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss'Z'");

datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSS");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSS X");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSS Z");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSS z");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSS");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSS X");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSS Z");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSS z");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'");

datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS X");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS Z");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS z");
datePatterns.add("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'");

datePatterns.add("yyyy-M-d'T'HH:mm");
datePatterns.add("yyyy-M-d'T'HH:mm X");
datePatterns.add("yyyy-M-d'T'HH:mm Z");
datePatterns.add("yyyy-M-d'T'HH:mm z");
datePatterns.add("yyyy-M-d'T'HH:mm'Z'");

datePatterns.add("yyyy-M-d'T'HH:mm:ss");
datePatterns.add("yyyy-M-d'T'HH:mm:ss X");
datePatterns.add("yyyy-M-d'T'HH:mm:ss Z");
datePatterns.add("yyyy-M-d'T'HH:mm:ss z");
datePatterns.add("yyyy-M-d'T'HH:mm:ss'Z'");

datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSS");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSS X");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSS Z");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSS z");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSS'Z'");

datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSS");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSS X");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSS Z");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSS z");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSS'Z'");

datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSSSSS");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSSSSS X");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSSSSS Z");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSSSSS z");
datePatterns.add("yyyy-M-d'T'HH:mm:ss.SSSSSSSSS'Z'");

datePatterns.add("EEE MMM d HH:mm:ss yyyy");
datePatterns.add("EEE, d MMM yyyy HH:mm:ss Z");
datePatterns.add("EEE, d MMM yyyy HH:mm:ss z");
datePatterns.add("MMM d HH:mm:ss yyyy");
datePatterns.add("d MMM yyyy HH:mm:ss Z");
datePatterns.add("d MMM yyyy HH:mm:ss z");
datePatterns.add("yyyy-M-d'T'HH:mm:ssXXX");

datePatterns.add("d MMM");
datePatterns.add("d MMM yy");
datePatterns.add("d MMM yyyy");
datePatterns.add("d MMMM");
datePatterns.add("d MMMM yy");
datePatterns.add("d MMMM yyyy");

datePatterns.add("MMM d");
datePatterns.add("MMM d, yy");
datePatterns.add("MMM d, yyyy");

datePatterns.add("MMMM d");
datePatterns.add("MMMM d, yy");
datePatterns.add("MMMM d, yyyy");

datePatterns.add("MMM");
datePatterns.add("MMM yy");
datePatterns.add("MMM yyyy");

datePatterns.add("MMMM");
datePatterns.add("MMMM yy");
datePatterns.add("MMMM yyyy");

datePatterns.add("H:mm");
datePatterns.add("H:mm:ss");
}

public static ZonedDateTime parse(String str, Locale locale, ZoneId defaultZone) {

String normalized = str.toLowerCase();

for(String value : toBeRemoved) {
normalized = normalized.replace(value, "");
}

for(String pattern : datePatterns) {
try {

Expand All @@ -110,7 +163,7 @@ public static ZonedDateTime parse(String str, Locale locale, ZoneId defaultZone)
.appendPattern(pattern)
.toFormatter(locale);

TemporalAccessor temporalAccessor = timeFormatter.parse(str);
TemporalAccessor temporalAccessor = timeFormatter.parse(normalized);
return getZonedDateTimeFromTemporalAccessor(temporalAccessor, defaultZone);
} catch (Exception e) {
// ignore
Expand Down
53 changes: 53 additions & 0 deletions src/test/java/liqp/filters/DateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class DateTest {

Expand Down Expand Up @@ -118,6 +119,7 @@ public void applyOriginalTest() throws Exception {
final Filter filter = dateFilterSetting.filters.get("date");

assertThat(filter.apply("Fri Jul 16 01:00:00 2004", context, "%m/%d/%Y"), is((Object)"07/16/2004"));
assertThat(filter.apply("Fri Jul 9 01:00:00 2004", context, "%m/%d/%Y"), is((Object)"07/09/2004"));

assertThat(filter.apply("Fri Jul 16 01:00 2004", context, "%m/%d/%Y"), is((Object)"07/16/2004"));

Expand Down Expand Up @@ -233,4 +235,55 @@ public void test298InstantWhenEpochBeginAtUTC() {
String res = parser.parse("{{ val }}").render("val", instant);
assertEquals("1970-01-01 00:00:00 Z", res);
}

// https://github.com/bkiers/Liqp/issues/309
@Test
public void testSupportedDateStrings() {
String[] tests = {
"now",
"today",
"1 March",
"MAR",
"MARCH",
"2024 MAR",
"2 mar",
"2 MAR",
"march 2nd",
"MARCH 2",
"MARCH 2nd",
"MARCH 3RD",
"MARCH 4th",
"MARCH 5th",
"MARCH 10th",
"2010-10-31",
"Aug 2000",
"Aug 31",
"Wed Nov 28 14:33:20 2001",
"Wed, 05 Oct 2011 22:26:12 -0400",
"Wed, 05 Oct 2011 02:26:12 GMT",
"Nov 29 14:33:20 2001",
"05 Oct 2011 22:26:12 -0400",
"06 Oct 2011 02:26:12 GMT",
"2011-10-05T22:26:12-04:00",
"0:00",
"1:00",
"01:00",
"12:00",
"16:30",
"3/2024",
"01/03",
"03/31",
"2001/03",
"01/2003",
"70-10-31"
};

for (String test : tests) {
// Parse every test date. If this fails, the rendering engine will just return the
// string. The '%s' will convert the date into a timestamp we check that what is
// returned is a sequence of digits.
String rendered = TemplateParser.DEFAULT.parse("{% assign d = '" + test + "' | date: '%s' %}{{ d }}").render();
assertTrue("Failed to parse: '" + test + "'", rendered.matches("\\d+"));
}
}
}

0 comments on commit 4f2a07a

Please sign in to comment.