[bug-69583] DateUtil needs to handle time only dates (issue with 1900 format dates)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1923785 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2025-02-13 12:34:38 +00:00
parent 8ad10c0ed7
commit 694765368e
2 changed files with 89 additions and 68 deletions

View File

@ -220,8 +220,12 @@ public class DateUtil {
return internalGetExcelDate(year, dayOfYear, hour, minute, second, milliSecond, use1904windowing); return internalGetExcelDate(year, dayOfYear, hour, minute, second, milliSecond, use1904windowing);
} }
private static boolean isLastDay1899(final int year, final int dayOfYear) {
return year == 1899 && dayOfYear == 365;
}
private static double internalGetExcelDate(int year, int dayOfYear, int hour, int minute, int second, int milliSecond, boolean use1904windowing) { private static double internalGetExcelDate(int year, int dayOfYear, int hour, int minute, int second, int milliSecond, boolean use1904windowing) {
if ((!use1904windowing && year < 1900) || if ((!use1904windowing && (year < 1900 && !isLastDay1899(year, dayOfYear))) ||
(use1904windowing && year < 1904)) (use1904windowing && year < 1904))
{ {
return BAD_DATE; return BAD_DATE;
@ -866,7 +870,7 @@ public class DateUtil {
static int daysInPriorYears(int yr, boolean use1904windowing) static int daysInPriorYears(int yr, boolean use1904windowing)
{ {
if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1904)) { if ((!use1904windowing && yr < 1899) || (use1904windowing && yr < 1904)) {
throw new IllegalArgumentException("'year' must be 1900 or greater"); throw new IllegalArgumentException("'year' must be 1900 or greater");
} }

View File

@ -183,6 +183,7 @@ class TestDateUtil {
// Cell show "2016年12月8日" // Cell show "2016年12月8日"
assertTrue(DateUtil.isADateFormat(178, "[DBNum3][$-804]yyyy\"\u5e74\"m\"\u6708\"d\"\u65e5\";@")); assertTrue(DateUtil.isADateFormat(178, "[DBNum3][$-804]yyyy\"\u5e74\"m\"\u6708\"d\"\u65e5\";@"));
} }
/** /**
* Checks the date conversion functions in the DateUtil class. * Checks the date conversion functions in the DateUtil class.
*/ */
@ -190,16 +191,16 @@ class TestDateUtil {
void dateConversion() { void dateConversion() {
// Iterating over the hours exposes any rounding issues. // Iterating over the hours exposes any rounding issues.
Calendar cal = LocaleUtil.getLocaleCalendar(2002,JANUARY,1,0,1,1); Calendar cal = LocaleUtil.getLocaleCalendar(2002, JANUARY, 1, 0, 1, 1);
for (int hour = 0; hour < 24; hour++) { for (int hour = 0; hour < 24; hour++) {
double excelDate = DateUtil.getExcelDate(cal.getTime(), false); double excelDate = DateUtil.getExcelDate(cal.getTime(), false);
assertEquals(cal.getTime().getTime(), DateUtil.getJavaDate(excelDate, false).getTime(), assertEquals(cal.getTime().getTime(), DateUtil.getJavaDate(excelDate, false).getTime(),
"getJavaDate: Checking hour = " + hour); "getJavaDate: Checking hour = " + hour);
LocalDateTime ldt = LocalDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId()); LocalDateTime ldt = LocalDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId());
assertEquals(ldt, DateUtil.getLocalDateTime(excelDate, false), assertEquals(ldt, DateUtil.getLocalDateTime(excelDate, false),
"getLocalDateTime: Checking hour = " + hour); "getLocalDateTime: Checking hour = " + hour);
cal.add(Calendar.HOUR_OF_DAY, 1); cal.add(Calendar.HOUR_OF_DAY, 1);
} }
@ -208,23 +209,23 @@ class TestDateUtil {
double excelDate = 36526.0; double excelDate = 36526.0;
// with 1900 windowing, excelDate is Jan. 1, 2000 // with 1900 windowing, excelDate is Jan. 1, 2000
// with 1904 windowing, excelDate is Jan. 2, 2004 // with 1904 windowing, excelDate is Jan. 2, 2004
cal.set(2000,JANUARY,1,0,0,0); // Jan. 1, 2000 cal.set(2000, JANUARY, 1, 0, 0, 0); // Jan. 1, 2000
Date dateIf1900 = cal.getTime(); Date dateIf1900 = cal.getTime();
cal.add(Calendar.YEAR,4); // now Jan. 1, 2004 cal.add(Calendar.YEAR, 4); // now Jan. 1, 2004
cal.add(Calendar.DATE,1); // now Jan. 2, 2004 cal.add(Calendar.DATE, 1); // now Jan. 2, 2004
Date dateIf1904 = cal.getTime(); Date dateIf1904 = cal.getTime();
// 1900 windowing // 1900 windowing
assertEquals(dateIf1900.getTime(), DateUtil.getJavaDate(excelDate,false).getTime(), assertEquals(dateIf1900.getTime(), DateUtil.getJavaDate(excelDate, false).getTime(),
"Checking 1900 Date Windowing"); "Checking 1900 Date Windowing");
// 1904 windowing // 1904 windowing
assertEquals(dateIf1904.getTime(), DateUtil.getJavaDate(excelDate,true).getTime(), assertEquals(dateIf1904.getTime(), DateUtil.getJavaDate(excelDate, true).getTime(),
"Checking 1904 Date Windowing"); "Checking 1904 Date Windowing");
// 1900 windowing (LocalDateTime) // 1900 windowing (LocalDateTime)
assertEquals(LocalDateTime.of(2000,1,1,0,0), DateUtil.getLocalDateTime(excelDate,false), assertEquals(LocalDateTime.of(2000, 1, 1, 0, 0), DateUtil.getLocalDateTime(excelDate, false),
"Checking 1900 Date Windowing"); "Checking 1900 Date Windowing");
// 1904 windowing (LocalDateTime) // 1904 windowing (LocalDateTime)
assertEquals(LocalDateTime.of(2004,1,2,0,0), DateUtil.getLocalDateTime(excelDate,true), assertEquals(LocalDateTime.of(2004, 1, 2, 0, 0), DateUtil.getLocalDateTime(excelDate, true),
"Checking 1904 Date Windowing"); "Checking 1904 Date Windowing");
} }
/** /**
@ -233,7 +234,7 @@ class TestDateUtil {
*/ */
@Test @Test
void excelConversionOnDSTStart() { void excelConversionOnDSTStart() {
Calendar cal = LocaleUtil.getLocaleCalendar(2004,MARCH,28,0,0,0); Calendar cal = LocaleUtil.getLocaleCalendar(2004, MARCH, 28, 0, 0, 0);
for (int hour = 0; hour < 24; hour++) { for (int hour = 0; hour < 24; hour++) {
// Skip 02:00 CET as that is the Daylight change time // Skip 02:00 CET as that is the Daylight change time
@ -250,18 +251,18 @@ class TestDateUtil {
assertEquals(hour, differenceInHours, "Checking " + hour + " hour on Daylight Saving Time start date"); assertEquals(hour, differenceInHours, "Checking " + hour + " hour on Daylight Saving Time start date");
assertEquals(javaDate.getTime(), DateUtil.getJavaDate(excelDate, false).getTime(), assertEquals(javaDate.getTime(), DateUtil.getJavaDate(excelDate, false).getTime(),
"Checking " + hour + " hour on Daylight Saving Time start date"); "Checking " + hour + " hour on Daylight Saving Time start date");
// perform the same checks with LocalDateTime // perform the same checks with LocalDateTime
LocalDateTime localDate = LocalDateTime.of(2004,3,28,hour,0,0); LocalDateTime localDate = LocalDateTime.of(2004, 3, 28, hour, 0, 0);
double excelLocalDate = DateUtil.getExcelDate(localDate, false); double excelLocalDate = DateUtil.getExcelDate(localDate, false);
double differenceLocalDate = excelLocalDate - Math.floor(excelLocalDate); double differenceLocalDate = excelLocalDate - Math.floor(excelLocalDate);
int differenceLocalDateInHours = (int) (differenceLocalDate * 24 * 60 + 0.5) / 60; int differenceLocalDateInHours = (int) (differenceLocalDate * 24 * 60 + 0.5) / 60;
assertEquals(hour, differenceLocalDateInHours, assertEquals(hour, differenceLocalDateInHours,
"Checking " + hour + " hour on Daylight Saving Time start date (LocalDateTime)"); "Checking " + hour + " hour on Daylight Saving Time start date (LocalDateTime)");
assertEquals(localDate, DateUtil.getLocalDateTime(excelLocalDate, false), assertEquals(localDate, DateUtil.getLocalDateTime(excelLocalDate, false),
"Checking " + hour + " hour on Daylight Saving Time start date (LocalDateTime)"); "Checking " + hour + " hour on Daylight Saving Time start date (LocalDateTime)");
} }
} }
@ -271,7 +272,7 @@ class TestDateUtil {
*/ */
@Test @Test
void javaConversionOnDSTStart() { void javaConversionOnDSTStart() {
Calendar cal = LocaleUtil.getLocaleCalendar(2004,MARCH,28,0,0,0); Calendar cal = LocaleUtil.getLocaleCalendar(2004, MARCH, 28, 0, 0, 0);
double excelDate = DateUtil.getExcelDate(cal.getTime(), false); double excelDate = DateUtil.getExcelDate(cal.getTime(), false);
double oneHour = 1.0 / 24; double oneHour = 1.0 / 24;
double oneMinute = oneHour / 60; double oneMinute = oneHour / 60;
@ -287,14 +288,14 @@ class TestDateUtil {
Date javaDate = DateUtil.getJavaDate(excelDate, false); Date javaDate = DateUtil.getJavaDate(excelDate, false);
double actDate = DateUtil.getExcelDate(javaDate, false); double actDate = DateUtil.getExcelDate(javaDate, false);
assertEquals(excelDate, actDate, oneMinute, assertEquals(excelDate, actDate, oneMinute,
"Checking " + hour + " hours on Daylight Saving Time start date"); "Checking " + hour + " hours on Daylight Saving Time start date");
// perform the same check with LocalDateTime // perform the same check with LocalDateTime
cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.HOUR_OF_DAY, hour);
LocalDateTime localDate = DateUtil.getLocalDateTime(excelDate, false); LocalDateTime localDate = DateUtil.getLocalDateTime(excelDate, false);
double actLocalDate = DateUtil.getExcelDate(localDate, false); double actLocalDate = DateUtil.getExcelDate(localDate, false);
assertEquals(excelDate, actLocalDate, oneMinute, assertEquals(excelDate, actLocalDate, oneMinute,
"Checking " + hour + " hours on Daylight Saving Time start date (LocalDateTime)"); "Checking " + hour + " hours on Daylight Saving Time start date (LocalDateTime)");
} }
} }
@ -304,7 +305,7 @@ class TestDateUtil {
*/ */
@Test @Test
void excelConversionOnDSTEnd() { void excelConversionOnDSTEnd() {
Calendar cal = LocaleUtil.getLocaleCalendar(2004,OCTOBER,31,0,0,0); Calendar cal = LocaleUtil.getLocaleCalendar(2004, OCTOBER, 31, 0, 0, 0);
for (int hour = 0; hour < 24; hour++) { for (int hour = 0; hour < 24; hour++) {
cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.HOUR_OF_DAY, hour);
Date javaDate = cal.getTime(); Date javaDate = cal.getTime();
@ -312,18 +313,18 @@ class TestDateUtil {
double difference = excelDate - Math.floor(excelDate); double difference = excelDate - Math.floor(excelDate);
int differenceInHours = (int) (difference * 24 * 60 + 0.5) / 60; int differenceInHours = (int) (difference * 24 * 60 + 0.5) / 60;
assertEquals(hour, differenceInHours, assertEquals(hour, differenceInHours,
"Checking " + hour + " hour on Daylight Saving Time end date"); "Checking " + hour + " hour on Daylight Saving Time end date");
assertEquals(javaDate.getTime(), DateUtil.getJavaDate(excelDate, false).getTime(), assertEquals(javaDate.getTime(), DateUtil.getJavaDate(excelDate, false).getTime(),
"Checking " + hour + " hour on Daylight Saving Time start date"); "Checking " + hour + " hour on Daylight Saving Time start date");
// perform the same checks using LocalDateTime // perform the same checks using LocalDateTime
LocalDateTime localDate = LocalDateTime.of(2004,10,31,hour,0,0); LocalDateTime localDate = LocalDateTime.of(2004, 10, 31, hour, 0, 0);
double excelLocalDate = DateUtil.getExcelDate(localDate, false); double excelLocalDate = DateUtil.getExcelDate(localDate, false);
int differenceLocalDateInHours = (int) (difference * 24 * 60 + 0.5) / 60; int differenceLocalDateInHours = (int) (difference * 24 * 60 + 0.5) / 60;
assertEquals(hour, differenceLocalDateInHours, assertEquals(hour, differenceLocalDateInHours,
"Checking " + hour + " hour on Daylight Saving Time end date (LocalDateTime)"); "Checking " + hour + " hour on Daylight Saving Time end date (LocalDateTime)");
assertEquals(localDate, DateUtil.getLocalDateTime(excelLocalDate, false), assertEquals(localDate, DateUtil.getLocalDateTime(excelLocalDate, false),
"Checking " + hour + " hour on Daylight Saving Time start date (LocalDateTime)"); "Checking " + hour + " hour on Daylight Saving Time start date (LocalDateTime)");
} }
} }
@ -333,7 +334,7 @@ class TestDateUtil {
*/ */
@Test @Test
void javaConversionOnDSTEnd() { void javaConversionOnDSTEnd() {
Calendar cal = LocaleUtil.getLocaleCalendar(2004,OCTOBER,31,0,0,0); Calendar cal = LocaleUtil.getLocaleCalendar(2004, OCTOBER, 31, 0, 0, 0);
double excelDate = DateUtil.getExcelDate(cal.getTime(), false); double excelDate = DateUtil.getExcelDate(cal.getTime(), false);
double oneHour = 1.0 / 24; double oneHour = 1.0 / 24;
double oneMinute = oneHour / 60; double oneMinute = oneHour / 60;
@ -341,12 +342,12 @@ class TestDateUtil {
cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.HOUR_OF_DAY, hour);
Date javaDate = DateUtil.getJavaDate(excelDate, false); Date javaDate = DateUtil.getJavaDate(excelDate, false);
assertEquals(excelDate, DateUtil.getExcelDate(javaDate, false), oneMinute, assertEquals(excelDate, DateUtil.getExcelDate(javaDate, false), oneMinute,
"Checking " + hour + " hours on Daylight Saving Time start date"); "Checking " + hour + " hours on Daylight Saving Time start date");
// perform the same checks using LocalDateTime // perform the same checks using LocalDateTime
LocalDateTime localDate = DateUtil.getLocalDateTime(excelDate, false); LocalDateTime localDate = DateUtil.getLocalDateTime(excelDate, false);
assertEquals(excelDate, DateUtil.getExcelDate(localDate, false), oneMinute, assertEquals(excelDate, DateUtil.getExcelDate(localDate, false), oneMinute,
"Checking " + hour + " hours on Daylight Saving Time start date"); "Checking " + hour + " hours on Daylight Saving Time start date");
} }
} }
@ -358,12 +359,11 @@ class TestDateUtil {
TimeZone userTZ = LocaleUtil.getUserTimeZone(); TimeZone userTZ = LocaleUtil.getUserTimeZone();
LocaleUtil.setUserTimeZone(TimeZone.getTimeZone("CET")); LocaleUtil.setUserTimeZone(TimeZone.getTimeZone("CET"));
try { try {
Calendar cal = LocaleUtil.getLocaleCalendar(2002,JANUARY,1,12,1,1); Calendar cal = LocaleUtil.getLocaleCalendar(2002, JANUARY, 1, 12, 1, 1);
Date expected = cal.getTime(); Date expected = cal.getTime();
// Iterating over the hours exposes any rounding issues. // Iterating over the hours exposes any rounding issues.
for (int hour = -12; hour <= 12; hour++) for (int hour = -12; hour <= 12; hour++) {
{
String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00"; String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00";
cal.setTimeZone(TimeZone.getTimeZone(id)); cal.setTimeZone(TimeZone.getTimeZone(id));
cal.set(Calendar.HOUR_OF_DAY, 12); cal.set(Calendar.HOUR_OF_DAY, 12);
@ -383,14 +383,14 @@ class TestDateUtil {
// Same, no change // Same, no change
assertEquals( assertEquals(
DateUtil.getJavaDate(excelDate, false).getTime(), DateUtil.getJavaDate(excelDate, false).getTime(),
DateUtil.getJavaDate(excelDate, false, cet).getTime() DateUtil.getJavaDate(excelDate, false, cet).getTime()
); );
// London vs Copenhagen, should differ by an hour // London vs Copenhagen, should differ by an hour
Date cetDate = DateUtil.getJavaDate(excelDate, false); Date cetDate = DateUtil.getJavaDate(excelDate, false);
Date ldnDate = DateUtil.getJavaDate(excelDate, false, ldn); Date ldnDate = DateUtil.getJavaDate(excelDate, false, ldn);
assertEquals(ldnDate.getTime() - cetDate.getTime(), 60*60*1000); assertEquals(ldnDate.getTime() - cetDate.getTime(), 60 * 60 * 1000);
} finally { } finally {
LocaleUtil.setUserTimeZone(userTZ); LocaleUtil.setUserTimeZone(userTZ);
} }
@ -402,19 +402,19 @@ class TestDateUtil {
@Test @Test
void identifyDateFormats() { void identifyDateFormats() {
// First up, try with a few built in date formats // First up, try with a few built in date formats
short[] builtins = new short[] { 0x0e, 0x0f, 0x10, 0x16, 0x2d, 0x2e }; short[] builtins = new short[]{0x0e, 0x0f, 0x10, 0x16, 0x2d, 0x2e};
for (short builtin : builtins) { for (short builtin : builtins) {
String formatStr = HSSFDataFormat.getBuiltinFormat(builtin); String formatStr = HSSFDataFormat.getBuiltinFormat(builtin);
assertTrue( DateUtil.isInternalDateFormat(builtin) ); assertTrue(DateUtil.isInternalDateFormat(builtin));
assertTrue( DateUtil.isADateFormat(builtin,formatStr) ); assertTrue(DateUtil.isADateFormat(builtin, formatStr));
} }
// Now try a few built-in non date formats // Now try a few built-in non date formats
builtins = new short[] { 0x01, 0x02, 0x17, 0x1f, 0x30 }; builtins = new short[]{0x01, 0x02, 0x17, 0x1f, 0x30};
for (short builtin : builtins) { for (short builtin : builtins) {
String formatStr = HSSFDataFormat.getBuiltinFormat(builtin); String formatStr = HSSFDataFormat.getBuiltinFormat(builtin);
assertFalse( DateUtil.isInternalDateFormat(builtin) ); assertFalse(DateUtil.isInternalDateFormat(builtin));
assertFalse( DateUtil.isADateFormat(builtin,formatStr) ); assertFalse(DateUtil.isADateFormat(builtin, formatStr));
} }
// Now for some non-internal ones // Now for some non-internal ones
@ -422,10 +422,10 @@ class TestDateUtil {
int numBuiltins = HSSFDataFormat.getNumberOfBuiltinBuiltinFormats(); int numBuiltins = HSSFDataFormat.getNumberOfBuiltinBuiltinFormats();
assertTrue(numBuiltins < 60); assertTrue(numBuiltins < 60);
short formatId = 60; short formatId = 60;
assertFalse( DateUtil.isInternalDateFormat(formatId) ); assertFalse(DateUtil.isInternalDateFormat(formatId));
// Valid ones first // Valid ones first
String[] formats = new String[] { String[] formats = new String[]{
"yyyy-mm-dd", "yyyy/mm/dd", "yy/mm/dd", "yy/mmm/dd", "yyyy-mm-dd", "yyyy/mm/dd", "yy/mm/dd", "yy/mmm/dd",
"dd/mm/yy", "dd/mm/yyyy", "dd/mmm/yy", "dd/mm/yy", "dd/mm/yyyy", "dd/mmm/yy",
"dd-mm-yy", "dd-mm-yyyy", "dd-mm-yy", "dd-mm-yyyy",
@ -453,7 +453,7 @@ class TestDateUtil {
} }
// Then time based ones too // Then time based ones too
formats = new String[] { formats = new String[]{
"yyyy-mm-dd hh:mm:ss", "yyyy/mm/dd HH:MM:SS", "yyyy-mm-dd hh:mm:ss", "yyyy/mm/dd HH:MM:SS",
"mm/dd HH:MM", "yy/mmm/dd SS", "mm/dd HH:MM", "yy/mmm/dd SS",
"mm/dd HH:MM AM", "mm/dd HH:MM am", "mm/dd HH:MM AM", "mm/dd HH:MM am",
@ -468,7 +468,7 @@ class TestDateUtil {
} }
// Then invalid ones // Then invalid ones
formats = new String[] { formats = new String[]{
"yyyy*mm*dd", "yyyy*mm*dd",
"0.0", "0.000", "0.0", "0.000",
"0%", "0.0%", "0%", "0.0%",
@ -521,13 +521,13 @@ class TestDateUtil {
assertEquals(38074.00, DateUtil.getExcelDate(createDate(2004, MARCH, 28), false), 0.00001); assertEquals(38074.00, DateUtil.getExcelDate(createDate(2004, MARCH, 28), false), 0.00001);
// perform the same checks using LocalDateTime // perform the same checks using LocalDateTime
assertEquals(59.0, DateUtil.getExcelDate(LocalDateTime.of(1900, 2, 28, 0,0), false), 0.00001); assertEquals(59.0, DateUtil.getExcelDate(LocalDateTime.of(1900, 2, 28, 0, 0), false), 0.00001);
assertEquals(61.0, DateUtil.getExcelDate(LocalDateTime.of(1900, 3, 1, 0,0), false), 0.00001); assertEquals(61.0, DateUtil.getExcelDate(LocalDateTime.of(1900, 3, 1, 0, 0), false), 0.00001);
assertEquals(37315.00, DateUtil.getExcelDate(LocalDateTime.of(2002, 2, 28, 0,0), false), 0.00001); assertEquals(37315.00, DateUtil.getExcelDate(LocalDateTime.of(2002, 2, 28, 0, 0), false), 0.00001);
assertEquals(37316.00, DateUtil.getExcelDate(LocalDateTime.of(2002, 3, 1, 0,0), false), 0.00001); assertEquals(37316.00, DateUtil.getExcelDate(LocalDateTime.of(2002, 3, 1, 0, 0), false), 0.00001);
assertEquals(37257.00, DateUtil.getExcelDate(LocalDateTime.of(2002, 1, 1, 0,0), false), 0.00001); assertEquals(37257.00, DateUtil.getExcelDate(LocalDateTime.of(2002, 1, 1, 0, 0), false), 0.00001);
assertEquals(38074.00, DateUtil.getExcelDate(LocalDateTime.of(2004, 3, 28, 0,0), false), 0.00001); assertEquals(38074.00, DateUtil.getExcelDate(LocalDateTime.of(2004, 3, 28, 0, 0), false), 0.00001);
} }
@Test @Test
@ -578,7 +578,7 @@ class TestDateUtil {
/** /**
* @param month zero based * @param month zero based
* @param day one based * @param day one based
*/ */
private static Date createDate(int year, int month, int day) { private static Date createDate(int year, int month, int day) {
return createDate(year, month, day, 0, 0); return createDate(year, month, day, 0, 0);
@ -586,7 +586,7 @@ class TestDateUtil {
/** /**
* @param month zero based * @param month zero based
* @param day one based * @param day one based
*/ */
private static Date createDate(int year, int month, int day, int hour, int minute) { private static Date createDate(int year, int month, int day, int hour, int minute) {
Calendar c = LocaleUtil.getLocaleCalendar(year, month, day, hour, minute, 0); Calendar c = LocaleUtil.getLocaleCalendar(year, month, day, hour, minute, 0);
@ -599,27 +599,27 @@ class TestDateUtil {
@Test @Test
void absoluteDay() { void absoluteDay() {
// 1 Jan 1900 is 1 day after 31 Dec 1899 // 1 Jan 1900 is 1 day after 31 Dec 1899
Calendar cal = LocaleUtil.getLocaleCalendar(1900,JANUARY,1,0,0,0); Calendar cal = LocaleUtil.getLocaleCalendar(1900, JANUARY, 1, 0, 0, 0);
assertEquals(1, DateUtil.absoluteDay(cal, false), "Checking absolute day (1 Jan 1900)"); assertEquals(1, DateUtil.absoluteDay(cal, false), "Checking absolute day (1 Jan 1900)");
LocalDateTime ldt = LocalDateTime.of(1900,1,1,0,0,0); LocalDateTime ldt = LocalDateTime.of(1900, 1, 1, 0, 0, 0);
assertEquals(1, DateUtil.absoluteDay(ldt, false), "Checking absolute day (1 Jan 1900) (LocalDateTime)"); assertEquals(1, DateUtil.absoluteDay(ldt, false), "Checking absolute day (1 Jan 1900) (LocalDateTime)");
// 1 Jan 1901 is 366 days after 31 Dec 1899 // 1 Jan 1901 is 366 days after 31 Dec 1899
ldt = LocalDateTime.of(1901,1,1,0,0,0); ldt = LocalDateTime.of(1901, 1, 1, 0, 0, 0);
cal.set(1901,JANUARY,1,0,0,0); cal.set(1901, JANUARY, 1, 0, 0, 0);
assertEquals(366, DateUtil.absoluteDay(ldt, false), "Checking absolute day (1 Jan 1901) (LocalDateTime)"); assertEquals(366, DateUtil.absoluteDay(ldt, false), "Checking absolute day (1 Jan 1901) (LocalDateTime)");
} }
@Test @Test
void absoluteDayYearTooLow() { void absoluteDayYearTooLow() {
Calendar cal = LocaleUtil.getLocaleCalendar(1899,JANUARY,1,0,0,0); Calendar cal = LocaleUtil.getLocaleCalendar(1899, JANUARY, 1, 0, 0, 0);
assertThrows(IllegalArgumentException.class, () -> DateUtil.absoluteDay(cal, false)); assertThrows(IllegalArgumentException.class, () -> DateUtil.absoluteDay(cal, false));
cal.set(1903,JANUARY,1,0,0,0); cal.set(1903, JANUARY, 1, 0, 0, 0);
assertThrows(IllegalArgumentException.class, () -> DateUtil.absoluteDay(cal, true)); assertThrows(IllegalArgumentException.class, () -> DateUtil.absoluteDay(cal, true));
// same for LocalDateTime // same for LocalDateTime
assertThrows(IllegalArgumentException.class, () -> DateUtil.absoluteDay(LocalDateTime.of(1899,1,1,0,0,0), false)); assertThrows(IllegalArgumentException.class, () -> DateUtil.absoluteDay(LocalDateTime.of(1899, 1, 1, 0, 0, 0), false));
assertThrows(IllegalArgumentException.class, () -> DateUtil.absoluteDay(LocalDateTime.of(1903,1,1,0,0,0), true)); assertThrows(IllegalArgumentException.class, () -> DateUtil.absoluteDay(LocalDateTime.of(1903, 1, 1, 0, 0, 0), true));
} }
@Test @Test
@ -627,7 +627,7 @@ class TestDateUtil {
final double delta = 1E-7; // a couple of digits more accuracy than strictly required final double delta = 1E-7; // a couple of digits more accuracy than strictly required
assertEquals(0.5, DateUtil.convertTime("12:00"), delta); assertEquals(0.5, DateUtil.convertTime("12:00"), delta);
assertEquals(2.0/3, DateUtil.convertTime("16:00"), delta); assertEquals(2.0 / 3, DateUtil.convertTime("16:00"), delta);
assertEquals(0.0000116, DateUtil.convertTime("0:00:01"), delta); assertEquals(0.0000116, DateUtil.convertTime("0:00:01"), delta);
assertEquals(0.7330440, DateUtil.convertTime("17:35:35"), delta); assertEquals(0.7330440, DateUtil.convertTime("17:35:35"), delta);
} }
@ -657,7 +657,7 @@ class TestDateUtil {
/** /**
* User reported a datetime issue in POI-2.5: * User reported a datetime issue in POI-2.5:
* Setting Cell's value to Jan 1, 1900 without a time doesn't return the same value set to * Setting Cell's value to Jan 1, 1900 without a time doesn't return the same value set to
*/ */
@Test @Test
void bug19172() throws IOException { void bug19172() throws IOException {
@ -713,4 +713,21 @@ class TestDateUtil {
assertEquals(0, ldtRound.getMinute()); assertEquals(0, ldtRound.getMinute());
assertEquals(0, ldtRound.getSecond()); assertEquals(0, ldtRound.getSecond());
} }
}
@Test
void timeOnly() {
final double d = 22.0 / 24.0; // 22:00 (10pm)
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", LocaleUtil.getUserLocale());
final Date date = DateUtil.getJavaDate(d, false);
assertEquals("1899-12-31T23:00:00.000+0100", sdf.format(date));
final Date date1904 = DateUtil.getJavaDate(d, true);
assertEquals("1904-01-01T22:00:00.000+0000", sdf.format(date1904));
final double d0 = DateUtil.getExcelDate(date, false);
assertEquals(d, d0);
final double d1 = DateUtil.getExcelDate(date1904, true);
assertEquals(d, d1, 1E-10);
}
}