Skip to content

Commit dcf26d5

Browse files
authored
Merge pull request #919 from bitwiseman/task/one-more-date
Fixed and streamlined date parsing
2 parents 4f0d62f + 4d46872 commit dcf26d5

File tree

2 files changed

+49
-28
lines changed

2 files changed

+49
-28
lines changed

src/main/java/org/kohsuke/github/GitHubClient.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@
1919
import java.net.SocketTimeoutException;
2020
import java.net.URL;
2121
import java.nio.charset.StandardCharsets;
22-
import java.text.ParseException;
23-
import java.text.SimpleDateFormat;
22+
import java.time.Instant;
23+
import java.time.format.DateTimeFormatter;
24+
import java.time.temporal.ChronoUnit;
2425
import java.util.Base64;
2526
import java.util.Date;
2627
import java.util.HashMap;
2728
import java.util.List;
2829
import java.util.Map;
2930
import java.util.Objects;
30-
import java.util.TimeZone;
3131
import java.util.function.Consumer;
3232
import java.util.logging.Logger;
3333

@@ -80,9 +80,8 @@ abstract class GitHubClient {
8080
private static final ObjectMapper MAPPER = new ObjectMapper();
8181
static final String GITHUB_URL = "https://api.github.com";
8282

83-
private static final String[] TIME_FORMATS = { "yyyy/MM/dd HH:mm:ss ZZZZ", "yyyy-MM-dd'T'HH:mm:ss'Z'",
84-
"yyyy-MM-dd'T'HH:mm:ss.S'Z'" // GitHub App endpoints return a different date format
85-
};
83+
private static final DateTimeFormatter DATE_TIME_PARSER_SLASHES = DateTimeFormatter
84+
.ofPattern("yyyy/MM/dd HH:mm:ss Z");
8685

8786
static {
8887
MAPPER.setVisibility(new VisibilityChecker.Std(NONE, NONE, NONE, NONE, ANY));
@@ -652,22 +651,24 @@ static URL parseURL(String s) {
652651
static Date parseDate(String timestamp) {
653652
if (timestamp == null)
654653
return null;
655-
for (String f : TIME_FORMATS) {
656-
try {
657-
SimpleDateFormat df = new SimpleDateFormat(f);
658-
df.setTimeZone(TimeZone.getTimeZone("GMT"));
659-
return df.parse(timestamp);
660-
} catch (ParseException e) {
661-
// try next
662-
}
654+
655+
return Date.from(parseInstant(timestamp));
656+
}
657+
658+
static Instant parseInstant(String timestamp) {
659+
if (timestamp == null)
660+
return null;
661+
662+
if (timestamp.charAt(4) == '/') {
663+
// Unsure where this is used, but retained for compatibility.
664+
return Instant.from(DATE_TIME_PARSER_SLASHES.parse(timestamp));
665+
} else {
666+
return Instant.from(DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp));
663667
}
664-
throw new IllegalStateException("Unable to parse the timestamp: " + timestamp);
665668
}
666669

667670
static String printDate(Date dt) {
668-
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
669-
df.setTimeZone(TimeZone.getTimeZone("GMT"));
670-
return df.format(dt);
671+
return DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(dt.getTime()).truncatedTo(ChronoUnit.SECONDS));
671672
}
672673

673674
/**

src/test/java/org/kohsuke/github/GitHubStaticTest.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.text.SimpleDateFormat;
66
import java.time.Instant;
7+
import java.time.format.DateTimeParseException;
78
import java.time.temporal.ChronoUnit;
89
import java.util.Date;
910
import java.util.TimeZone;
@@ -20,25 +21,38 @@ public class GitHubStaticTest extends AbstractGitHubWireMockTest {
2021

2122
@Test
2223
public void timeRoundTrip() throws Exception {
23-
Instant instantNow = Instant.now();
24+
final long stableInstantEpochMilli = 1533721222255L;
25+
Instant instantNow = Instant.ofEpochMilli(stableInstantEpochMilli);
2426

2527
Date instantSeconds = Date.from(instantNow.truncatedTo(ChronoUnit.SECONDS));
2628
Date instantMillis = Date.from(instantNow.truncatedTo(ChronoUnit.MILLIS));
2729

28-
// if we happen to land exactly on zero milliseconds, add 1 milli
29-
if (instantSeconds.equals(instantMillis)) {
30-
instantMillis = Date.from(instantNow.plusMillis(1).truncatedTo(ChronoUnit.MILLIS));
31-
}
30+
String instantFormatSlash = formatZonedDate(instantMillis, "yyyy/MM/dd HH:mm:ss ZZZZ", "PST");
31+
assertThat(instantFormatSlash, equalTo("2018/08/08 02:40:22 -0700"));
3232

33-
// TODO: other formats
34-
String instantFormatSlash = formatDate(instantMillis, "yyyy/MM/dd HH:mm:ss ZZZZ");
3533
String instantFormatDash = formatDate(instantMillis, "yyyy-MM-dd'T'HH:mm:ss'Z'");
34+
assertThat(instantFormatDash, equalTo("2018-08-08T09:40:22Z"));
35+
3636
String instantFormatMillis = formatDate(instantMillis, "yyyy-MM-dd'T'HH:mm:ss.S'Z'");
37+
assertThat(instantFormatMillis, equalTo("2018-08-08T09:40:22.255Z"));
38+
39+
String instantFormatMillisZoned = formatZonedDate(instantMillis, "yyyy-MM-dd'T'HH:mm:ss.SXXX", "PST");
40+
assertThat(instantFormatMillisZoned, equalTo("2018-08-08T02:40:22.255-07:00"));
41+
3742
String instantSecondsFormatMillis = formatDate(instantSeconds, "yyyy-MM-dd'T'HH:mm:ss.S'Z'");
43+
assertThat(instantSecondsFormatMillis, equalTo("2018-08-08T09:40:22.0Z"));
44+
45+
String instantSecondsFormatMillisZoned = formatZonedDate(instantSeconds, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", "PST");
46+
assertThat(instantSecondsFormatMillisZoned, equalTo("2018-08-08T02:40:22.000-07:00"));
47+
3848
String instantBadFormat = formatDate(instantMillis, "yy-MM-dd'T'HH:mm'Z'");
49+
assertThat(instantBadFormat, equalTo("18-08-08T09:40Z"));
3950

4051
assertThat(GitHubClient.parseDate(GitHubClient.printDate(instantSeconds)),
4152
equalTo(GitHubClient.parseDate(GitHubClient.printDate(instantMillis))));
53+
assertThat(GitHubClient.printDate(instantSeconds), equalTo("2018-08-08T09:40:22Z"));
54+
assertThat(GitHubClient.printDate(GitHubClient.parseDate(instantFormatMillisZoned)),
55+
equalTo("2018-08-08T09:40:22Z"));
4256

4357
assertThat(instantSeconds, equalTo(GitHubClient.parseDate(GitHubClient.printDate(instantSeconds))));
4458

@@ -51,14 +65,16 @@ public void timeRoundTrip() throws Exception {
5165

5266
// This parser does not truncate to the nearest second, so it will be equal
5367
assertThat(instantMillis, equalTo(GitHubClient.parseDate(instantFormatMillis)));
68+
assertThat(instantMillis, equalTo(GitHubClient.parseDate(instantFormatMillisZoned)));
5469

5570
assertThat(instantSeconds, equalTo(GitHubClient.parseDate(instantSecondsFormatMillis)));
71+
assertThat(instantSeconds, equalTo(GitHubClient.parseDate(instantSecondsFormatMillisZoned)));
5672

5773
try {
5874
GitHubClient.parseDate(instantBadFormat);
5975
fail("Bad time format should throw.");
60-
} catch (IllegalStateException e) {
61-
assertThat(e.getMessage(), equalTo("Unable to parse the timestamp: " + instantBadFormat));
76+
} catch (DateTimeParseException e) {
77+
assertThat(e.getMessage(), equalTo("Text '" + instantBadFormat + "' could not be parsed at index 0"));
6278
}
6379
}
6480

@@ -226,8 +242,12 @@ public void testMappingReaderWriter() throws Exception {
226242
}
227243

228244
static String formatDate(Date dt, String format) {
245+
return formatZonedDate(dt, format, "GMT");
246+
}
247+
248+
static String formatZonedDate(Date dt, String format, String timeZone) {
229249
SimpleDateFormat df = new SimpleDateFormat(format);
230-
df.setTimeZone(TimeZone.getTimeZone("GMT"));
250+
df.setTimeZone(TimeZone.getTimeZone(timeZone));
231251
return df.format(dt);
232252
}
233253

0 commit comments

Comments
 (0)