|
| 1 | +/* ==================================================================== |
| 2 | + Licensed to the Apache Software Foundation (ASF) under one or more |
| 3 | + contributor license agreements. See the NOTICE file distributed with |
| 4 | + this work for additional information regarding copyright ownership. |
| 5 | + The ASF licenses this file to You under the Apache License, Version 2.0 |
| 6 | + (the "License"); you may not use this file except in compliance with |
| 7 | + the License. You may obtain a copy of the License at |
| 8 | +
|
| 9 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +
|
| 11 | + Unless required by applicable law or agreed to in writing, software |
| 12 | + distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + See the License for the specific language governing permissions and |
| 15 | + limitations under the License. |
| 16 | +==================================================================== */ |
| 17 | + |
1 | 18 | package com.alibaba.excel.util;
|
2 | 19 |
|
3 | 20 | import java.math.BigDecimal;
|
|
9 | 26 | import java.time.LocalTime;
|
10 | 27 | import java.time.ZoneId;
|
11 | 28 | import java.time.format.DateTimeFormatter;
|
| 29 | +import java.util.Calendar; |
12 | 30 | import java.util.Date;
|
13 | 31 | import java.util.HashMap;
|
14 | 32 | import java.util.Locale;
|
15 | 33 | import java.util.Map;
|
| 34 | +import java.util.TimeZone; |
16 | 35 | import java.util.regex.Pattern;
|
17 | 36 |
|
18 | 37 | import org.apache.poi.ss.usermodel.DateUtil;
|
| 38 | +import org.apache.poi.util.LocaleUtil; |
19 | 39 |
|
20 | 40 | /**
|
21 | 41 | * Date utils
|
@@ -63,6 +83,15 @@ public class DateUtils {
|
63 | 83 |
|
64 | 84 | public static String defaultLocalDateFormat = DATE_FORMAT_10;
|
65 | 85 |
|
| 86 | + public static final int SECONDS_PER_MINUTE = 60; |
| 87 | + public static final int MINUTES_PER_HOUR = 60; |
| 88 | + public static final int HOURS_PER_DAY = 24; |
| 89 | + public static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE); |
| 90 | + |
| 91 | + // used to specify that date is invalid |
| 92 | + private static final int BAD_DATE = -1; |
| 93 | + public static final long DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L; |
| 94 | + |
66 | 95 | private DateUtils() {}
|
67 | 96 |
|
68 | 97 | /**
|
@@ -301,13 +330,75 @@ private static DateFormat getCacheDateFormat(String dateFormat) {
|
301 | 330 | * @return Java representation of the date, or null if date is not a valid Excel date
|
302 | 331 | */
|
303 | 332 | public static Date getJavaDate(double date, boolean use1904windowing) {
|
304 |
| - //To calculate the Date, in the use of `org.apache.poi.ss.usermodel.DateUtil.getJavaDate(double, boolean, |
305 |
| - // java.util.TimeZone, boolean), Date when similar `2023-01-01 00:00:00.500`, returns the`2023-01-01 |
306 |
| - // 00:00:01`, but excel in fact shows the `2023-01-01 00:00:00`. |
307 |
| - // `org.apache.poi.ss.usermodel.DateUtil.getLocalDateTime(double, boolean, boolean)` There is no problem. |
308 |
| - return Date.from(getLocalDateTime(date, use1904windowing).atZone(ZoneId.systemDefault()).toInstant()); |
| 333 | + Calendar calendar = getJavaCalendar(date, use1904windowing, null, true); |
| 334 | + return calendar == null ? null : calendar.getTime(); |
309 | 335 | }
|
310 | 336 |
|
| 337 | + /** |
| 338 | + * Get EXCEL date as Java Calendar with given time zone. |
| 339 | + * @param date The Excel date. |
| 340 | + * @param use1904windowing true if date uses 1904 windowing, |
| 341 | + * or false if using 1900 date windowing. |
| 342 | + * @param timeZone The TimeZone to evaluate the date in |
| 343 | + * @param roundSeconds round to closest second |
| 344 | + * @return Java representation of the date, or null if date is not a valid Excel date |
| 345 | + */ |
| 346 | + public static Calendar getJavaCalendar(double date, boolean use1904windowing, TimeZone timeZone, boolean roundSeconds) { |
| 347 | + if (!isValidExcelDate(date)) { |
| 348 | + return null; |
| 349 | + } |
| 350 | + int wholeDays = (int)Math.floor(date); |
| 351 | + int millisecondsInDay = (int)((date - wholeDays) * DAY_MILLISECONDS + 0.5); |
| 352 | + Calendar calendar; |
| 353 | + if (timeZone != null) { |
| 354 | + calendar = LocaleUtil.getLocaleCalendar(timeZone); |
| 355 | + } else { |
| 356 | + calendar = LocaleUtil.getLocaleCalendar(); // using default time-zone |
| 357 | + } |
| 358 | + setCalendar(calendar, wholeDays, millisecondsInDay, use1904windowing, roundSeconds); |
| 359 | + return calendar; |
| 360 | + } |
| 361 | + |
| 362 | + |
| 363 | + public static void setCalendar(Calendar calendar, int wholeDays, |
| 364 | + int millisecondsInDay, boolean use1904windowing, boolean roundSeconds) { |
| 365 | + int startYear = 1900; |
| 366 | + int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't |
| 367 | + if (use1904windowing) { |
| 368 | + startYear = 1904; |
| 369 | + dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day |
| 370 | + } |
| 371 | + else if (wholeDays < 61) { |
| 372 | + // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists |
| 373 | + // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation |
| 374 | + dayAdjust = 0; |
| 375 | + } |
| 376 | + calendar.set(startYear, Calendar.JANUARY, wholeDays + dayAdjust, 0, 0, 0); |
| 377 | + calendar.set(Calendar.MILLISECOND, millisecondsInDay); |
| 378 | + if (calendar.get(Calendar.MILLISECOND) == 0) { |
| 379 | + calendar.clear(Calendar.MILLISECOND); |
| 380 | + } |
| 381 | + if (roundSeconds) { |
| 382 | + // This is different from poi where you need to change 500 to 499 |
| 383 | + calendar.add(Calendar.MILLISECOND, 499); |
| 384 | + calendar.clear(Calendar.MILLISECOND); |
| 385 | + } |
| 386 | + } |
| 387 | + |
| 388 | + |
| 389 | + /** |
| 390 | + * Given a double, checks if it is a valid Excel date. |
| 391 | + * |
| 392 | + * @return true if valid |
| 393 | + * @param value the double value |
| 394 | + */ |
| 395 | + |
| 396 | + public static boolean isValidExcelDate(double value) |
| 397 | + { |
| 398 | + return (value > -Double.MIN_VALUE); |
| 399 | + } |
| 400 | + |
| 401 | + |
311 | 402 | /**
|
312 | 403 | * Given an Excel date with either 1900 or 1904 date windowing,
|
313 | 404 | * converts it to a java.time.LocalDateTime.
|
|
0 commit comments