diff --git a/src/java/org/apache/poi/hpsf/Filetime.java b/src/java/org/apache/poi/hpsf/Filetime.java index 42d864d23..98b2c27c6 100644 --- a/src/java/org/apache/poi/hpsf/Filetime.java +++ b/src/java/org/apache/poi/hpsf/Filetime.java @@ -16,15 +16,23 @@ ==================================================================== */ package org.apache.poi.hpsf; +import static org.apache.poi.util.LittleEndianConsts.LONG_SIZE; + import java.io.IOException; import java.io.OutputStream; +import java.math.BigInteger; import java.util.Date; import org.apache.poi.util.Internal; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianByteArrayInputStream; -import org.apache.poi.util.LittleEndianConsts; +/** + * The Windows FILETIME structure holds a date and time associated with a + * file. The structure identifies a 64-bit integer specifying the + * number of 100-nanosecond intervals which have passed since + * January 1, 1601, Coordinated Universal Time (UTC). + */ @Internal public class Filetime { /** @@ -32,74 +40,47 @@ public class Filetime { * 00:00:00) and the Unix epoch (1970-01-01 00:00:00) in * milliseconds. */ - private static final long EPOCH_DIFF = -11644473600000L; + private static final BigInteger EPOCH_DIFF = BigInteger.valueOf(-11_644_473_600_000L); - private static final int SIZE = LittleEndian.INT_SIZE * 2; - private static final long UINT_MASK = 0x00000000FFFFFFFFL; - private static final long NANO_100 = 1000L * 10L; + /** Factor between filetime long and date milliseconds */ + private static final BigInteger NANO_100 = BigInteger.valueOf(10_000L); - private int _dwHighDateTime; - private int _dwLowDateTime; + private long fileTime; public Filetime() {} - public Filetime( int low, int high ) { - _dwLowDateTime = low; - _dwHighDateTime = high; - } - public Filetime( Date date ) { - long filetime = Filetime.dateToFileTime(date); - _dwHighDateTime = (int) ((filetime >>> 32) & UINT_MASK); - _dwLowDateTime = (int) (filetime & UINT_MASK); + fileTime = dateToFileTime(date); } - public void read( LittleEndianByteArrayInputStream lei ) { - _dwLowDateTime = lei.readInt(); - _dwHighDateTime = lei.readInt(); - } - - public long getHigh() { - return _dwHighDateTime; - } - - public long getLow() { - return _dwLowDateTime; + fileTime = lei.readLong(); } public byte[] toByteArray() { - byte[] result = new byte[SIZE]; - LittleEndian.putInt( result, 0 * LittleEndianConsts.INT_SIZE, _dwLowDateTime ); - LittleEndian.putInt( result, 1 * LittleEndianConsts.INT_SIZE, _dwHighDateTime ); + byte[] result = new byte[LONG_SIZE]; + LittleEndian.putLong( result, 0, fileTime); return result; } public int write( OutputStream out ) throws IOException { - LittleEndian.putInt( _dwLowDateTime, out ); - LittleEndian.putInt( _dwHighDateTime, out ); - return SIZE; + out.write(toByteArray()); + return LONG_SIZE; } public Date getJavaValue() { - long l = (((long)_dwHighDateTime) << 32) | (_dwLowDateTime & UINT_MASK); - return filetimeToDate( l ); + return filetimeToDate( fileTime ); } /** - * Converts a Windows FILETIME into a {@link Date}. The Windows - * FILETIME structure holds a date and time associated with a - * file. The structure identifies a 64-bit integer specifying the - * number of 100-nanosecond intervals which have passed since - * January 1, 1601. + * Converts a Windows FILETIME (in UTC) into a {@link Date} (in UTC). * * @param filetime The filetime to convert. * @return The Windows FILETIME as a {@link Date}. */ public static Date filetimeToDate(final long filetime) { - final long ms_since_16010101 = filetime / NANO_100; - final long ms_since_19700101 = ms_since_16010101 + EPOCH_DIFF; - return new Date(ms_since_19700101); + final BigInteger bi = (filetime < 0) ? twoComplement(filetime) : BigInteger.valueOf(filetime); + return new Date(bi.divide(NANO_100).add(EPOCH_DIFF).longValue()); } /** @@ -111,9 +92,7 @@ public class Filetime { * @see #filetimeToDate(long) */ public static long dateToFileTime(final Date date) { - long ms_since_19700101 = date.getTime(); - long ms_since_16010101 = ms_since_19700101 - EPOCH_DIFF; - return ms_since_16010101 * NANO_100; + return BigInteger.valueOf(date.getTime()).subtract(EPOCH_DIFF).multiply(NANO_100).longValue(); } /** @@ -125,4 +104,21 @@ public class Filetime { public static boolean isUndefined(Date date) { return (date == null || dateToFileTime(date) == 0); } + + private static BigInteger twoComplement(final long val) { + // for negative BigInteger, top byte is negative + final byte[] contents = { + (byte)(val < 0 ? 0 : -1), + (byte)((val >> 56) & 0xFF), + (byte)((val >> 48) & 0xFF), + (byte)((val >> 40) & 0xFF), + (byte)((val >> 32) & 0xFF), + (byte)((val >> 24) & 0xFF), + (byte)((val >> 16) & 0xFF), + (byte)((val >> 8) & 0xFF), + (byte)(val & 0xFF), + }; + + return new BigInteger(contents); + } } diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestHPSFBugs.java b/src/testcases/org/apache/poi/hpsf/basic/TestHPSFBugs.java index 0600ffea9..2fff500a0 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestHPSFBugs.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestHPSFBugs.java @@ -170,4 +170,21 @@ public final class TestHPSFBugs { fs.close(); } + + @Test + public void bug62451() throws IOException { + final long millis = 920355314183864L; + try (HSSFWorkbook wb = new HSSFWorkbook()) { + wb.createSheet().createRow(0).createCell(0).setCellValue("foo"); + wb.createInformationProperties(); + SummaryInformation si = wb.getSummaryInformation(); + si.setLastPrinted(new Date(millis)); + try (HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb)) { + SummaryInformation si2 = wb2.getSummaryInformation(); + Date d = si.getLastPrinted(); + assertNotNull(d); + assertEquals(millis, d.getTime()); + } + } + } }