diff --git a/README.md b/README.md
index 7fef9d91..25bd3be3 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析
com.alibaba
easyexcel
- 4.0.2
+ 4.0.3
```
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java
index 3184d087..035b984c 100644
--- a/easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java
@@ -1,3 +1,20 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
package com.alibaba.excel.util;
import java.math.BigDecimal;
@@ -9,13 +26,16 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.TimeZone;
import java.util.regex.Pattern;
import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.util.LocaleUtil;
/**
* Date utils
@@ -63,6 +83,15 @@ public class DateUtils {
public static String defaultLocalDateFormat = DATE_FORMAT_10;
+ public static final int SECONDS_PER_MINUTE = 60;
+ public static final int MINUTES_PER_HOUR = 60;
+ public static final int HOURS_PER_DAY = 24;
+ public static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
+
+ // 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 DateUtils() {}
/**
@@ -301,13 +330,75 @@ public class DateUtils {
* @return Java representation of the date, or null if date is not a valid Excel date
*/
public static Date getJavaDate(double date, boolean use1904windowing) {
- //To calculate the Date, in the use of `org.apache.poi.ss.usermodel.DateUtil.getJavaDate(double, boolean,
- // java.util.TimeZone, boolean), Date when similar `2023-01-01 00:00:00.500`, returns the`2023-01-01
- // 00:00:01`, but excel in fact shows the `2023-01-01 00:00:00`.
- // `org.apache.poi.ss.usermodel.DateUtil.getLocalDateTime(double, boolean, boolean)` There is no problem.
- return Date.from(getLocalDateTime(date, use1904windowing).atZone(ZoneId.systemDefault()).toInstant());
+ Calendar calendar = getJavaCalendar(date, use1904windowing, null, true);
+ return calendar == null ? null : calendar.getTime();
}
+ /**
+ * Get EXCEL date as Java Calendar with given time zone.
+ * @param date The Excel date.
+ * @param use1904windowing true if date uses 1904 windowing,
+ * or false if using 1900 date windowing.
+ * @param timeZone The TimeZone to evaluate the date in
+ * @param roundSeconds round to closest second
+ * @return Java representation of the date, or null if date is not a valid Excel date
+ */
+ public static Calendar getJavaCalendar(double date, boolean use1904windowing, TimeZone timeZone, boolean roundSeconds) {
+ if (!isValidExcelDate(date)) {
+ return null;
+ }
+ int wholeDays = (int)Math.floor(date);
+ int millisecondsInDay = (int)((date - wholeDays) * DAY_MILLISECONDS + 0.5);
+ Calendar calendar;
+ if (timeZone != null) {
+ calendar = LocaleUtil.getLocaleCalendar(timeZone);
+ } else {
+ calendar = LocaleUtil.getLocaleCalendar(); // using default time-zone
+ }
+ setCalendar(calendar, wholeDays, millisecondsInDay, use1904windowing, roundSeconds);
+ return calendar;
+ }
+
+
+ public static void setCalendar(Calendar calendar, int wholeDays,
+ int millisecondsInDay, boolean use1904windowing, boolean roundSeconds) {
+ 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;
+ }
+ calendar.set(startYear, Calendar.JANUARY, wholeDays + dayAdjust, 0, 0, 0);
+ calendar.set(Calendar.MILLISECOND, millisecondsInDay);
+ if (calendar.get(Calendar.MILLISECOND) == 0) {
+ calendar.clear(Calendar.MILLISECOND);
+ }
+ if (roundSeconds) {
+ // This is different from poi where you need to change 500 to 499
+ calendar.add(Calendar.MILLISECOND, 499);
+ calendar.clear(Calendar.MILLISECOND);
+ }
+ }
+
+
+ /**
+ * Given a double, checks if it is a valid Excel date.
+ *
+ * @return true if valid
+ * @param value the double value
+ */
+
+ public static boolean isValidExcelDate(double value)
+ {
+ return (value > -Double.MIN_VALUE);
+ }
+
+
/**
* Given an Excel date with either 1900 or 1904 date windowing,
* converts it to a java.time.LocalDateTime.
diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java
index 1229b238..fa7a517b 100644
--- a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java
+++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java
@@ -3,10 +3,12 @@ package com.alibaba.easyexcel.test.core.dataformat;
import java.io.File;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Objects;
import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel;
+import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
@@ -22,6 +24,7 @@ import org.junit.jupiter.api.TestMethodOrder;
@Slf4j
public class DateFormatTest {
+ private static File file07V2;
private static File file07;
private static File file03;
@@ -29,6 +32,8 @@ public class DateFormatTest {
public static void init() {
file07 = TestFileUtil.readFile("dataformat" + File.separator + "dataformat.xlsx");
file03 = TestFileUtil.readFile("dataformat" + File.separator + "dataformat.xls");
+ file07V2 = TestFileUtil.readFile("dataformat" + File.separator + "dataformatv2.xlsx");
+
}
@Test
@@ -43,6 +48,16 @@ public class DateFormatTest {
readUs(file03);
}
+ @Test
+ public void t03Read() {
+ List