aboutsummaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
authorDominik Stadler <centic@apache.org>2019-10-09 19:12:59 +0000
committerDominik Stadler <centic@apache.org>2019-10-09 19:12:59 +0000
commitb10f94cc75d00d2b215f3a148dd4e79bbaf2b1b3 (patch)
tree8d81cb6b541655373493c8c1bc3d6b0f2c6b9788 /src/java
parent97c21e31b10a37365cf499dbe1c5345680c872df (diff)
downloadpoi-b10f94cc75d00d2b215f3a148dd4e79bbaf2b1b3.tar.gz
poi-b10f94cc75d00d2b215f3a148dd4e79bbaf2b1b3.zip
63779 Add support for the new Java date/time API added in Java 8
Deprecate HSSFDateUtil Closes #160 on Github git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1868198 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java')
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/DVConstraint.java7
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFCell.java48
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java4
-rw-r--r--src/java/org/apache/poi/ss/usermodel/Cell.java50
-rw-r--r--src/java/org/apache/poi/ss/usermodel/CellBase.java19
-rw-r--r--src/java/org/apache/poi/ss/usermodel/DateUtil.java243
6 files changed, 339 insertions, 32 deletions
diff --git a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java
index f3f5e557a4..5317969278 100644
--- a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java
+++ b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java
@@ -31,6 +31,7 @@ import org.apache.poi.ss.formula.ptg.NumberPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.StringPtg;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
+import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.util.LocaleUtil;
/**
@@ -242,7 +243,7 @@ public class DVConstraint implements DataValidationConstraint {
if (timeStr == null) {
return null;
}
- return Double.valueOf(HSSFDateUtil.convertTime(timeStr));
+ return Double.valueOf(DateUtil.convertTime(timeStr));
}
/**
* @param dateFormat pass <code>null</code> for default YYYYMMDD
@@ -254,7 +255,7 @@ public class DVConstraint implements DataValidationConstraint {
}
Date dateVal;
if (dateFormat == null) {
- dateVal = HSSFDateUtil.parseYYYYMMDDDate(dateStr);
+ dateVal = DateUtil.parseYYYYMMDDDate(dateStr);
} else {
try {
dateVal = dateFormat.parse(dateStr);
@@ -263,7 +264,7 @@ public class DVConstraint implements DataValidationConstraint {
+ "' using specified format '" + dateFormat + "'", e);
}
}
- return Double.valueOf(HSSFDateUtil.getExcelDate(dateVal));
+ return Double.valueOf(DateUtil.getExcelDate(dateVal));
}
public static DVConstraint createCustomFormulaConstraint(String formula) {
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
index 12788d5bf9..7b309fe54f 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
@@ -18,6 +18,7 @@
package org.apache.poi.hssf.usermodel;
import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
@@ -47,6 +48,7 @@ import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FormulaError;
import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.RichTextString;
@@ -453,11 +455,22 @@ public class HSSFCell extends CellBase {
* {@inheritDoc}
*
* <p>In HSSF, only the number of days is stored. The fractional part is ignored.</p>
- * @see HSSFDateUtil
+ * @see DateUtil
* @see org.apache.poi.ss.usermodel.DateUtil
*/
protected void setCellValueImpl(Date value) {
- setCellValue(HSSFDateUtil.getExcelDate(value, _book.getWorkbook().isUsing1904DateWindowing()));
+ setCellValue(DateUtil.getExcelDate(value, _book.getWorkbook().isUsing1904DateWindowing()));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>In HSSF, only the number of days is stored. The fractional part is ignored.</p>
+ * @see DateUtil
+ * @see org.apache.poi.ss.usermodel.DateUtil
+ */
+ protected void setCellValueImpl(LocalDateTime value) {
+ setCellValue(DateUtil.getExcelDate(value, _book.getWorkbook().isUsing1904DateWindowing()));
}
/**
@@ -465,7 +478,7 @@ public class HSSFCell extends CellBase {
*/
@Override
protected void setCellValueImpl(Calendar value) {
- setCellValue( HSSFDateUtil.getExcelDate(value, _book.getWorkbook().isUsing1904DateWindowing()) );
+ setCellValue( DateUtil.getExcelDate(value, _book.getWorkbook().isUsing1904DateWindowing()) );
}
/**
@@ -679,9 +692,28 @@ public class HSSFCell extends CellBase {
}
double value = getNumericCellValue();
if (_book.getWorkbook().isUsing1904DateWindowing()) {
- return HSSFDateUtil.getJavaDate(value, true);
+ return DateUtil.getJavaDate(value, true);
}
- return HSSFDateUtil.getJavaDate(value, false);
+ return DateUtil.getJavaDate(value, false);
+ }
+
+ /**
+ * Get the value of the cell as a LocalDateTime.
+ * For strings we throw an exception.
+ * For blank cells we return a null.
+ * See {@link HSSFDataFormatter} for formatting
+ * this date into a string similar to how excel does.
+ */
+ public LocalDateTime getLocalDateTimeCellValue() {
+
+ if (_cellType == CellType.BLANK) {
+ return null;
+ }
+ double value = getNumericCellValue();
+ if (_book.getWorkbook().isUsing1904DateWindowing()) {
+ return DateUtil.getLocalDateTime(value, true);
+ }
+ return DateUtil.getLocalDateTime(value, false);
}
/**
@@ -853,7 +885,7 @@ public class HSSFCell extends CellBase {
default:
throw new IllegalStateException("Unexpected formula result type (" + _cellType + ")");
}
-
+
}
/**
@@ -899,7 +931,7 @@ public class HSSFCell extends CellBase {
/**
* <p>Set the style for the cell. The style should be an HSSFCellStyle created/retreived from
* the HSSFWorkbook.</p>
- *
+ *
* <p>To change the style of a cell without affecting other cells that use the same style,
* use {@link org.apache.poi.ss.util.CellUtil#setCellStyleProperties(org.apache.poi.ss.usermodel.Cell, java.util.Map)}</p>
*
@@ -1001,7 +1033,7 @@ public class HSSFCell extends CellBase {
return getCellFormula();
case NUMERIC:
//TODO apply the dataformat for this cell
- if (HSSFDateUtil.isCellDateFormatted(this)) {
+ if (DateUtil.isCellDateFormatted(this)) {
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", LocaleUtil.getUserLocale());
sdf.setTimeZone(LocaleUtil.getUserTimeZone());
return sdf.format(getDateCellValue());
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java
index eb415269e9..f0ff039fd1 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java
@@ -30,8 +30,10 @@ import org.apache.poi.ss.usermodel.DateUtil;
/**
* Contains methods for dealing with Excel dates.
+ * @deprecated Use {@link DateUtil} instead
*/
-public class HSSFDateUtil extends DateUtil {
+@Deprecated
+public final class HSSFDateUtil extends DateUtil {
protected static int absoluteDay(Calendar cal, boolean use1904windowing) {
return DateUtil.absoluteDay(cal, use1904windowing);
}
diff --git a/src/java/org/apache/poi/ss/usermodel/Cell.java b/src/java/org/apache/poi/ss/usermodel/Cell.java
index d806ef6842..c6a017b26b 100644
--- a/src/java/org/apache/poi/ss/usermodel/Cell.java
+++ b/src/java/org/apache/poi/ss/usermodel/Cell.java
@@ -17,6 +17,8 @@
package org.apache.poi.ss.usermodel;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
@@ -179,6 +181,42 @@ public interface Cell {
void setCellValue(Date value);
/**
+ * <p>Converts the supplied date to its equivalent Excel numeric value and sets
+ * that into the cell.</p>
+ *
+ * <p><b>Note</b> - There is actually no 'DATE' cell type in Excel. In many
+ * cases (when entering date values), Excel automatically adjusts the
+ * <i>cell style</i> to some date format, creating the illusion that the cell
+ * data type is now something besides {@link CellType#NUMERIC}. POI
+ * does not attempt to replicate this behaviour. To make a numeric cell
+ * display as a date, use {@link #setCellStyle(CellStyle)} etc.</p>
+ *
+ * @param value the numeric value to set this cell to. For formulas we'll set the
+ * precalculated value, for numerics we'll set its value. For other types we
+ * will change the cell to a numerics cell and set its value.
+ */
+ void setCellValue(LocalDateTime value);
+
+ /**
+ * <p>Converts the supplied date to its equivalent Excel numeric value and sets
+ * that into the cell.</p>
+ *
+ * <p><b>Note</b> - There is actually no 'DATE' cell type in Excel. In many
+ * cases (when entering date values), Excel automatically adjusts the
+ * <i>cell style</i> to some date format, creating the illusion that the cell
+ * data type is now something besides {@link CellType#NUMERIC}. POI
+ * does not attempt to replicate this behaviour. To make a numeric cell
+ * display as a date, use {@link #setCellStyle(CellStyle)} etc.</p>
+ *
+ * @param value the numeric value to set this cell to. For formulas we'll set the
+ * precalculated value, for numerics we'll set its value. For other types we
+ * will change the cell to a numerics cell and set its value.
+ */
+ default void setCellValue(LocalDate value) {
+ setCellValue(value.atStartOfDay());
+ }
+
+ /**
* <p>Set a date value for the cell. Excel treats dates as numeric so you will need to format the cell as
* a date.</p>
* <p>
@@ -280,6 +318,18 @@ public interface Cell {
Date getDateCellValue();
/**
+ * Get the value of the cell as a LocalDateTime.
+ * <p>
+ * For strings we throw an exception. For blank cells we return a null.
+ * </p>
+ * @return the value of the cell as a LocalDateTime
+ * @throws IllegalStateException if the cell type returned by {@link #getCellType()} is {@link CellType#STRING}
+ * @exception NumberFormatException if the cell value isn't a parsable <code>double</code>.
+ * @see DataFormatter for formatting this date into a string similar to how excel does.
+ */
+ LocalDateTime getLocalDateTimeCellValue();
+
+ /**
* Get the value of the cell as a XSSFRichTextString
* <p>
* For numeric cells we throw an exception. For blank cells we return an empty string.
diff --git a/src/java/org/apache/poi/ss/usermodel/CellBase.java b/src/java/org/apache/poi/ss/usermodel/CellBase.java
index 7193075fa7..819c789ca8 100644
--- a/src/java/org/apache/poi/ss/usermodel/CellBase.java
+++ b/src/java/org/apache/poi/ss/usermodel/CellBase.java
@@ -24,6 +24,7 @@ import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.Removal;
+import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
@@ -228,6 +229,15 @@ public abstract class CellBase implements Cell {
setCellValueImpl(value);
}
+ @Override
+ public void setCellValue(LocalDateTime value) {
+ if(value == null) {
+ setBlank();
+ return;
+ }
+ setCellValueImpl(value);
+ }
+
/**
* Implementation-specific way to set a date value.
* <code>value</code> is guaranteed to be non-null.
@@ -238,6 +248,15 @@ public abstract class CellBase implements Cell {
protected abstract void setCellValueImpl(Date value);
/**
+ * Implementation-specific way to set a date value.
+ * <code>value</code> is guaranteed to be non-null.
+ * The implementation is expected to adjust the cell type accordingly, so that after this call
+ * getCellType() or getCachedFormulaResultType() would return {@link CellType#NUMERIC}.
+ * @param value the new date to set
+ */
+ protected abstract void setCellValueImpl(LocalDateTime value);
+
+ /**
* {@inheritDoc}
*/
@Override
diff --git a/src/java/org/apache/poi/ss/usermodel/DateUtil.java b/src/java/org/apache/poi/ss/usermodel/DateUtil.java
index fbf34b7ae7..981abb4a31 100644
--- a/src/java/org/apache/poi/ss/usermodel/DateUtil.java
+++ b/src/java/org/apache/poi/ss/usermodel/DateUtil.java
@@ -18,7 +18,11 @@
package org.apache.poi.ss.usermodel;
+import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;
+import org.apache.poi.util.LocaleUtil;
+
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@@ -31,13 +35,11 @@ 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.
*/
public class DateUtil {
+ // FIXME this should be changed to private and the class marked final once HSSFDateUtil can be removed
protected DateUtil() {
// no instances of this class
}
@@ -75,6 +77,88 @@ public class DateUtil {
.toFormatter();
/**
+ * Convert a Java Date (at UTC) to LocalDateTime.
+ * @param date the date
+ * @return LocalDateTime instance
+ */
+ public static LocalDateTime toLocalDateTime(Date date) {
+ return date.toInstant()
+ .atZone(TimeZone.getTimeZone("UTC").toZoneId()) // java.util.Date uses UTC
+ .toLocalDateTime();
+ }
+
+ /**
+ * Convert a Java Calendar (at UTC) to LocalDateTime.
+ * @param date the date
+ * @return LocalDateTime instance
+ */
+ public static LocalDateTime toLocalDateTime(Calendar date) {
+ return date.toInstant()
+ .atZone(TimeZone.getTimeZone("UTC").toZoneId()) // java.util.Date uses UTC
+ .toLocalDateTime();
+ }
+
+ /**
+ * Given a LocalDate, converts it into a double representing its internal Excel representation,
+ * which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
+ *
+ * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
+ * @param date the Date
+ */
+ public static double getExcelDate(LocalDate date) {
+ return getExcelDate(date, false);
+ }
+
+ /**
+ * Given a LocalDate, converts it into a double representing its internal Excel representation,
+ * which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
+ *
+ * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
+ * @param date the Date
+ * @param use1904windowing Should 1900 or 1904 date windowing be used?
+ */
+ public static double getExcelDate(LocalDate date, boolean use1904windowing) {
+ int year = date.getYear();
+ int dayOfYear = date.getDayOfYear();
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ int milliSecond = 0;
+
+ return internalGetExcelDate(year, dayOfYear, hour, minute, second, milliSecond, use1904windowing);
+ }
+
+ /**
+ * Given a LocalDateTime, converts it into a double representing its internal Excel representation,
+ * which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
+ *
+ * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
+ * @param date the Date
+ */
+ public static double getExcelDate(LocalDateTime date) {
+ return getExcelDate(date, false);
+ }
+
+ /**
+ * Given a LocalDateTime, converts it into a double representing its internal Excel representation,
+ * which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
+ *
+ * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
+ * @param date the Date
+ * @param use1904windowing Should 1900 or 1904 date windowing be used?
+ */
+ public static double getExcelDate(LocalDateTime date, boolean use1904windowing) {
+ int year = date.getYear();
+ int dayOfYear = date.getDayOfYear();
+ int hour = date.getHour();
+ int minute = date.getMinute();
+ int second = date.getSecond();
+ int milliSecond = date.getNano()/1_000_000;
+
+ return internalGetExcelDate(year, dayOfYear, hour, minute, second, milliSecond, use1904windowing);
+ }
+
+ /**
* Given a Date, converts it into a double representing its internal Excel representation,
* which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
*
@@ -84,6 +168,7 @@ public class DateUtil {
public static double getExcelDate(Date date) {
return getExcelDate(date, false);
}
+
/**
* Given a Date, converts it into a double representing its internal Excel representation,
* which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
@@ -94,9 +179,17 @@ public class DateUtil {
*/
public static double getExcelDate(Date date, boolean use1904windowing) {
Calendar calStart = LocaleUtil.getLocaleCalendar();
- calStart.setTime(date); // If date includes hours, minutes, and seconds, set them to 0
- return internalGetExcelDate(calStart, use1904windowing);
+ calStart.setTime(date);
+ int year = calStart.get(Calendar.YEAR);
+ int dayOfYear = calStart.get(Calendar.DAY_OF_YEAR);
+ int hour = calStart.get(Calendar.HOUR_OF_DAY);
+ int minute = calStart.get(Calendar.MINUTE);
+ int second = calStart.get(Calendar.SECOND);
+ int milliSecond = calStart.get(Calendar.MILLISECOND);
+
+ return internalGetExcelDate(year, dayOfYear, hour, minute, second, milliSecond, use1904windowing);
}
+
/**
* Given a Date in the form of a Calendar, converts it into a double
* representing its internal Excel representation, which is the
@@ -108,15 +201,23 @@ public class DateUtil {
* @param use1904windowing Should 1900 or 1904 date windowing be used?
*/
public static double getExcelDate(Calendar date, boolean use1904windowing) {
- // Don't alter the supplied Calendar as we do our work
- return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
- }
- private static double internalGetExcelDate(Calendar date, boolean use1904windowing) {
- if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) ||
- (use1904windowing && date.get(Calendar.YEAR) < 1904))
+ int year = date.get(Calendar.YEAR);
+ int dayOfYear = date.get(Calendar.DAY_OF_YEAR);
+ int hour = date.get(Calendar.HOUR_OF_DAY);
+ int minute = date.get(Calendar.MINUTE);
+ int second = date.get(Calendar.SECOND);
+ int milliSecond = date.get(Calendar.MILLISECOND);
+
+ return internalGetExcelDate(year, dayOfYear, hour, minute, second, milliSecond, use1904windowing);
+ }
+
+ private static double internalGetExcelDate(int year, int dayOfYear, int hour, int minute, int second, int milliSecond, boolean use1904windowing) {
+ if ((!use1904windowing && year < 1900) ||
+ (use1904windowing && year < 1904))
{
return BAD_DATE;
}
+
// Because of daylight time saving we cannot use
// date.getTime() - calStart.getTimeInMillis()
// as the difference in milliseconds between 00:00 and 04:00
@@ -124,14 +225,13 @@ public class DateUtil {
// be 4 hours.
// E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
// and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
- double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60.0
- + date.get(Calendar.MINUTE)
- ) * 60.0 + date.get(Calendar.SECOND)
- ) * 1000.0 + date.get(Calendar.MILLISECOND)
+ double fraction = (((hour * 60.0
+ + minute
+ ) * 60.0 + second
+ ) * 1000.0 + milliSecond
) / DAY_MILLISECONDS;
- Calendar calStart = dayStart(date);
- double value = fraction + absoluteDay(calStart, use1904windowing);
+ double value = fraction + absoluteDay(year, dayOfYear, use1904windowing);
if (!use1904windowing && value >= 60) {
value++;
@@ -241,6 +341,86 @@ public class DateUtil {
public static Date getJavaDate(double date, boolean use1904windowing) {
return getJavaDate(date, use1904windowing, null, false);
}
+
+ /**
+ * Given an Excel date with using 1900 date windowing, and
+ * converts it to a java.time.LocalDateTime.
+ *
+ * NOTE: If the default <code>TimeZone</code> in Java uses Daylight
+ * Saving Time then the conversion back to an Excel date may not give
+ * the same value, that is the comparison
+ * <CODE>excelDate == getExcelDate(getLocalDateTime(excelDate,false))</CODE>
+ * is not always true. For example if default timezone is
+ * <code>Europe/Copenhagen</code>, on 2004-03-28 the minute after
+ * 01:59 CET is 03:00 CEST, if the excel date represents a time between
+ * 02:00 and 03:00 then it is converted to past 03:00 summer time
+ *
+ * @param date The Excel date.
+ * @return Java representation of the date, or null if date is not a valid Excel date
+ * @see java.util.TimeZone
+ */
+ public static LocalDateTime getLocalDateTime(double date) {
+ return getLocalDateTime(date, false, false);
+ }
+
+ /**
+ * Given an Excel date with either 1900 or 1904 date windowing,
+ * converts it to a java.time.LocalDateTime.
+ *
+ * Excel Dates and Times are stored without any timezone
+ * information. If you know (through other means) that your file
+ * uses a different TimeZone to the system default, you can use
+ * this version of the getJavaDate() method to handle it.
+ *
+ * @param date The Excel date.
+ * @param use1904windowing true if date uses 1904 windowing,
+ * or false if using 1900 date windowing.
+ * @return Java representation of the date, or null if date is not a valid Excel date
+ */
+ public static LocalDateTime getLocalDateTime(double date, boolean use1904windowing) {
+ return getLocalDateTime(date, use1904windowing, false);
+ }
+
+ /**
+ * Given an Excel date with either 1900 or 1904 date windowing,
+ * converts it to a java.time.LocalDateTime.
+ *
+ * Excel Dates and Times are stored without any timezone
+ * information. If you know (through other means) that your file
+ * uses a different TimeZone to the system default, you can use
+ * this version of the getJavaDate() method to handle it.
+ *
+ * @param date The Excel date.
+ * @param use1904windowing true if date uses 1904 windowing,
+ * or false if using 1900 date windowing.
+ * @param roundSeconds round to closest second
+ * @return Java representation of the date, or null if date is not a valid Excel date
+ */
+ 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);
+
+ int startYear = 1900;
+ int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
+ if (use1904windowing) {
+ startYear = 1904;
+ dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
+ }
+ else if (wholeDays < 61) {
+ // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
+ // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
+ dayAdjust = 0;
+ }
+
+ LocalDateTime ldt = LocalDateTime.of(startYear, 1, 1, 0, 0);
+ ldt = ldt.plusDays(wholeDays+dayAdjust-1);
+ ldt = ldt.plusNanos(millisecondsInDay*1_000_000L);
+
+ return ldt;
+ }
public static void setCalendar(Calendar calendar, int wholeDays,
int millisecondsInDay, boolean use1904windowing, boolean roundSeconds) {
@@ -616,11 +796,34 @@ public class DateUtil {
*/
protected static int absoluteDay(Calendar cal, boolean use1904windowing)
{
- return cal.get(Calendar.DAY_OF_YEAR)
- + daysInPriorYears(cal.get(Calendar.YEAR), use1904windowing);
+ return absoluteDay(cal.get(Calendar.YEAR), cal.get(Calendar.DAY_OF_YEAR), use1904windowing);
+ }
+
+ /**
+ * Given a LocalDateTime, return the number of days since 1900/12/31.
+ *
+ * @return days number of days since 1900/12/31
+ * @param date the Date
+ * @exception IllegalArgumentException if date is invalid
+ */
+ protected static int absoluteDay(LocalDateTime date, boolean use1904windowing)
+ {
+ return absoluteDay(date.getYear(), date.getDayOfYear(), use1904windowing);
}
/**
+ * Given a year and day of year, return the number of days since 1900/12/31.
+ *
+ * @return days number of days since 1900/12/31
+ * @param dayOfYear the day of the year
+ * @param year the year
+ * @exception IllegalArgumentException if date is invalid
+ */
+ private static int absoluteDay(int year, int dayOfYear, boolean use1904windowing) {
+ return dayOfYear + daysInPriorYears(year, use1904windowing);
+ }
+
+ /**
* Return the number of days in prior years since 1900
*
* @return days number of days in years prior to yr.
@@ -629,7 +832,7 @@ public class DateUtil {
* @exception IllegalArgumentException if year is outside of range.
*/
- private static int daysInPriorYears(int yr, boolean use1904windowing)
+ static int daysInPriorYears(int yr, boolean use1904windowing)
{
if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1904)) {
throw new IllegalArgumentException("'year' must be 1900 or greater");