mirror of https://github.com/apache/poi.git
Bug 63779 - Add support for the new Java date/time API added in Java 8
Active roundSeconds again git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1871065 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0f29ae8e4d
commit
1c678f415c
|
@ -18,9 +18,7 @@
|
|||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
|
||||
import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;
|
||||
import org.apache.poi.util.LocaleUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
@ -28,6 +26,7 @@ import java.time.ZoneId;
|
|||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalQueries;
|
||||
import java.util.Calendar;
|
||||
|
@ -35,6 +34,9 @@ import java.util.Date;
|
|||
import java.util.TimeZone;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;
|
||||
import org.apache.poi.util.LocaleUtil;
|
||||
|
||||
/**
|
||||
* Contains methods for dealing with Excel dates.
|
||||
*/
|
||||
|
@ -49,9 +51,16 @@ public class DateUtil {
|
|||
public static final int HOURS_PER_DAY = 24;
|
||||
public static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
|
||||
|
||||
private static final int BAD_DATE = -1; // used to specify that date is invalid
|
||||
// used to specify that date is invalid
|
||||
private static final int BAD_DATE = -1;
|
||||
public static final long DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L;
|
||||
|
||||
|
||||
|
||||
private static final BigDecimal BD_NANOSEC_DAY = BigDecimal.valueOf(SECONDS_PER_DAY * 1e9);
|
||||
private static final BigDecimal BD_MILISEC_RND = BigDecimal.valueOf(0.5 * 1e6);
|
||||
private static final BigDecimal BD_SECOND_RND = BigDecimal.valueOf(0.5 * 1e9);
|
||||
|
||||
private static final Pattern TIME_SEPARATOR_PATTERN = Pattern.compile(":");
|
||||
|
||||
/**
|
||||
|
@ -396,12 +405,15 @@ public class DateUtil {
|
|||
* @param roundSeconds round to closest second
|
||||
* @return Java representation of the date, or null if date is not a valid Excel date
|
||||
*/
|
||||
@SuppressWarnings("squid:S2111")
|
||||
public static LocalDateTime getLocalDateTime(double date, boolean use1904windowing, boolean roundSeconds) {
|
||||
if (!isValidExcelDate(date)) {
|
||||
return null;
|
||||
}
|
||||
int wholeDays = (int)Math.floor(date);
|
||||
int millisecondsInDay = (int)((date - wholeDays) * DAY_MILLISECONDS + 0.5);
|
||||
|
||||
BigDecimal bd = new BigDecimal(date);
|
||||
|
||||
int wholeDays = bd.intValue();
|
||||
|
||||
int startYear = 1900;
|
||||
int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
|
||||
|
@ -417,7 +429,15 @@ public class DateUtil {
|
|||
|
||||
LocalDateTime ldt = LocalDateTime.of(startYear, 1, 1, 0, 0);
|
||||
ldt = ldt.plusDays(wholeDays+dayAdjust-1);
|
||||
ldt = ldt.plusNanos(millisecondsInDay*1_000_000L);
|
||||
|
||||
long nanosTime =
|
||||
bd.subtract(BigDecimal.valueOf(wholeDays))
|
||||
.multiply(BD_NANOSEC_DAY)
|
||||
.add(roundSeconds ? BD_SECOND_RND : BD_MILISEC_RND)
|
||||
.longValue();
|
||||
|
||||
ldt = ldt.plusNanos(nanosTime);
|
||||
ldt = ldt.truncatedTo(roundSeconds ? ChronoUnit.SECONDS : ChronoUnit.MILLIS);
|
||||
|
||||
return ldt;
|
||||
}
|
||||
|
@ -524,25 +544,27 @@ public class DateUtil {
|
|||
// avoid re-checking DataUtil.isADateFormat(int, String) if a given format
|
||||
// string represents a date format if the same string is passed multiple times.
|
||||
// see https://issues.apache.org/bugzilla/show_bug.cgi?id=55611
|
||||
private static ThreadLocal<Integer> lastFormatIndex = new ThreadLocal<Integer>() {
|
||||
@Override
|
||||
protected Integer initialValue() {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
private static ThreadLocal<Integer> lastFormatIndex = ThreadLocal.withInitial(() -> -1);
|
||||
private static ThreadLocal<String> lastFormatString = new ThreadLocal<>();
|
||||
private static ThreadLocal<Boolean> lastCachedResult = new ThreadLocal<>();
|
||||
|
||||
private static boolean isCached(String formatString, int formatIndex) {
|
||||
String cachedFormatString = lastFormatString.get();
|
||||
return cachedFormatString != null && formatIndex == lastFormatIndex.get()
|
||||
&& formatString.equals(cachedFormatString);
|
||||
return formatIndex == lastFormatIndex.get()
|
||||
&& formatString.equals(lastFormatString.get());
|
||||
}
|
||||
|
||||
private static void cache(String formatString, int formatIndex, boolean cached) {
|
||||
lastFormatIndex.set(formatIndex);
|
||||
if (formatString == null || "".equals(formatString)) {
|
||||
lastFormatString.remove();
|
||||
} else {
|
||||
lastFormatString.set(formatString);
|
||||
lastCachedResult.set(Boolean.valueOf(cached));
|
||||
}
|
||||
if (formatIndex == -1) {
|
||||
lastFormatIndex.remove();
|
||||
} else {
|
||||
lastFormatIndex.set(formatIndex);
|
||||
}
|
||||
lastCachedResult.set(cached);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,23 +17,33 @@
|
|||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
|
||||
import static java.util.Calendar.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static java.util.Calendar.AUGUST;
|
||||
import static java.util.Calendar.FEBRUARY;
|
||||
import static java.util.Calendar.JANUARY;
|
||||
import static java.util.Calendar.JULY;
|
||||
import static java.util.Calendar.MARCH;
|
||||
import static java.util.Calendar.MAY;
|
||||
import static java.util.Calendar.OCTOBER;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.model.InternalWorkbook;
|
||||
import org.apache.poi.hssf.usermodel.*;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.util.LocaleUtil;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -56,24 +66,24 @@ public class TestDateUtil {
|
|||
|
||||
@Test
|
||||
public void getJavaDate_InvalidValue() {
|
||||
double dateValue = -1;
|
||||
TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
boolean use1904windowing = false;
|
||||
boolean roundSeconds = false;
|
||||
final double dateValue = -1;
|
||||
final TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
final boolean use1904windowing = false;
|
||||
final boolean roundSeconds = false;
|
||||
|
||||
assertEquals(null, DateUtil.getJavaDate(dateValue));
|
||||
assertEquals(null, DateUtil.getJavaDate(dateValue, tz));
|
||||
assertEquals(null, DateUtil.getJavaDate(dateValue, use1904windowing));
|
||||
assertEquals(null, DateUtil.getJavaDate(dateValue, use1904windowing, tz));
|
||||
assertEquals(null, DateUtil.getJavaDate(dateValue, use1904windowing, tz, roundSeconds));
|
||||
assertNull(DateUtil.getJavaDate(dateValue));
|
||||
assertNull(DateUtil.getJavaDate(dateValue, tz));
|
||||
assertNull(DateUtil.getJavaDate(dateValue, use1904windowing));
|
||||
assertNull(DateUtil.getJavaDate(dateValue, use1904windowing, tz));
|
||||
assertNull(DateUtil.getJavaDate(dateValue, use1904windowing, tz, roundSeconds));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJavaDate_ValidValue() {
|
||||
double dateValue = 0;
|
||||
TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
boolean use1904windowing = false;
|
||||
boolean roundSeconds = false;
|
||||
final double dateValue = 0;
|
||||
final TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
final boolean use1904windowing = false;
|
||||
final boolean roundSeconds = false;
|
||||
|
||||
Calendar calendar = LocaleUtil.getLocaleCalendar(1900, 0, 0);
|
||||
Date date = calendar.getTime();
|
||||
|
@ -87,23 +97,23 @@ public class TestDateUtil {
|
|||
|
||||
@Test
|
||||
public void getJavaCalendar_InvalidValue() {
|
||||
double dateValue = -1;
|
||||
TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
boolean use1904windowing = false;
|
||||
boolean roundSeconds = false;
|
||||
final double dateValue = -1;
|
||||
final TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
final boolean use1904windowing = false;
|
||||
final boolean roundSeconds = false;
|
||||
|
||||
assertEquals(null, DateUtil.getJavaCalendar(dateValue));
|
||||
assertEquals(null, DateUtil.getJavaCalendar(dateValue, use1904windowing));
|
||||
assertEquals(null, DateUtil.getJavaCalendar(dateValue, use1904windowing, tz));
|
||||
assertEquals(null, DateUtil.getJavaCalendar(dateValue, use1904windowing, tz, roundSeconds));
|
||||
assertNull(DateUtil.getJavaCalendar(dateValue));
|
||||
assertNull(DateUtil.getJavaCalendar(dateValue, use1904windowing));
|
||||
assertNull(DateUtil.getJavaCalendar(dateValue, use1904windowing, tz));
|
||||
assertNull(DateUtil.getJavaCalendar(dateValue, use1904windowing, tz, roundSeconds));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJavaCalendar_ValidValue() {
|
||||
double dateValue = 0;
|
||||
TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
boolean use1904windowing = false;
|
||||
boolean roundSeconds = false;
|
||||
final double dateValue = 0;
|
||||
final TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
final boolean use1904windowing = false;
|
||||
final boolean roundSeconds = false;
|
||||
|
||||
Calendar expCal = LocaleUtil.getLocaleCalendar(1900, 0, 0);
|
||||
|
||||
|
@ -121,21 +131,20 @@ public class TestDateUtil {
|
|||
|
||||
@Test
|
||||
public void getLocalDateTime_InvalidValue() {
|
||||
double dateValue = -1;
|
||||
TimeZone tz = LocaleUtil.getUserTimeZone();
|
||||
boolean use1904windowing = false;
|
||||
boolean roundSeconds = false;
|
||||
final double dateValue = -1;
|
||||
final boolean use1904windowing = false;
|
||||
final boolean roundSeconds = false;
|
||||
|
||||
assertEquals(null, DateUtil.getLocalDateTime(dateValue));
|
||||
assertEquals(null, DateUtil.getLocalDateTime(dateValue, use1904windowing));
|
||||
assertEquals(null, DateUtil.getLocalDateTime(dateValue, use1904windowing, roundSeconds));
|
||||
assertNull(DateUtil.getLocalDateTime(dateValue));
|
||||
assertNull(DateUtil.getLocalDateTime(dateValue, use1904windowing));
|
||||
assertNull(DateUtil.getLocalDateTime(dateValue, use1904windowing, roundSeconds));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLocalDateTime_ValidValue() {
|
||||
double dateValue = 0;
|
||||
boolean use1904windowing = false;
|
||||
boolean roundSeconds = false;
|
||||
final double dateValue = 0;
|
||||
final boolean use1904windowing = false;
|
||||
final boolean roundSeconds = false;
|
||||
|
||||
// note that the Date and Calendar examples use a zero day of month which is invalid in LocalDateTime
|
||||
LocalDateTime date = LocalDateTime.of(1899, 12, 31, 0, 0);
|
||||
|
@ -180,7 +189,7 @@ public class TestDateUtil {
|
|||
@Test
|
||||
public void dateConversion() {
|
||||
|
||||
// Iteratating 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);
|
||||
for (int hour = 0; hour < 24; hour++) {
|
||||
double excelDate = DateUtil.getExcelDate(cal.getTime(), false);
|
||||
|
@ -321,7 +330,6 @@ public class TestDateUtil {
|
|||
// perform the same checks using LocalDateTime
|
||||
LocalDateTime localDate = LocalDateTime.of(2004,10,31,hour,0,0);
|
||||
double excelLocalDate = DateUtil.getExcelDate(localDate, false);
|
||||
double differenceLocalDate = excelLocalDate - Math.floor(excelLocalDate);
|
||||
int differenceLocalDateInHours = (int) (difference * 24 * 60 + 0.5) / 60;
|
||||
assertEquals("Checking " + hour + " hour on Daylight Saving Time end date (LocalDateTime)",
|
||||
hour,
|
||||
|
@ -498,12 +506,13 @@ public class TestDateUtil {
|
|||
|
||||
// And these are ones we probably shouldn't allow,
|
||||
// but would need a better regexp
|
||||
formats = new String[] {
|
||||
"yyyy:mm:dd",
|
||||
};
|
||||
for (String format : formats) {
|
||||
// assertFalse( DateUtil.isADateFormat(formatId, formats[i]) );
|
||||
}
|
||||
|
||||
// formats = new String[] {
|
||||
// "yyyy:mm:dd",
|
||||
// };
|
||||
// for (String format : formats) {
|
||||
// assertFalse( DateUtil.isADateFormat(formatId, format) );
|
||||
// }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -596,15 +605,15 @@ public class TestDateUtil {
|
|||
* @param day one based
|
||||
*/
|
||||
private static Date createDate(int year, int month, int day) {
|
||||
return createDate(year, month, day, 0, 0, 0);
|
||||
return createDate(year, month, day, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param month zero based
|
||||
* @param day one based
|
||||
*/
|
||||
private static Date createDate(int year, int month, int day, int hour, int minute, int second) {
|
||||
Calendar c = LocaleUtil.getLocaleCalendar(year, month, day, hour, minute, second);
|
||||
private static Date createDate(int year, int month, int day, int hour, int minute) {
|
||||
Calendar c = LocaleUtil.getLocaleCalendar(year, month, day, hour, minute, 0);
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
|
@ -682,7 +691,7 @@ public class TestDateUtil {
|
|||
// Excel day 30000 is date 18-Feb-1982
|
||||
// 0.7 corresponds to time 16:48:00
|
||||
Date actual = DateUtil.getJavaDate(30000.7);
|
||||
Date expected = createDate(1982, 1, 18, 16, 48, 0);
|
||||
Date expected = createDate(1982, 1, 18, 16, 48);
|
||||
assertEquals(expected, actual);
|
||||
|
||||
// note that months in Calendar are zero-based, in LocalDateTime one-based
|
||||
|
@ -694,12 +703,10 @@ public class TestDateUtil {
|
|||
/**
|
||||
* 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
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void bug19172() throws IOException
|
||||
{
|
||||
HSSFWorkbook workbook = new HSSFWorkbook();
|
||||
public void bug19172() throws IOException {
|
||||
try (HSSFWorkbook workbook = new HSSFWorkbook()) {
|
||||
HSSFSheet sheet = workbook.createSheet();
|
||||
HSSFCell cell = sheet.createRow(0).createCell(0);
|
||||
|
||||
|
@ -713,8 +720,7 @@ public class TestDateUtil {
|
|||
Date returnedValue = cell.getDateCellValue();
|
||||
|
||||
assertEquals(valueToTest.getTime(), returnedValue.getTime());
|
||||
|
||||
workbook.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -722,23 +728,34 @@ public class TestDateUtil {
|
|||
* that's formatted as ".0000"
|
||||
*/
|
||||
@Test
|
||||
public void bug54557() throws Exception {
|
||||
public void bug54557() {
|
||||
final String format = ".0000";
|
||||
boolean isDateFormat = DateUtil.isADateFormat(165, format);
|
||||
|
||||
assertEquals(false, isDateFormat);
|
||||
assertFalse(isDateFormat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bug56269() throws Exception {
|
||||
public void bug56269() {
|
||||
double excelFraction = 41642.45833321759d;
|
||||
Calendar calNoRound = DateUtil.getJavaCalendar(excelFraction, false);
|
||||
assertEquals(10, calNoRound.get(Calendar.HOUR));
|
||||
assertEquals(59, calNoRound.get(Calendar.MINUTE));
|
||||
assertEquals(59, calNoRound.get(Calendar.SECOND));
|
||||
Calendar calRound = DateUtil.getJavaCalendar(excelFraction, false, null, true);
|
||||
assertNotNull(calRound);
|
||||
assertEquals(11, calRound.get(Calendar.HOUR));
|
||||
assertEquals(0, calRound.get(Calendar.MINUTE));
|
||||
assertEquals(0, calRound.get(Calendar.SECOND));
|
||||
|
||||
LocalDateTime ldtNoRound = DateUtil.getLocalDateTime(excelFraction, false);
|
||||
assertEquals(10, ldtNoRound.getHour());
|
||||
assertEquals(59, ldtNoRound.getMinute());
|
||||
assertEquals(59, ldtNoRound.getSecond());
|
||||
LocalDateTime ldtRound = DateUtil.getLocalDateTime(excelFraction, false, true);
|
||||
assertNotNull(ldtRound);
|
||||
assertEquals(11, ldtRound.getHour());
|
||||
assertEquals(0, ldtRound.getMinute());
|
||||
assertEquals(0, ldtRound.getSecond());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue