Skip to content

Commit 7876268

Browse files
authored
#435 fix: +1sec to latest timeQualifier if sub-seconds are used (#454)
Includes commits: change DefaultTimeFormat, DPLTimeFormat and EpochTimestamp(now InstantTimestamp) to return Instant. Add RoundedUpTimestamp decorator, which is called when LATEST timeQualifier is used, otherwise InstantTimestamp is used directly. cleanup DefaultTimeFormat as suggested in PR review add invalidTimeformat test added timezones to test data (earliestLatestTest_data/earliestLatestTest_epoch_data.jsonl); fixed failing tests due to that #435 initial commit: add +1sec to latest time if sub-seconds used
1 parent 0b64ffa commit 7876268

21 files changed

Lines changed: 475 additions & 127 deletions

src/main/java/com/teragrep/pth10/ast/DPLParserConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public void setEarliest(String earliest) {
111111
// Try to check if it is relative and catch exception
112112
try {
113113
RelativeTimestamp rtTimestamp = rtParser.parse(earliest); // can throw error if not relative timestamp
114-
earliestEpoch = rtTimestamp.calculate(now);
114+
earliestEpoch = rtTimestamp.calculate(now).getEpochSecond();
115115
}
116116
catch (NumberFormatException ne) {
117117
// absolute time
@@ -145,7 +145,7 @@ public void setLatest(String latest) {
145145
// Try to check if it is relative and catch exception
146146
try {
147147
RelativeTimestamp rtTimestamp = rtParser.parse(latest); // can throw exception if not relative timestamp
148-
latestEpoch = rtTimestamp.calculate(now);
148+
latestEpoch = rtTimestamp.calculate(now).getEpochSecond();
149149
}
150150
catch (NumberFormatException ne) {
151151
// absolute time

src/main/java/com/teragrep/pth10/ast/DPLTimeFormat.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
import java.text.ParseException;
4949
import java.text.SimpleDateFormat;
50+
import java.time.Instant;
5051

5152
/**
5253
* For using the DPL custom timeformat like Java's SimpleDateFormat. Get a Date object with parse -function for example.
@@ -78,8 +79,8 @@ public SimpleDateFormat createSimpleDateFormat() {
7879
* @return Unix Epoch
7980
* @throws ParseException when dplTime doesn't have the correct format
8081
*/
81-
public long getEpoch(String dplTime) throws ParseException {
82-
return createSimpleDateFormat().parse(dplTime).getTime() / 1000L;
82+
public Instant instantOf(String dplTime) throws ParseException {
83+
return createSimpleDateFormat().parse(dplTime).toInstant();
8384
}
8485

8586
// replace dpl time units with java-compatible time units

src/main/java/com/teragrep/pth10/ast/DefaultTimeFormat.java

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@
5656
*/
5757
public class DefaultTimeFormat {
5858

59+
private final String[] formats;
60+
61+
public DefaultTimeFormat() {
62+
this(new String[] {
63+
"MM/dd/yyyy:HH:mm:ss",
64+
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
65+
"yyyy-MM-dd'T'HH:mm:ss.SSS",
66+
"yyyy-MM-dd'T'HH:mm:ssXXX",
67+
"yyyy-MM-dd'T'HH:mm:ss"
68+
});
69+
}
70+
71+
public DefaultTimeFormat(String[] formats) {
72+
this.formats = formats;
73+
}
74+
5975
/**
6076
* Calculate the epoch from given string.
6177
*
@@ -73,40 +89,15 @@ public long getEpoch(String time) {
7389
* @return Date parsed from the given string
7490
*/
7591
public Date parse(String time) {
76-
// Try parsing all the three default timeformats
77-
Date date;
78-
79-
int attempt = 0;
80-
while (true) {
92+
// Try parsing all provided time formats in order
93+
for (final String format : formats) {
8194
try {
82-
if (attempt == 0) {
83-
// Use default format (MM/dd/yyyy:HH:mm:ss)
84-
// Use system default timezone
85-
date = this.parseDate(time, "MM/dd/yyyy:HH:mm:ss");
86-
}
87-
else if (attempt == 1) {
88-
// On first fail, try ISO 8601 with timezone offset, e.g. '2011-12-03T10:15:30+01:00'
89-
date = this.parseDate(time, "yyyy-MM-dd'T'HH:mm:ssXXX");
90-
}
91-
else {
92-
// On second fail, try ISO 8601 without offset, e.g. '2011-12-03T10:15:30'
93-
// Use system default timezone
94-
date = this.parseDate(time, "yyyy-MM-dd'T'HH:mm:ss");
95-
}
96-
break;
97-
95+
return parseDate(time, format);
9896
}
99-
catch (ParseException e) {
100-
if (attempt > 1) {
101-
throw new RuntimeException("TimeQualifier conversion error: <" + time + "> can't be parsed.");
102-
}
103-
}
104-
finally {
105-
attempt++;
97+
catch (ParseException ignored) {
10698
}
10799
}
108-
109-
return date;
100+
throw new RuntimeException("TimeQualifier conversion error: <" + time + "> can't be parsed.");
110101
}
111102

112103
private Date parseDate(String time, String timeFormat) throws ParseException {

src/main/java/com/teragrep/pth10/ast/commands/evalstatement/UDFs/Mvrange.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ else if (stepObj instanceof String) {
106106
RelativeTimestamp rtTimestamp = rtParser.parse("+" + stepStr);
107107
// Go until incremented past end
108108
while (time < end) {
109-
time = rtTimestamp.calculate(new Timestamp(time * 1000L));
109+
time = rtTimestamp.calculate(new Timestamp(time * 1000L)).getEpochSecond();
110110

111111
// If time went past end, stop incrementing and don't add to mv field
112112
if (time > end) {

src/main/java/com/teragrep/pth10/ast/commands/evalstatement/UDFs/Relative_time.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public class Relative_time implements UDF2<Long, String, Long>, Serializable {
6565
public Long call(Long unixtime, String modifier) throws Exception {
6666
RelativeTimeParser rtParser = new RelativeTimeParser();
6767
RelativeTimestamp rtTimestamp = rtParser.parse(modifier);
68-
return rtTimestamp.calculate(new Timestamp(unixtime * 1000L));
68+
return rtTimestamp.calculate(new Timestamp(unixtime * 1000L)).getEpochSecond();
6969
}
7070

7171
}

src/main/java/com/teragrep/pth10/ast/commands/transformstatement/convert/Mktime.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public class Mktime implements UDF2<String, String, String> {
6161
@Override
6262
public String call(String hrt, String tf) throws Exception {
6363
DPLTimeFormat format = new DPLTimeFormat(tf);
64-
Long rv = format.getEpoch(hrt);
64+
Long rv = format.instantOf(hrt).getEpochSecond();
6565

6666
return rv.toString();
6767
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Teragrep Data Processing Language (DPL) translator for Apache Spark (pth_10)
3+
* Copyright (C) 2019-2024 Suomen Kanuuna Oy
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*
18+
*
19+
* Additional permission under GNU Affero General Public License version 3
20+
* section 7
21+
*
22+
* If you modify this Program, or any covered work, by linking or combining it
23+
* with other code, such other code is not for that reason alone subject to any
24+
* of the requirements of the GNU Affero GPL version 3 as long as this Program
25+
* is the same Program as licensed from Suomen Kanuuna Oy without any additional
26+
* modifications.
27+
*
28+
* Supplemented terms under GNU Affero General Public License version 3
29+
* section 7
30+
*
31+
* Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
32+
* versions must be marked as "Modified version of" The Program.
33+
*
34+
* Names of the licensors and authors may not be used for publicity purposes.
35+
*
36+
* No rights are granted for use of trade names, trademarks, or service marks
37+
* which are in The Program if any.
38+
*
39+
* Licensee must indemnify licensors and authors for any liability that these
40+
* contractual assumptions impose on licensors and authors.
41+
*
42+
* To the extent this program is licensed as part of the Commercial versions of
43+
* Teragrep, the applicable Commercial License may apply to this file if you as
44+
* a licensee so wish it.
45+
*/
46+
package com.teragrep.pth10.ast.time;
47+
48+
import java.time.Instant;
49+
50+
public interface DPLTimestamp {
51+
52+
public abstract Instant instant();
53+
}

src/main/java/com/teragrep/pth10/ast/time/EpochTimestamp.java renamed to src/main/java/com/teragrep/pth10/ast/time/InstantTimestamp.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,46 +52,47 @@
5252

5353
import java.sql.Timestamp;
5454
import java.text.ParseException;
55+
import java.time.Instant;
5556
import java.util.Objects;
5657

57-
public final class EpochTimestamp {
58+
public final class InstantTimestamp implements DPLTimestamp {
5859

5960
private final String value;
6061
private final String timeformat;
6162

62-
public EpochTimestamp(final String value, final String timeformat) {
63+
public InstantTimestamp(final String value, final String timeformat) {
6364
this.value = value;
6465
this.timeformat = timeformat;
6566
}
6667

67-
public long epoch() {
68-
long rv;
68+
public Instant instant() {
69+
Instant rv;
6970
try {
7071
RelativeTimestamp relativeTimestamp = new RelativeTimeParser().parse(value);
7172
rv = relativeTimestamp.calculate(new Timestamp(System.currentTimeMillis()));
7273
}
7374
catch (NumberFormatException ne) {
74-
rv = epochFromString(value, timeformat);
75+
rv = instantFromString(value, timeformat);
7576
}
7677

7778
return rv;
7879
}
7980

8081
// Uses defaultTimeFormat if timeformat is null and DPLTimeFormat if timeformat isn't null (which means that the
8182
// timeformat= option was used).
82-
private long epochFromString(final String value, final String timeFormatString) {
83+
private Instant instantFromString(final String value, final String timeFormatString) {
8384
final String unquotedValue = new UnquotedText(new TextString(value)).read(); // erase the possible outer quotes
84-
final long timevalue;
85+
final Instant timevalue;
8586
if (timeFormatString == null || timeFormatString.isEmpty()) {
86-
timevalue = new DefaultTimeFormat().getEpoch(unquotedValue);
87+
timevalue = new DefaultTimeFormat().parse(unquotedValue).toInstant();
8788
}
8889
else {
8990
// TODO: should be included in DPLTimeFormat
9091
if (timeFormatString.equals("%s")) {
91-
return Long.parseLong(unquotedValue);
92+
return Instant.ofEpochSecond(Long.parseLong(unquotedValue));
9293
}
9394
try {
94-
timevalue = new DPLTimeFormat(timeFormatString).getEpoch(unquotedValue);
95+
timevalue = new DPLTimeFormat(timeFormatString).instantOf(unquotedValue);
9596
}
9697
catch (ParseException e) {
9798
throw new RuntimeException("TimeQualifier conversion error: <" + unquotedValue + "> can't be parsed.");
@@ -106,7 +107,7 @@ public boolean equals(Object o) {
106107
return true;
107108
if (o == null || getClass() != o.getClass())
108109
return false;
109-
EpochTimestamp that = (EpochTimestamp) o;
110+
InstantTimestamp that = (InstantTimestamp) o;
110111
return Objects.equals(value, that.value) && Objects.equals(timeformat, that.timeformat);
111112
}
112113

src/main/java/com/teragrep/pth10/ast/time/RelativeTimestamp.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,22 @@ public RelativeTimestamp(RelativeOffset offset, SnapToTime snapToTime) {
6464
* @param timestamp A moment in time, usually the current time
6565
* @return Calculated time as epoch milliseconds
6666
*/
67-
public long calculate(Timestamp timestamp) {
67+
public Instant calculate(Timestamp timestamp) {
6868
Instant time = timestamp.toInstant();
6969

7070
// if both are null, "now" option is left. Therefore, returns current time.
71-
if (offset == null && snapToTime == null)
71+
if (offset == null && snapToTime == null) {
7272
time = new Timestamp(System.currentTimeMillis()).toInstant();
73+
}
7374

74-
if (offset != null)
75+
if (offset != null) {
7576
time = offset.addOffset(time);
76-
if (snapToTime != null)
77+
}
78+
79+
if (snapToTime != null) {
7780
time = snapToTime.snap(time);
78-
return time.getEpochSecond();
81+
}
82+
83+
return time;
7984
}
8085
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Teragrep Data Processing Language (DPL) translator for Apache Spark (pth_10)
3+
* Copyright (C) 2019-2024 Suomen Kanuuna Oy
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*
18+
*
19+
* Additional permission under GNU Affero General Public License version 3
20+
* section 7
21+
*
22+
* If you modify this Program, or any covered work, by linking or combining it
23+
* with other code, such other code is not for that reason alone subject to any
24+
* of the requirements of the GNU Affero GPL version 3 as long as this Program
25+
* is the same Program as licensed from Suomen Kanuuna Oy without any additional
26+
* modifications.
27+
*
28+
* Supplemented terms under GNU Affero General Public License version 3
29+
* section 7
30+
*
31+
* Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
32+
* versions must be marked as "Modified version of" The Program.
33+
*
34+
* Names of the licensors and authors may not be used for publicity purposes.
35+
*
36+
* No rights are granted for use of trade names, trademarks, or service marks
37+
* which are in The Program if any.
38+
*
39+
* Licensee must indemnify licensors and authors for any liability that these
40+
* contractual assumptions impose on licensors and authors.
41+
*
42+
* To the extent this program is licensed as part of the Commercial versions of
43+
* Teragrep, the applicable Commercial License may apply to this file if you as
44+
* a licensee so wish it.
45+
*/
46+
package com.teragrep.pth10.ast.time;
47+
48+
import java.time.Instant;
49+
50+
public final class RoundedUpTimestamp implements DPLTimestamp {
51+
52+
private final DPLTimestamp dplTimestamp;
53+
54+
public RoundedUpTimestamp(final DPLTimestamp dplTimestamp) {
55+
this.dplTimestamp = dplTimestamp;
56+
}
57+
58+
public Instant instant() {
59+
Instant origin = dplTimestamp.instant();
60+
// If date is for latest timeQualifier and has fractions-of-second, add 1 second to capture events
61+
// that are on the same second
62+
if (origin.getNano() > 0) {
63+
origin = origin.plusSeconds(1);
64+
}
65+
return origin;
66+
}
67+
}

0 commit comments

Comments
 (0)