Leap second handling in Windows timestamps
Roger Riggs
roger.riggs at oracle.com
Wed May 17 17:19:46 UTC 2023
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, andreas at flueckiger.ch wrote:
> Subject:
> Leap second handling in Windows timestamps
> From:
> <andreas at flueckiger.ch>
> Date:
> 5/13/23, 2:43 PM
>
> To:
> <core-libs-dev at 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);
> }
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20230517/8713b8d0/attachment.htm>
More information about the core-libs-dev
mailing list