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

[CYB-205] leap year DateUtils fix #71

Merged
merged 4 commits into from
Apr 20, 2024
Merged
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
6 changes: 6 additions & 0 deletions flink-cyber/flink-parse/flink-parse-metron/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@
<version>${jupiter.junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
Expand All @@ -39,25 +44,29 @@ public class DateUtils {
// Per IBM LEEF guide at https://www.ibm.com/support/knowledgecenter/SS42VS_DSM/c_LEEF_Format_Guide_intro.html
public static List<SimpleDateFormat> DATE_FORMATS_LEEF = new ArrayList<SimpleDateFormat>() {
{
add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss.SSS zzz"));
add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss.SSS"));
add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss.SSS Z"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss.SSS z"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss.SSS"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss"));
}
};

public static List<SimpleDateFormat> DATE_FORMATS_CEF = new ArrayList<SimpleDateFormat>() {
{
// as per CEF Spec
add(new SimpleDateFormat("MMM dd HH:mm:ss.SSS zzz"));
add(new SimpleDateFormat("MMM dd HH:mm:ss.SSS"));
add(new SimpleDateFormat("MMM dd HH:mm:ss zzz"));
add(new SimpleDateFormat("MMM dd HH:mm:ss"));
add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss.SSS zzz"));
add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss.SSS"));
add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss zzz"));
add(new SimpleDateFormat("MMM dd yyyy HH:mm:ss"));
add(new SimpleDateFormat("MMM d HH:mm:ss.SSS Z"));
add(new SimpleDateFormat("MMM d HH:mm:ss.SSS z"));
add(new SimpleDateFormat("MMM d HH:mm:ss.SSS"));
add(new SimpleDateFormat("MMM d HH:mm:ss zzz"));
add(new SimpleDateFormat("MMM d HH:mm:ss"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss.SSS Z"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss.SSS z"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss.SSS"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss Z"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss z"));
add(new SimpleDateFormat("MMM d yyyy HH:mm:ss"));
// found in the wild
add(new SimpleDateFormat("dd MMMM yyyy HH:mm:ss"));
add(new SimpleDateFormat("d MMMM yyyy HH:mm:ss"));
}
};

Expand Down Expand Up @@ -103,22 +112,33 @@ public static long parseMultiformat(String candidate, List<SimpleDateFormat> val
} else {
for (SimpleDateFormat pattern : validPatterns) {
try {
Calendar cal = Calendar.getInstance();
cal.setTime(pattern.parse(candidate));
Calendar current = Calendar.getInstance();
if (cal.get(Calendar.YEAR) == 1970) {
cal.set(Calendar.YEAR, current.get(Calendar.YEAR));
DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder()
.appendPattern(pattern.toPattern());
if (!pattern.toPattern().contains("y")) {
formatterBuilder
.parseDefaulting(ChronoField.YEAR, LocalDate.now().getYear());
}
current.add(Calendar.DAY_OF_MONTH, 4);
if (cal.after(current)) {
cal.add(Calendar.YEAR, -1);
DateTimeFormatter formatter = formatterBuilder.toFormatter();
ZonedDateTime parsedValue = parseDateTimeWithDefaultTimezone(candidate, formatter);
ZonedDateTime current = ZonedDateTime.now(parsedValue.getZone());

current = current.plusDays(4);
if (parsedValue.isAfter(current)) {
parsedValue = parsedValue.minusYears(1);
}
return cal.getTimeInMillis();
} catch (ParseException e) {
return parsedValue.toInstant().toEpochMilli();
} catch (DateTimeParseException e) {
continue;
}
}
throw new ParseException("Failed to parse any of the given date formats", 0);
}
}

private static ZonedDateTime parseDateTimeWithDefaultTimezone(String candidate, DateTimeFormatter formatter) {
TemporalAccessor temporalAccessor = formatter.parseBest(candidate, ZonedDateTime::from, LocalDateTime::from);
return temporalAccessor instanceof ZonedDateTime
? ((ZonedDateTime) temporalAccessor)
: ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,22 @@
import org.json.simple.parser.JSONParser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.TemporalAccessor;
import java.util.*;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

public class CEFParserTest {
private CEFParser parser;
Expand Down Expand Up @@ -159,6 +165,27 @@ public void testMissingYearFromDate() throws java.text.ParseException {
runMissingYear(correct, current);
}

@Test
public void testMissingLeapYearFromDate() {
Calendar current = Calendar.getInstance();
Calendar correct = Calendar.getInstance();

current.set(Calendar.YEAR, 2024);
current.set(Calendar.MONTH, 1);
current.set(Calendar.DAY_OF_MONTH, 29);

correct.setTimeInMillis(current.getTimeInMillis());

LocalDate fakeNowDate = LocalDate.of(2024, 2, 29);
try (MockedStatic<LocalDate> dateContext = Mockito.mockStatic(LocalDate.class, Mockito.CALLS_REAL_METHODS)) {
dateContext.when(LocalDate::now).thenReturn(fakeNowDate);
assertEquals(fakeNowDate, LocalDate.now());
assertNotNull(fakeNowDate);

runMissingYear(correct, current);
}
}

@Test
public void testFourDayFutureBecomesPast() {
Calendar current = Calendar.getInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@
import org.json.simple.parser.JSONParser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

public class LEEFParserTest {
private LEEFParser parser;
Expand Down Expand Up @@ -109,6 +114,27 @@ public void testMissingYearFromDate() {
runMissingYear(correct, current);
}

@Test
public void testMissingLeapYearFromDate() {
Calendar current = Calendar.getInstance();
Calendar correct = Calendar.getInstance();

current.set(Calendar.YEAR, 2024);
current.set(Calendar.MONTH, 1);
current.set(Calendar.DAY_OF_MONTH, 29);

correct.setTimeInMillis(current.getTimeInMillis());

LocalDate fakeNowDate = LocalDate.of(2024, 2, 29);
try (MockedStatic<LocalDate> dateContext = Mockito.mockStatic(LocalDate.class, Mockito.CALLS_REAL_METHODS)) {
dateContext.when(LocalDate::now).thenReturn(fakeNowDate);
assertEquals(fakeNowDate, LocalDate.now());
assertNotNull(fakeNowDate);

runMissingYear(correct, current);
}
}

@Test
public void testFourDayFutureBecomesPast() {
Calendar current = Calendar.getInstance();
Expand Down
Loading