jdk9/10 reject zip/jar files where seconds value of timestamp is out of supported range 0 - 59
Claes Redestad
claes.redestad at oracle.com
Mon Oct 9 15:26:16 UTC 2017
On 2017-10-09 16:24, Stephen Colebourne wrote:
> On 9 October 2017 at 14:40, Claes Redestad <claes.redestad at oracle.com> wrote:
>> In addition to being a cleanup, the move to use java.time did provide a
>> speedup, however, which
>> might become significant when loading lots of jar files.
>>
>> I've not found my notes on how big this speed-up was (I recall ~3x in
>> micros), but if anyone has time
>> to fix (and test) the overflows without reverting to java.util.Date then we
>> might be able to keep most
>> of that gain intact. This can of course be considered a follow-up, or
>> ignored.
> In theory, this code should be equivalent, although I've not tested it:
>
> ```
> int year = (int) (((dtime >> 25) & 0x7f) + 1980);
> int month = (int) ((dtime >> 21) & 0x0f);
> int day = ((int) ((dtime >> 16) & 0x1f)) - 1;
> int hour = (int) ((dtime >> 11) & 0x1f);
> int min = (int) ((dtime >> 5) & 0x3f);
> int sec = (int) ((dtime << 1) & 0x3e);
> long secs = day * 86400 + hour * 3600 + min * 60 + sec;
>
> LocalDateTime ldt = LocalDateTime.of(year, month, 1).plusSeconds(secs);
>
> return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond(
> ZoneId.systemDefault().getRules().getOffset(ldt)),
> TimeUnit.SECONDS);
> ```
>
> It could be further enhanced if the day was known to be in range, but
> this code assumes that can't be guaranteed.
Chaining (of(..).plusSeconds(..)) possibly makes this slower than
using Date() during startup (which is often where ZipUtils.dosToJavaTime
might show up), and still doesn't deal with the cases where month is zero
(or 13, or 14, or 15), which JDK-8184940 dealt with partially.
>
> Another option would be to add a method `ofLenient(...)` to
> `LocalDate`, `LocalTime`, `LocalDateTime` etc, as the problem is a
> generally applicable one.
This might be the best option, long-term.
I was fiddling a bit with the idea of outlining odd cases, but since
the DOS format allows all manner of odd dates that a sane date
library deals with exceptionally(!) then I guess the best we could
do to maintain performance for the common cases now is
something like:
/**
* Converts DOS time to Java time (number of milliseconds since epoch).
*/
public static long dosToJavaTime(long dtime) {
int year = (int) (((dtime >> 25) & 0x7f) + 1980);
int month = (int) ((dtime >> 21) & 0x0f);
int day = (int) ((dtime >> 16) & 0x1f);
int hour = (int) ((dtime >> 11) & 0x1f);
int minute = (int) ((dtime >> 5) & 0x3f);
int second = (int) ((dtime << 1) & 0x3e);
try {
LocalDateTime ldt = LocalDateTime.of(year, month, day,
hour, minute, second);
return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond(
ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS);
} catch (DateTimeException dte) {
return overflowDosToJavaTime(year, month, day, hour,
minute, second);
}
}
/**
* Deal with corner cases where an arguably mal-formed DOS time is used
*/
@SuppressWarnings("deprecation") // Use of date constructor
private static long overflowDosToJavaTime(int year, int month, int
day, int hour, int minute, int second) {
return new Date(year - 1900, month - 1, day, hour, minute,
second).getTime();
}
/Claes
More information about the core-libs-dev
mailing list