Skip to content

Commit 0931d5f

Browse files
committed
[WIP]
1 parent b14efe5 commit 0931d5f

21 files changed

+432
-170
lines changed

ruby/cases_date.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,18 @@
3232
# assertEqual("", render({"null" => "bad"}, "{{null}}"))
3333
# assertEqual("", render({"empty" => "bad"}, "{{empty}}"))
3434
# assertEqual("", render({"blank" => "bad"}, "{{blank}}"))
35-
assertEqual("2007-11-01...", render({"a" => t }, "{{ a | truncate: 13 }}"))
35+
# assertEqual("2007-11-01...", render({"a" => t }, "{{ a | truncate: 13 }}"))
3636

3737
if isJekyll
38+
pp render({"a" => '2004-12-31'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # "2004-12-31 00:00:00 +0200"
39+
pp render({"a" => '31 December'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # "2025-12-31 00:00:00 +0200"
40+
pp render({"a" => '12:00'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # "2025-01-04 12:00:00 +0200"
41+
pp render({"a" => 'Friday'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # Friday
42+
pp render({"a" => 'Friday 12/24'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # "2025-12-24 00:00:00 +0200"
43+
pp render({"a" => '2004-12-31 11:23:58 Z'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # "2004-12-31 11:23:58 +0000"
44+
pp render({"a" => 'September 1969'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # "1969-09-01 00:00:00 +0300"
45+
pp render({"a" => '06 Nov 04'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # "2004-11-06 00:00:00 +0200"
46+
pp render({"a" => '1994-11-06T08'}, "{{ a | date: '%Y-%m-%d %H:%M:%S %z'}}") # "1994-11-06 00:00:00 +0200"
3847

3948
# target is string representation, source is iterated as collection(and so = match in "year" part)
4049
assertEqual("target is string representation: 2007-11-01 15:25:00 +0900", render({"a" => [{ "time" => t }], "b" => "2007"}, "target is string representation: {{ a | where: 'time', b | map: 'time'}}"))

src/main/java/liqp/filters/date/BasicDateParser.java

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.time.*;
44
import java.time.format.DateTimeFormatter;
55
import java.time.format.DateTimeFormatterBuilder;
6+
import java.time.temporal.ChronoField;
67
import java.time.temporal.TemporalAccessor;
78
import java.time.temporal.TemporalAdjusters;
89
import java.time.temporal.TemporalField;
@@ -97,16 +98,6 @@ public static ZonedDateTime getFullDateIfPossible(TemporalAccessor temporal, Zon
9798
if (temporal instanceof Instant) {
9899
return ZonedDateTime.ofInstant((Instant) temporal, defaultZone);
99100
}
100-
TemporalField[] copyThese = new TemporalField[]{
101-
YEAR,
102-
MONTH_OF_YEAR,
103-
DAY_OF_MONTH,
104-
HOUR_OF_DAY,
105-
MINUTE_OF_HOUR,
106-
SECOND_OF_MINUTE,
107-
NANO_OF_SECOND
108-
};
109-
110101

111102
ZoneId zoneId = temporal.query(TemporalQueries.zone());
112103
if (zoneId == null) {
@@ -115,6 +106,16 @@ public static ZonedDateTime getFullDateIfPossible(TemporalAccessor temporal, Zon
115106

116107
final LocalDateTime now = LocalDateTime.now(zoneId);
117108

109+
TemporalField[] copyThese = new TemporalField[]{
110+
NANO_OF_SECOND,
111+
SECOND_OF_MINUTE,
112+
MINUTE_OF_HOUR,
113+
HOUR_OF_DAY,
114+
DAY_OF_MONTH,
115+
MONTH_OF_YEAR,
116+
YEAR,
117+
};
118+
118119
if ("java.time.format.Parsed".equals(temporal.getClass().getName())) {
119120
Map<TemporalField, Function<TemporalAccessor, LocalDateTime>> factories = new HashMap<>();
120121
factories.put(DAY_OF_WEEK, t -> now.with(TemporalAdjusters.previousOrSame(DayOfWeek.from(t))));
@@ -124,11 +125,25 @@ public static ZonedDateTime getFullDateIfPossible(TemporalAccessor temporal, Zon
124125
}
125126
}
126127

127-
128-
LocalDateTime res = now.with(TemporalAdjusters.ofDateAdjuster(date -> date));
128+
LocalDateTime res = now;
129+
boolean zeroField = true;
129130
for (TemporalField tf: copyThese) {
130-
if (temporal.isSupported(tf)) {
131-
res = res.with(tf, temporal.get(tf));
131+
if (zeroField && temporal.isSupported(tf)) {
132+
zeroField = false;
133+
}
134+
if (zeroField) {
135+
if (temporal.isSupported(tf)) {
136+
long minimum = temporal.range(tf).getMinimum();
137+
res = res.with(tf, minimum);
138+
} else {
139+
res = res.with(tf, tf.range().getMinimum());
140+
}
141+
} else {
142+
if (temporal.isSupported(tf)) {
143+
res = res.with(tf, temporal.get(tf));
144+
} else {
145+
res = res.with(tf, now.get(tf));
146+
}
132147
}
133148
}
134149
return res.atZone(zoneId);

src/main/java/liqp/filters/date/fuzzy/DatePatternRecognizingContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public class DatePatternRecognizingContext {
66

77
public final Locale locale;
88
public Boolean hasYear;
9+
public Boolean hasEra;
910
public Boolean hasMonth;
1011
public Boolean hasDate;
1112
public Boolean weekDay;

src/main/java/liqp/filters/date/fuzzy/FuzzyDateParser.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,12 @@ private List<String> newList(String pattern) {
130130
private GuessingResult getGuessingResult(Stream<String> guessingStream, String normalized, Locale locale, ZoneId defaultZone) {
131131
return guessingStream
132132
.map(pattern -> {
133-
TemporalAccessor temporalAccessor = parseUsingPattern(normalized, pattern, locale);
133+
TemporalAccessor temporalAccessor = null;
134+
try {
135+
temporalAccessor = parseUsingPattern(normalized, pattern, locale);
136+
} catch (Exception e) {
137+
// ignore
138+
}
134139
if (temporalAccessor != null) {
135140
GuessingResult result = new GuessingResult();
136141
result.pattern = pattern;

src/main/java/liqp/filters/date/fuzzy/Part.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,18 @@ public String toString() {
161161
'}';
162162
}
163163
}
164+
class RecognizedYearWithoutEraPart extends RecognizedPart {
165+
public RecognizedYearWithoutEraPart(int start, int end, List<String> patterns, String source) {
166+
super(start, end, patterns, source);
167+
}
168+
169+
@Override
170+
public String toString() {
171+
return "RecognizedYearWithoutEraPart{" +
172+
"start=" + start +
173+
", end=" + end +
174+
", pattern='" + patterns + '\'' +
175+
'}';
176+
}
177+
}
164178
}

src/main/java/liqp/filters/date/fuzzy/PartExtractor.java

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
package liqp.filters.date.fuzzy;
22

3+
import static liqp.LValue.isBlank;
4+
35
import java.util.Arrays;
46
import java.util.List;
57
import java.util.function.Supplier;
68
import java.util.stream.Collectors;
79
import liqp.filters.date.fuzzy.Part.NewPart;
810
import liqp.filters.date.fuzzy.Part.RecognizedMonthNamePart;
911
import liqp.filters.date.fuzzy.Part.RecognizedPart;
12+
import liqp.filters.date.fuzzy.Part.RecognizedYearWithoutEraPart;
1013
import liqp.filters.date.fuzzy.extractors.PartExtractorResult;
1114

1215
public abstract class PartExtractor {
1316

17+
/**
18+
* for debugging purposes
19+
*/
20+
protected final String name;
21+
22+
public PartExtractor(String name) {
23+
this.name = name;
24+
}
25+
1426
public PartExtractorResult extract(String source, List<Part> parts, int i) {
1527
throw new UnsupportedOperationException("Not supported yet.");
1628
}
@@ -51,28 +63,58 @@ protected LookupResult getLookupResult(List<Part> parts, int i, PartExtractorRes
5163

5264
parts.remove(i);
5365

66+
int recognizedEnd = part.start() + per.end;
5467
if (per.end != source.length()) {
55-
NewPart after = new NewPart(part.start() + per.end, part.end(), source.substring(per.end));
68+
NewPart after = new NewPart(recognizedEnd, part.end(), source.substring(per.end));
5669
parts.add(i, after);
5770
}
5871

5972
RecognizedPart recognized;
60-
if (per.isMonthName) {
61-
recognized = new RecognizedMonthNamePart(part.start() + per.start, part.start() + per.end, per.formatterPatterns, source.substring(
62-
per.start, per.end));
73+
int recognizedStart = part.start() + per.start;
74+
String recognizedSource = source.substring(per.start, per.end);
75+
if (per.yearWithoutEra) {
76+
recognized = new RecognizedYearWithoutEraPart(recognizedStart, recognizedEnd, per.formatterPatterns, recognizedSource);
77+
} else if (per.isMonthName) {
78+
recognized = new RecognizedMonthNamePart(recognizedStart, recognizedEnd, per.formatterPatterns, recognizedSource);
6379
} else {
64-
recognized = new RecognizedPart(part.start() + per.start, part.start() + per.end, per.formatterPatterns, source.substring(
65-
per.start, per.end));
80+
recognized = new RecognizedPart(recognizedStart, recognizedEnd, per.formatterPatterns, recognizedSource);
6681
}
6782
parts.add(i, recognized);
6883

6984
if (per.start != 0) {
7085
NewPart before = new NewPart(
71-
part.start(), part.start() + per.start, source.substring(0, per.start));
86+
part.start(), recognizedStart, source.substring(0, per.start));
7287
parts.add(i, before);
7388
}
7489

7590
return new LookupResult(per.extractorName, parts, true);
7691
}
7792

93+
protected int getIndexByPartType(List<Part> parts, Class<? extends Part> partType) {
94+
for (int i = 0; i < parts.size(); i++) {
95+
Part part = parts.get(i);
96+
if (partType.isInstance(part)) {
97+
return i;
98+
}
99+
}
100+
return -1;
101+
}
102+
103+
protected LookupResult locatePart(List<Part> parts, PartExtractor extractor, int index) {
104+
Part part = parts.get(index);
105+
if (part instanceof RecognizedPart) {
106+
return new LookupResult(this.name, parts, false);
107+
}
108+
if (part instanceof NewPart) {
109+
NewPart newPart = (NewPart) part;
110+
String source = newPart.source();
111+
if (!isBlank(source) && extractor != null) {
112+
PartExtractorResult result = extractor.extract(source, parts, index);
113+
if (result.found) {
114+
return getLookupResult(parts, index, result);
115+
}
116+
}
117+
}
118+
return null;
119+
}
78120
}

src/main/java/liqp/filters/date/fuzzy/PartRecognizer.java

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import static liqp.filters.date.fuzzy.Part.PunctuationPart.punctuationChars;
44
import static liqp.filters.date.fuzzy.extractors.Extractors.allYMDPatternExtractor;
5+
import static liqp.filters.date.fuzzy.extractors.Extractors.eraAfterYearExtractor;
56
import static liqp.filters.date.fuzzy.extractors.Extractors.fullWeekdaysExtractor;
67
import static liqp.filters.date.fuzzy.extractors.Extractors.monthDateExtractor;
7-
import static liqp.filters.date.fuzzy.extractors.Extractors.monthExtractor;
8+
import static liqp.filters.date.fuzzy.extractors.Extractors.monthNameExtractor;
89
import static liqp.filters.date.fuzzy.extractors.Extractors.plainYearExtractor;
910
import static liqp.filters.date.fuzzy.extractors.Extractors.regularTimeExtractor;
1011
import static liqp.filters.date.fuzzy.extractors.Extractors.shortWeekdaysExtractor;
@@ -13,12 +14,8 @@
1314
import java.util.ArrayList;
1415
import java.util.Collections;
1516
import java.util.List;
16-
import liqp.filters.date.fuzzy.Part.NewPart;
1717
import liqp.filters.date.fuzzy.Part.PunctuationPart;
18-
import liqp.filters.date.fuzzy.Part.RecognizedMonthNamePart;
19-
import liqp.filters.date.fuzzy.Part.RecognizedPart;
2018
import liqp.filters.date.fuzzy.Part.UnrecognizedPart;
21-
import liqp.filters.date.fuzzy.extractors.PartExtractorResult;
2219

2320
public class PartRecognizer {
2421

@@ -38,13 +35,6 @@ List<Part> recognizePart(List<Part> parts, DatePatternRecognizingContext ctx) {
3835
ctx.weekDay = false;
3936
}
4037

41-
if (notSet(ctx.hasYear)) {
42-
LookupResult result = lookup(parts, yearWithEraExtractor.get(ctx.locale));
43-
if (result.found) {
44-
ctx.hasYear = true;
45-
return result.parts;
46-
}
47-
}
4838
if (notSet(ctx.hasTime)) {
4939
LookupResult result = lookup(parts, regularTimeExtractor.get(ctx.locale));
5040
if (result.found) {
@@ -61,20 +51,30 @@ List<Part> recognizePart(List<Part> parts, DatePatternRecognizingContext ctx) {
6151
ctx.hasDate = true;
6252
return result.parts;
6353
}
54+
result = lookup(parts, yearWithEraExtractor.get(ctx.locale));
55+
if (result.found) {
56+
ctx.hasYear = true;
57+
ctx.hasEra = true;
58+
return result.parts;
59+
}
60+
result = lookup(parts, plainYearExtractor.get(ctx.locale));
61+
if (result.found) {
62+
ctx.hasYear = true;
63+
return result.parts;
64+
}
6465
}
6566

66-
if (notSet(ctx.hasYear)) {
67-
LookupResult result = lookup(parts, plainYearExtractor.get(ctx.locale));
67+
if (isTrue(ctx.hasYear) && notSet(ctx.hasEra)) {
68+
LookupResult result = lookup(parts, eraAfterYearExtractor.get(ctx.locale));
6869
if (result.found) {
69-
ctx.hasYear = true;
70+
ctx.hasEra = true;
7071
return result.parts;
7172
}
72-
// last "year check" and since we are here - there is no year
73-
ctx.hasYear = false;
73+
ctx.hasEra = false;
7474
}
7575

7676
if (notSet(ctx.hasMonth)) {
77-
LookupResult result = lookup(parts, monthExtractor.get(ctx.locale));
77+
LookupResult result = lookup(parts, monthNameExtractor.get(ctx.locale));
7878
if (result.found) {
7979
ctx.hasMonth = true;
8080
return result.parts;
@@ -90,6 +90,14 @@ List<Part> recognizePart(List<Part> parts, DatePatternRecognizingContext ctx) {
9090
}
9191
ctx.hasDate = false;
9292
}
93+
//
94+
// if (notSet(ctx.hasYear)) {
95+
// LookupResult result = lookup(parts, twoDigitYearExtractor.get(ctx.locale));
96+
// if (result.found) {
97+
// ctx.hasYear = true;
98+
// return result.parts;
99+
// }
100+
// }
93101

94102
return markAsUnrecognized(parts);
95103
}

src/main/java/liqp/filters/date/fuzzy/extractors/AllYMDPatternExtractor.java

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import static liqp.filters.date.fuzzy.extractors.AnyYMDPatternExtractor.pD;
44
import static liqp.filters.date.fuzzy.extractors.AnyYMDPatternExtractor.pM;
5+
import static liqp.filters.date.fuzzy.extractors.AnyYMDPatternExtractor.pMn;
56
import static liqp.filters.date.fuzzy.extractors.AnyYMDPatternExtractor.pY2;
67
import static liqp.filters.date.fuzzy.extractors.AnyYMDPatternExtractor.pY4;
7-
import static liqp.filters.date.fuzzy.extractors.AnyYMDPatternExtractor.pp;
8+
import static liqp.filters.date.fuzzy.extractors.AnyYMDPatternExtractor.pp1;
9+
import static liqp.filters.date.fuzzy.extractors.AnyYMDPatternExtractor.pp2;
810

911
import java.util.ArrayList;
1012
import java.util.List;
@@ -16,39 +18,44 @@ public class AllYMDPatternExtractor extends PartExtractor {
1618
private final List<AnyYMDPatternExtractor> extractors = new ArrayList<>();
1719

1820
public AllYMDPatternExtractor() {
21+
super("AllYMDPatternExtractor");
1922
extractors.add(new AnyYMDPatternExtractor("iSO8601Y4MDPatternExtractor",
20-
pY4(), pp("-"), pM(), pp("-"), pD())); // yyyy-MM-dd
23+
pY4(), pp1("-"), pM(), pp2("-"), pD())); // yyyy-MM-dd
2124

2225
extractors.add(new AnyYMDPatternExtractor("americanY4MDPatternExtractor",
23-
pM(), pp("/"), pD(), pp("/"), pY4())); // MM/dd/yyyy
26+
pM(), pp1("/"), pD(), pp2("/"), pY4())); // MM/dd/yyyy
2427
// next are top-rated locale formats, according to gpt
2528
extractors.add(new AnyYMDPatternExtractor("indianY4MDPatternExtractor",
26-
pD(), pp("-"), pM(), pp("-"), pY4())); // d-M-yyyy
29+
pD(), pp1("-"), pM(), pp2("-"), pY4())); // d-M-yyyy
2730
extractors.add(new AnyYMDPatternExtractor("chineseY4MDPatternExtractor",
28-
pY4(), pp("/"), pM(), pp("/"), pD())); // yyyy/M/d
31+
pY4(), pp1("/"), pM(), pp2("/"), pD())); // yyyy/M/d
2932
extractors.add(new AnyYMDPatternExtractor("englishY4MDPatternExtractor",
30-
pD(), pp("/"), pM(), pp("/"), pY4())); // d/M/yyyy
33+
pD(), pp1("/"), pM(), pp2("/"), pY4())); // d/M/yyyy
3134
extractors.add(new AnyYMDPatternExtractor("slavicY4MDPatternExtractor",
32-
pD(), pp("."), pM(), pp("."), pY4())); // dd.MM.yyyy
35+
pD(), pp1("."), pM(), pp2("."), pY4())); // dd.MM.yyyy
3336
extractors.add(new AnyYMDPatternExtractor("coldEuropeY4MDPatternExtractor",
34-
pY4(), pp("-"), pM(), pp("-"), pD())); // yyyy-MM-dd
37+
pY4(), pp1("-"), pM(), pp2("-"), pD())); // yyyy-MM-dd
3538
extractors.add(new AnyYMDPatternExtractor("espanaY4MDPatternExtractor",
36-
pY4(), pp("-"), pM(), pp("-"), pD())); // yyyy/MM/dd
39+
pY4(), pp1("-"), pM(), pp2("-"), pD())); // yyyy/MM/dd
3740

3841
extractors.add(new AnyYMDPatternExtractor("americanY2MDPatternExtractor",
39-
pM(), pp("/"), pD(), pp("/"), pY2())); // MM/dd/yy
42+
pM(), pp1("/"), pD(), pp2("/"), pY2())); // MM/dd/yy
4043
extractors.add(new AnyYMDPatternExtractor("indianY2MDPatternExtractor",
41-
pD(), pp("-"), pM(), pp("-"), pY2())); // d-M-yy
44+
pD(), pp1("-"), pM(), pp2("-"), pY2())); // d-M-yy
4245
extractors.add(new AnyYMDPatternExtractor("chineseY2MDPatternExtractor",
43-
pY2(), pp("/"), pM(), pp("/"), pD())); // yy/M/d
46+
pY2(), pp1("/"), pM(), pp2("/"), pD())); // yy/M/d
4447
extractors.add(new AnyYMDPatternExtractor("englishY2MDPatternExtractor",
45-
pD(), pp("/"), pM(), pp("/"), pY2())); // d/M/yy
48+
pD(), pp1("/"), pM(), pp2("/"), pY2())); // d/M/yy
4649
extractors.add(new AnyYMDPatternExtractor("slavicY2MDPatternExtractor",
47-
pD(), pp("."), pM(), pp("."), pY2())); // dd.MM.yy
50+
pD(), pp1("."), pM(), pp2("."), pY2())); // dd.MM.yy
4851
extractors.add(new AnyYMDPatternExtractor("coldEuropeY2MDPatternExtractor",
49-
pY2(), pp("-"), pM(), pp("-"), pD())); // yy-MM-dd
52+
pY2(), pp1("-"), pM(), pp2("-"), pD())); // yy-MM-dd
5053
extractors.add(new AnyYMDPatternExtractor("espanaY2MDPatternExtractor",
51-
pY2(), pp("-"), pM(), pp("-"), pD())); // yy/MM/dd
54+
pY2(), pp1("-"), pM(), pp2("-"), pD())); // yy/MM/dd
55+
extractors.add(new AnyYMDPatternExtractor("RFC822Y4MDPatternExtractor",
56+
pD(), pp1(" "), pMn(), pp2(" "), pY4())); // dd MMMM yyyy
57+
extractors.add(new AnyYMDPatternExtractor("RFC822Y2MDPatternExtractor",
58+
pD(), pp1(" "), pMn(), pp2(" "), pY2())); // dd MMMM yy
5259
}
5360

5461
@Override
@@ -59,6 +66,6 @@ public PartExtractorResult extract(String source, List<Part> parts, int i) {
5966
return result;
6067
}
6168
}
62-
return new PartExtractorResult("AllYMDPatternExtractor");
69+
return new PartExtractorResult(this.name);
6370
}
6471
}

0 commit comments

Comments
 (0)