-
Notifications
You must be signed in to change notification settings - Fork 14.6k
KAFKA-18585 Fix fail test ValuesTest#shouldConvertDateValues #18611
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
Conversation
Hello @AndrewJSchofield, if you have free cycle, PTAL |
TimeZone tz = TimeZone.getDefault(); | ||
java.util.Date currentDate3 = new java.util.Date(current.getTime() - currentMillis - tz.getOffset(current.getTime())); | ||
java.util.Date d3 = Values.convertToDate(Date.SCHEMA, LocalDate.ofEpochDay(days).format(DateTimeFormatter.ISO_LOCAL_DATE)); | ||
assertEquals(currentDate, d3); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must be missing something here so please help me understand the following:
Java's util.Date is timezone agnostic (it's always epochs in UTC timezone) until you want to print it (where you can format it). Hence, the currentDate here should already be in UTC timezone. And the actual value from LocalDate.ofEpochDay(days).format(DateTimeFormatter.ISO_LOCAL_DATE)
should also be in UTC. So, where is the disconnect here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'm not sure either. But I can believe it's possible to write a test that is timezone-dependent.
Digging into this further, I would say that the behaviour for some of these tests with respect to timezones is incorrect, or at least unwise. I am not convinced by this patch, but I will dig into it further and give a more concrete answer shortly. |
TimeZone tz = TimeZone.getDefault(); | ||
java.util.Date currentDate3 = new java.util.Date(current.getTime() - currentMillis - tz.getOffset(current.getTime())); | ||
java.util.Date d3 = Values.convertToDate(Date.SCHEMA, LocalDate.ofEpochDay(days).format(DateTimeFormatter.ISO_LOCAL_DATE)); | ||
assertEquals(currentDate, d3); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@divijvaidya is correct that java.util.Date
is timezone-agnostic. However, the LocalDate
here is timezone-ignorant and the timezone offset is not accounted for. The methods available for manipulating dates and times with timezones are a bit limited, and I think that it's best to stop using LocalDate.ofEpochDay(long)
and moving to LocalDateTime.ofInstant(Instant, ZoneId)
.
I suggest replacing the first line of the method with:
LocalDateTime localTime = LocalDateTime.now();
LocalDateTime localTimeTruncated = localTime.truncatedTo(ChronoUnit.DAYS);
ZoneOffset zoneOffset = ZoneId.systemDefault().getRules().getOffset(localTime);
java.util.Date current = new java.util.Date(localTime.toEpochSecond(zoneOffset) * 1000);
And then the check becomes:
// ISO8601 strings - accept a string matching pattern "yyyy-MM-dd"
java.util.Date d3 = Values.convertToDate(Date.SCHEMA, LocalDate.ofEpochDay(days).format(DateTimeFormatter.ISO_LOCAL_DATE));
LocalDateTime date3 = LocalDateTime.ofInstant(Instant.ofEpochMilli(d3.getTime()), ZoneId.systemDefault());
assertEquals(localTimeTruncated, date3);
I tried this with various timezones locally and it seems to work properly. Essentially, the time information is discarded in a way that a person in the timezone would expect looking at a clock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can observe that when java.util.Date
performs equals comparison, it takes timezone into account. Therefore, the equals comparison will yield different results under different timezones. In my case, being in UTC+8, it results in an 8-hour difference.
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/Date.java#L1210
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@m1a2st Are you happy that this works now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, using LocalDateTime
makes sense to me.
…18611) Reviewers: Divij Vaidya <[email protected]>, Andrew Schofield <[email protected]>
…18611) Reviewers: Divij Vaidya <[email protected]>, Andrew Schofield <[email protected]>
…18611) Reviewers: Divij Vaidya <[email protected]>, Andrew Schofield <[email protected]>
|
||
// ISO8601 strings - accept a string matching pattern "yyyy-MM-dd" | ||
LocalDateTime localTimeTruncated = localTime.truncatedTo(ChronoUnit.DAYS); | ||
java.util.Date d3 = Values.convertToDate(Date.SCHEMA, LocalDate.ofEpochDay(days).format(DateTimeFormatter.ISO_LOCAL_DATE)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for raising this question on the merged PR. This test case is intended to ensure the parser works for ISO8601
. That said, is it really necessary to use days
here? It seems to me that relying on days
could introduce a risk: since the granularity is at the "day" level, it my case an off-by-one issue if the local time falls on a different "day" than UTC. For example, when the local time is 9/1 01:00 (UTC+8)
Perhaps we could instead use localTime.format(DateTimeFormatter.ISO_LOCAL_DATE)
to avoid the conversion risk
@AndrewJSchofield WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chia7712 Yes, I think you are totally right. There will be an off-by-one issue if the local time falls on a different day than UTC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Were you thinking something like this?
java.util.Date d3 = Values.convertToDate(Date.SCHEMA, localTime.format(DateTimeFormatter.ISO_LOCAL_DATE));
LocalDateTime date3 = LocalDateTime.ofInstant(Instant.ofEpochMilli(d3.getTime()), ZoneId.systemDefault());
LocalDateTime localTimeTruncated = localTime.truncatedTo(ChronoUnit.DAYS);
assertEquals(localTimeTruncated, date3);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes! we are on the same page!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a free cycle? If not, my mentee can file a minor patch based on your approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest the mentee does it and then I'll review. Thanks for following up on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test case ensures that the parser can convert ISO8601 correctly. However, when the local time falls on a different day than the UTC time, there will be an off-by-one issue. I changed the test to convert the local time and then compare it with the expected local time. This should fix the off-by-one issue. [Reference link](#18611 (comment)) Reviewers: Andrew Schofield <[email protected]> --------- Signed-off-by: Alex <[email protected]>
Jira: https://issues.apache.org/jira/browse/KAFKA-18585
The test scenario involves inputting dates in ISO8601 format
yyyy-MM-dd
. At this point, the time information is already lost. Then in theValues#convertToDate
method, the date conversion is performed using string manipulation, without any timezone information being provided. Therefore, it's expected and normal behavior that the time gets converted to 00:00:00.The solution is to explicitly convert Java Date objects, which internally store UTC timestamps, to UTC+0 timezone
before:


after:
Committer Checklist (excluded from commit message)