Compare commits

...

21 Commits

Author SHA1 Message Date
Jiaju Zhuang aae9c61ab6
Merge pull request #4039 from alibaba/zhuangjiaju-patch-3
Update README.md
2024-10-29 14:07:06 +08:00
Jiaju Zhuang d8f1508739
Update README.md
新增公告
2024-10-29 14:06:49 +08:00
Jiaju Zhuang 3afdea9d5d
Merge pull request #3986 from alibaba/bugfix
Bugfix
2024-09-11 20:36:08 +08:00
Jiaju Zhuang 4826273f09 # 4.0.3
* 兼容部分日期格式读取异常的问题
2024-09-11 20:34:01 +08:00
Jiaju Zhuang fab7653cd4 # 4.0.2
* 兼容部分日期格式读取异常的问题
2024-09-11 20:30:11 +08:00
Jiaju Zhuang c42183df46
Merge pull request #3919 from alibaba/bugfix
Bugfix
2024-08-06 18:40:45 +08:00
Jiaju Zhuang efa7dff66b * 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误
* 重新加回 `commons-io`
2024-08-06 18:38:45 +08:00
Jiaju Zhuang be283842ef * 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误
* 重新加回 `commons-io`
2024-08-06 18:36:36 +08:00
Jiaju Zhuang c9d43270da * 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误
* 重新加回 `commons-io`
2024-08-06 18:33:52 +08:00
Jiaju Zhuang 898ca7d268
Merge pull request #3918 from alibaba/bugfix
* 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误
2024-08-06 17:54:05 +08:00
Jiaju Zhuang 71d743d7dc * 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误 2024-08-06 17:53:30 +08:00
Jiaju Zhuang 802c86fc84
Merge pull request #3917 from alibaba/bugfix
* 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误
2024-08-06 17:51:20 +08:00
Jiaju Zhuang e2f302f4cf * 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误 2024-08-06 17:43:36 +08:00
Jiaju Zhuang 3a1db020a7
Merge pull request #3916 from alibaba/bugfix
Bugfix
2024-08-06 17:36:51 +08:00
Jiaju Zhuang 1cbc7da53f * 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误 2024-08-06 17:32:57 +08:00
Jiaju Zhuang ae9f3861c4 * 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误 2024-08-06 17:32:04 +08:00
Jiaju Zhuang 4658a0e87d
Merge pull request #3875 from alibaba/bugfix
新增csv样例
2024-07-09 17:04:04 +08:00
Jiaju Zhuang 1521e9e8f0 新增csv样例 2024-07-09 17:03:13 +08:00
Jiaju Zhuang ea64d8a4d6
Merge pull request #3844 from alibaba/developing
Developing
2024-06-18 19:18:17 +08:00
Jiaju Zhuang 0eb0833235
Merge pull request #3840 from alibaba/developing
修改版本
2024-06-18 09:45:08 +08:00
Jiaju Zhuang fa592214b3
Merge pull request #3838 from alibaba/developing
Developing
2024-06-17 20:05:34 +08:00
11 changed files with 357 additions and 37 deletions

View File

@ -7,6 +7,20 @@ EasyExcel
[![License](http://img.shields.io/:license-apache-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)
[![](https://img.shields.io/badge/EasyExcel-%E6%9F%A5%E7%9C%8B%E8%B4%A1%E7%8C%AE%E6%8E%92%E8%A1%8C%E6%A6%9C-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=easyexcel)
# 维护公告
尊敬的EasyExcel用户们
首先我们想表达最深的谢意感谢您长期以来对EasyExcel的信任与支持。在这段旅程中EasyExcel有幸陪伴众多开发者和企业共同成长见证了无数数据处理任务的高效完成。
近期我们注意到市场上出现了更多优秀的数据处理工具和解决方案为用户提供了更丰富的选择。为了确保每位用户都能享受到最佳的体验和服务我们决定采取一项重要措施EasyExcel将逐步进入维护模式并给予用户充足的时间评估并迁移到其他产品。同时我们也欢迎并乐于见到社区内外分享关于同类优秀产品的使用心得和迁移经验共同促进技术生态的健康发展。
请放心即使进入维护模式我们仍会确保EasyExcel的基本功能稳定运行会进行Bug修复但不再主动新增功能。我们相信通过大家的共同努力每一段代码、每一个项目都将继续在各自的领域发光发热服务于更广泛的用户群体。
再次感谢您对EasyExcel的支持与理解期待在技术的广阔天地里我们能以新的形式再度携手共创辉煌。
祝您在未来的工作与学习中一切顺利!
阿里巴巴EasyExcel团队敬上
# 新手必读
* 官方网站:[https://easyexcel.opensource.alibaba.com/](https://easyexcel.opensource.alibaba.com/)
@ -33,7 +47,7 @@ easyexcel重写了poi对07版Excel的解析一个3M的excel用POI sax解析
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.1</version>
<version>4.0.3</version>
</dependency>
```

View File

@ -4,6 +4,7 @@ import java.util.Locale;
import java.util.Map;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.util.StringUtils;
/**
* Excel's built-in format conversion.Currently only supports Chinese.
@ -20,8 +21,116 @@ import com.alibaba.excel.util.MapUtils;
**/
public class BuiltinFormats {
private static final String RESERVED = "reserved-";
public static short GENERAL = 0;
public static final String[] BUILTIN_FORMATS_ALL_LANGUAGES = {
// 0
"General",
// 1
"0",
// 2
"0.00",
// 3
"#,##0",
// 4
"#,##0.00",
// 5
"\"\"#,##0_);(\"\"#,##0)",
// 6
"\"\"#,##0_);[Red](\"\"#,##0)",
// 7
"\"\"#,##0.00_);(\"\"#,##0.00)",
// 8
"\"\"#,##0.00_);[Red](\"\"#,##0.00)",
// 9
"0%",
// 10
"0.00%",
// 11
"0.00E+00",
// 12
"# ?/?",
// 13
"# ??/??",
// 14
// The official documentation shows "m/d/yy", but the actual test is "yyyy/m/d".
"yyyy/m/d",
// 15
"d-mmm-yy",
// 16
"d-mmm",
// 17
"mmm-yy",
// 18
"h:mm AM/PM",
// 19
"h:mm:ss AM/PM",
// 20
"h:mm",
// 21
"h:mm:ss",
// 22
// The official documentation shows "m/d/yy h:mm", but the actual test is "yyyy-m-d h:mm".
"yyyy-m-d h:mm",
// 23-36 No specific correspondence found in the official documentation.
// 23
null,
// 24
null,
// 25
null,
// 26
null,
// 27
null,
// 28
null,
// 29
null,
// 30
null,
// 31
null,
// 32
null,
// 33
null,
// 34
null,
// 35
null,
// 36
null,
// 37
"#,##0_);(#,##0)",
// 38
"#,##0_);[Red](#,##0)",
// 39
"#,##0.00_);(#,##0.00)",
// 40
"#,##0.00_);[Red](#,##0.00)",
// 41
"_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",
// 42
"_(\"\"* #,##0_);_(\"\"* (#,##0);_(\"\"* \"-\"_);_(@_)",
// 43
"_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",
// 44
"_(\"\"* #,##0.00_);_(\"\"* (#,##0.00);_(\"\"* \"-\"??_);_(@_)",
// 45
"mm:ss",
// 46
"[h]:mm:ss",
// 47
"mm:ss.0",
// 48
"##0.0E+0",
// 49
"@",
};
public static final String[] BUILTIN_FORMATS_CN = {
// 0
"General",
@ -371,8 +480,26 @@ public class BuiltinFormats {
public static final short MIN_CUSTOM_DATA_FORMAT_INDEX = 82;
public static String getBuiltinFormat(Short index, String defaultFormat, Locale locale) {
if (index == null || index <= 0) {
return defaultFormat;
}
// Give priority to checking if it is the default value for all languages
if (index < BUILTIN_FORMATS_ALL_LANGUAGES.length) {
String format = BUILTIN_FORMATS_ALL_LANGUAGES[index];
if (format != null) {
return format;
}
}
// In other cases, give priority to using the externally provided format
if (!StringUtils.isEmpty(defaultFormat) && !defaultFormat.startsWith(RESERVED)) {
return defaultFormat;
}
// Finally, try using the built-in format
String[] builtinFormat = switchBuiltinFormats(locale);
if (index == null || index < 0 || index >= builtinFormat.length) {
if (index >= builtinFormat.length) {
return defaultFormat;
}
return builtinFormat[index];

View File

@ -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.
@ -470,31 +561,10 @@ public class DateUtils {
case 0x14:
case 0x15:
case 0x16:
// 27-36
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
// 45-47
// 45-47
case 0x2d:
case 0x2e:
case 0x2f:
// 50-58
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
return true;
}
return false;

View File

@ -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,19 @@ public class DateFormatTest {
readUs(file03);
}
@Test
public void t03Read() {
List<Map<Integer, String>> dataMap = EasyExcel.read(file07V2).headRowNumber(0).doReadAllSync();
log.info("dataMap:{}", JSON.toJSONString(dataMap));
Assertions.assertEquals("15:00", dataMap.get(0).get(0));
Assertions.assertEquals("2023-1-01 00:00:00", dataMap.get(1).get(0));
Assertions.assertEquals("2023-1-01 00:00:00", dataMap.get(2).get(0));
Assertions.assertEquals("2023-1-01 00:00:01", dataMap.get(3).get(0));
Assertions.assertEquals("2023-1-01 00:00:00", dataMap.get(4).get(0));
Assertions.assertEquals("2023-1-01 00:00:00", dataMap.get(5).get(0));
Assertions.assertEquals("2023-1-01 00:00:01", dataMap.get(6).get(0));
}
private void readCn(File file) {
List<DateFormatData> list =
EasyExcel.read(file, DateFormatData.class, null).locale(Locale.CHINA).sheet().doReadSync();

View File

@ -16,6 +16,7 @@ import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson2.JSON;
@ -309,4 +310,28 @@ public class ReadTest {
// 这里 只要然后读取第一个sheet 同步读取会自动finish
EasyExcel.read(fileName, new NoModelDataListener()).sheet().doRead();
}
/**
* 自定义修改csv配置
*/
@Test
public void csvFormat() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.csv";
try (ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build()) {
// 判断是 csv 文件
if (excelReader.analysisContext().readWorkbookHolder() instanceof CsvReadWorkbookHolder) {
CsvReadWorkbookHolder csvReadWorkbookHolder = (CsvReadWorkbookHolder)excelReader.analysisContext()
.readWorkbookHolder();
// 设置成逗号分隔 当然默认也是逗号分隔
// 这里要注意 withDelimiter 会重新生成一个 所以要放回去
csvReadWorkbookHolder.setCsvFormat(csvReadWorkbookHolder.getCsvFormat().withDelimiter(','));
}
// 拿到所有 sheet
List<ReadSheet> readSheetList = excelReader.excelExecutor().sheetList();
// 如果只想读取第一个 咋样传入参数即可
//ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheetList);
}
}
}

View File

@ -52,8 +52,7 @@ public class Lock2Test {
File file = new File("/Users/zhuangjiaju/IdeaProjects/easyexcel/src/test/resources/converter/converter07.xlsx");
List<Object> list = EasyExcel.read(
"/Users/zhuangjiaju/IdeaProjects/easyexcel/easyexcel-test/target/test-classes"
+ "/simpleWrite1674051907397.xlsx")
"/Users/zhuangjiaju/Downloads/证券投资基金估值表_外贸信托-稳盈淳享37号集合资金信托计划_2024-07-23(1).xls")
//.useDefaultListener(false)
.sheet(0)
.headRowNumber(0).doReadSync();

View File

@ -0,0 +1,40 @@
package com.alibaba.easyexcel.test.temp.poi;
import java.io.IOException;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 测试poi
*
* @author Jiaju Zhuang
**/
public class PoiDateFormatTest {
private static final Logger LOGGER = LoggerFactory.getLogger(PoiDateFormatTest.class);
@Test
public void read() throws IOException {
String file
= "/Users/zhuangjiaju/IdeaProjects/easyexcel/easyexcel-test/src/test/resources/dataformat/dataformat.xlsx";
XSSFWorkbook xssfWorkbook = new XSSFWorkbook( file);
XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum());
XSSFRow row = xssfSheet.getRow(7);
XSSFCell cell = row.getCell(0);
LOGGER.info("dd{}", cell.getDateCellValue());
LOGGER.info("dd{}", cell.getNumericCellValue());
LOGGER.info("dd{}", DateUtil.isCellDateFormatted(cell));
}
}

View File

@ -0,0 +1,11 @@
字符串标题,日期标题,数字标题
字符串0,2020-01-01 01:01:00,1
字符串1,2020-01-02 01:01:00,2
字符串2,2020-01-03 01:01:00,3
字符串3,2020-01-04 01:01:00,4
字符串4,2020-01-05 01:01:00,5
字符串5,2020-01-06 01:01:00,6
字符串6,2020-01-07 01:01:00,7
字符串7,2020-01-08 01:01:00,8
字符串8,2020-01-09 01:01:00,9
字符串9,2020-01-10 01:01:00,10
1 字符串标题 日期标题 数字标题
2 字符串0 2020-01-01 01:01:00 1
3 字符串1 2020-01-02 01:01:00 2
4 字符串2 2020-01-03 01:01:00 3
5 字符串3 2020-01-04 01:01:00 4
6 字符串4 2020-01-05 01:01:00 5
7 字符串5 2020-01-06 01:01:00 6
8 字符串6 2020-01-07 01:01:00 7
9 字符串7 2020-01-08 01:01:00 8
10 字符串8 2020-01-09 01:01:00 9
11 字符串9 2020-01-10 01:01:00 10

19
pom.xml
View File

@ -20,7 +20,7 @@
<properties>
<revision>4.0.1</revision>
<revision>4.0.3</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.8</jdk.version>
<gpg.skip>true</gpg.skip>
@ -93,6 +93,12 @@
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
@ -103,16 +109,17 @@
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.9.11</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

View File

@ -1,3 +1,12 @@
# 4.0.3
* 兼容部分日期格式读取异常的问题
# 4.0.2
* 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误
* 重新加回 `commons-io`
# 4.0.1
* `commons-io` 修改为依赖 `poi`的版本