Hi,
I looked into this issue and added comments to the issue tracker.
8308302 <https://bugs.openjdk.org/browse/JDK-8308302> Leap second
handling in Windows timestamps
I don't see an issue with the implementation for past leap-seconds and
there is significant discussion about whether if or when there will be
another one.
Until another leap-second is declared, I don't see any immediate followup.
Regards, Roger
On 5/17/23 1:19 PM, Roger Riggs wrote:
Hi,
Thanks for the report, I created a Jira issue to track the investigation.
https://bugs.openjdk.org/browse/JDK-8308302
Regards, Roger
On 5/13/23 2:43 PM, andr...@flueckiger.ch wrote:
Subject:
Leap second handling in Windows timestamps
From:
<andr...@flueckiger.ch>
Date:
5/13/23, 2:43 PM
To:
<core-libs-dev@openjdk.org>
Hello,
This is my first post to this mailing list. I've been exploring a problem
concerning leap seconds that emerged with the Windows 10 October 2018 Update.
The current implementation of InstantSource and other classes that interact
with FILETIME structures seem to be affected. This problem extends beyond just
leap second days and will occur on any future day where the UTC-TAI offset
deviates from 37 seconds.
The Java code snippet below, which uses JNA to convert a Windows FILETIME to an
Instant, represents my initial attempt to address this issue. This approach
makes the assumption that no more than one leap second is added or removed in a
day, which should hold true until at least 2035, and likely a few years beyond.
I'm not sure how this will impact performance, and I'm not certain about the
exact performance requirements. Also, I'm not sure if my current level of
experience and permissions allow me to contribute directly to the JDK codebase.
Still, I hope this code can provide some direction towards refining the
handling of Windows timestamps.
Kind regards,
Andreas
private static Instant fileTimeToInstant(long fileTime) {
if (fileTime < 0L) {
throw new DateTimeException("file time must not be negative");
}
// Calculate nano adjustment and round down fileTime to the nearest second
int nanoAdjustment = (int) (fileTime % 10000000L);
fileTime -= nanoAdjustment;
nanoAdjustment *= 100;
// Populate FILETIME structure
FILETIME fileTimeStruct = new FILETIME();
fileTimeStruct.dwHighDateTime = (int) (fileTime >>> 32);
fileTimeStruct.dwLowDateTime = (int) (fileTime & 0xffffffffL);
// Convert FILETIME structure to a SYSTEMTIME structure
SYSTEMTIME systemTime = new SYSTEMTIME();
if (!Kernel32.INSTANCE.FileTimeToSystemTime(fileTimeStruct, systemTime)) {
throw new LastErrorException(Native.getLastError());
}
// Calculate epoch day and second of day
long epochDay = LocalDate.of(systemTime.wYear, systemTime.wMonth,
systemTime.wDay).toEpochDay();
int secondOfDay = systemTime.wHour * 3600 + systemTime.wMinute * 60 +
systemTime.wSecond;
// Calculate UTC-SLS slew if necessary and only for dates before December 31,
// 30827 (epochDay < 10540167). SystemTimeToFileTime does not support dates
// after the year 30827.
if (secondOfDay >= 85399 && epochDay < 10540167) {
// If the actual second of the day is 86400 (leap second) and the process
is in
// "compatible mode", increment the secondOfDay variable. In compatible
mode,
// the clock slows down to half speed for two seconds at the '59' second
mark,
// and systemTime.wMilliseconds reaches 500 at the beginning of the leap
second.
// This ensures that the leap second is properly accounted for without
depending
// on the ProcessLeapSecondInfo option. Rounding down fileTime to the
nearest
// second ensures that this check works as intended.
if (secondOfDay == 86399 && systemTime.wMilliseconds == 500) {
secondOfDay++;
}
// Calculate leap adjustment
int leapAdjustment;
if (secondOfDay == 86400) {
// In case of a leap second, set leap adjustment to 1
// to avoid unnecessary further calculations
leapAdjustment = 1;
} else {
// If it's not a leap second, calculate leap adjustment by
// determining the difference to the beginning of the next day
LocalDate nextDay = LocalDate.ofEpochDay(epochDay + 1);
systemTime.wYear = (short) nextDay.getYear();
systemTime.wMonth = (short) nextDay.getMonthValue();
systemTime.wDay = (short) nextDay.getDayOfMonth();
systemTime.wHour = 0;
systemTime.wMinute = 0;
systemTime.wSecond = 0;
systemTime.wMilliseconds = 0;
if (!Kernel32.INSTANCE.SystemTimeToFileTime(systemTime, fileTimeStruct))
{
throw new LastErrorException(Native.getLastError());
}
long nextDayFileTime = (((long) fileTimeStruct.dwHighDateTime) << 32) |
(fileTimeStruct.dwLowDateTime & 0xffffffffL);
leapAdjustment = (int) ((nextDayFileTime - fileTime) / 10000000L +
secondOfDay - 86400L);
}
// Adjust nanoseconds based on leap adjustment
if (leapAdjustment != 0 && secondOfDay - leapAdjustment >= 85400) {
nanoAdjustment -= ((secondOfDay - leapAdjustment - 85400) * 1000000000L
+ nanoAdjustment) * leapAdjustment / 1000L;
}
}
return Instant.ofEpochSecond(epochDay * 86400L + secondOfDay,
nanoAdjustment);
}