|
|
@ -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; |
|
|
|
package com.alibaba.excel.util; |
|
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.math.BigDecimal; |
|
|
@ -9,13 +26,16 @@ import java.time.LocalDateTime; |
|
|
|
import java.time.LocalTime; |
|
|
|
import java.time.LocalTime; |
|
|
|
import java.time.ZoneId; |
|
|
|
import java.time.ZoneId; |
|
|
|
import java.time.format.DateTimeFormatter; |
|
|
|
import java.time.format.DateTimeFormatter; |
|
|
|
|
|
|
|
import java.util.Calendar; |
|
|
|
import java.util.Date; |
|
|
|
import java.util.Date; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.Locale; |
|
|
|
import java.util.Locale; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import java.util.TimeZone; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
|
|
|
|
|
|
|
|
import org.apache.poi.ss.usermodel.DateUtil; |
|
|
|
import org.apache.poi.ss.usermodel.DateUtil; |
|
|
|
|
|
|
|
import org.apache.poi.util.LocaleUtil; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Date utils |
|
|
|
* Date utils |
|
|
@ -63,6 +83,15 @@ public class DateUtils { |
|
|
|
|
|
|
|
|
|
|
|
public static String defaultLocalDateFormat = DATE_FORMAT_10; |
|
|
|
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() {} |
|
|
|
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 |
|
|
|
* @return Java representation of the date, or null if date is not a valid Excel date |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static Date getJavaDate(double date, boolean use1904windowing) { |
|
|
|
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,
|
|
|
|
Calendar calendar = getJavaCalendar(date, use1904windowing, null, true); |
|
|
|
// java.util.TimeZone, boolean), Date when similar `2023-01-01 00:00:00.500`, returns the`2023-01-01
|
|
|
|
return calendar == null ? null : calendar.getTime(); |
|
|
|
// 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()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 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, |
|
|
|
* Given an Excel date with either 1900 or 1904 date windowing, |
|
|
|
* converts it to a java.time.LocalDateTime. |
|
|
|
* converts it to a java.time.LocalDateTime. |
|
|
|