diff --git a/README.md b/README.md index 960ec63e..7987c16c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析 com.alibaba easyexcel - 3.1.5 + 3.2.0 ``` diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java index 09188cb2..dcf7f548 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java @@ -5,6 +5,7 @@ import java.util.Map; import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.constant.EasyExcelConstants; import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.RowTypeEnum; @@ -56,7 +57,9 @@ public class FormulaRecordHandler extends AbstractXlsRecordHandler implements Ig break; case NUMERIC: tempCellData.setType(CellDataTypeEnum.NUMBER); - tempCellData.setNumberValue(BigDecimal.valueOf(frec.getValue())); + tempCellData.setOriginalNumberValue(BigDecimal.valueOf(frec.getValue())); + tempCellData.setNumberValue( + tempCellData.getOriginalNumberValue().round(EasyExcelConstants.EXCEL_MATH_CONTEXT)); int dataFormat = xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex(frec); DataFormatData dataFormatData = new DataFormatData(); diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java index 1573b8e7..031b895f 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java @@ -4,6 +4,7 @@ import java.math.BigDecimal; import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.constant.EasyExcelConstants; import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.enums.RowTypeEnum; import com.alibaba.excel.metadata.data.DataFormatData; @@ -22,7 +23,7 @@ public class NumberRecordHandler extends AbstractXlsRecordHandler implements Ign @Override public void processRecord(XlsReadContext xlsReadContext, Record record) { NumberRecord nr = (NumberRecord)record; - ReadCellData cellData = ReadCellData.newInstance(BigDecimal.valueOf(nr.getValue()), nr.getRow(), + ReadCellData cellData = ReadCellData.newInstanceOriginal(BigDecimal.valueOf(nr.getValue()), nr.getRow(), (int)nr.getColumn()); short dataFormat = (short)xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex( nr); diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java index ce84d5e2..75e86aab 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java @@ -2,6 +2,7 @@ package com.alibaba.excel.analysis.v07.handlers; import java.math.BigDecimal; +import com.alibaba.excel.constant.EasyExcelConstants; import com.alibaba.excel.constant.ExcelXmlConstants; import com.alibaba.excel.context.xlsx.XlsxReadContext; import com.alibaba.excel.enums.CellDataTypeEnum; @@ -88,7 +89,9 @@ public class CellTagHandler extends AbstractXlsxTagHandler { break; } tempCellData.setType(CellDataTypeEnum.NUMBER); - tempCellData.setNumberValue(BigDecimal.valueOf(Double.parseDouble(tempDataString))); + tempCellData.setOriginalNumberValue(new BigDecimal(tempDataString)); + tempCellData.setNumberValue( + tempCellData.getOriginalNumberValue().round(EasyExcelConstants.EXCEL_MATH_CONTEXT)); break; default: throw new IllegalStateException("Cannot set values now"); diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java new file mode 100644 index 00000000..cdc414fa --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java @@ -0,0 +1,19 @@ +package com.alibaba.excel.constant; + +import java.math.MathContext; +import java.math.RoundingMode; + +/** + * Used to store constant + * + * @author Jiaju Zhuang + */ +public class EasyExcelConstants { + + /** + * Excel by default with 15 to store Numbers, and the double in Java can use to store number 17, led to the accuracy + * will be a problem. So you need to set up 15 to deal with precision + */ + public static final MathContext EXCEL_MATH_CONTEXT = new MathContext(15, RoundingMode.HALF_UP); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java index 34bb7536..02ba26aa 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java @@ -9,6 +9,7 @@ import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.data.ReadCellData; import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; import org.apache.poi.ss.usermodel.DateUtil; @@ -33,11 +34,11 @@ public class DateNumberConverter implements Converter { public Date convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { - return DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), - globalConfiguration.getUse1904windowing(), null); + return DateUtils.getJavaDate(cellData.getNumberValue().doubleValue(), + globalConfiguration.getUse1904windowing()); } else { - return DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), - contentProperty.getDateTimeFormatProperty().getUse1904windowing(), null); + return DateUtils.getJavaDate(cellData.getNumberValue().doubleValue(), + contentProperty.getDateTimeFormatProperty().getUse1904windowing()); } } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateNumberConverter.java index f57f0bdc..4542fd08 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateNumberConverter.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateNumberConverter.java @@ -9,6 +9,7 @@ import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.data.ReadCellData; import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; import org.apache.poi.ss.usermodel.DateUtil; @@ -33,10 +34,10 @@ public class LocalDateNumberConverter implements Converter { public LocalDateTime convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { - return DateUtil.getLocalDateTime(cellData.getNumberValue().doubleValue(), + return DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(), globalConfiguration.getUse1904windowing()); } else { - return DateUtil.getLocalDateTime(cellData.getNumberValue().doubleValue(), + return DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(), contentProperty.getDateTimeFormatProperty().getUse1904windowing()); } } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java index 0d0813d0..ff96317d 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java @@ -37,9 +37,8 @@ public class StringNumberConverter implements Converter { GlobalConfiguration globalConfiguration) { // If there are "DateTimeFormat", read as date if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { - return DateUtils.format( - DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), - contentProperty.getDateTimeFormatProperty().getUse1904windowing(), null), + return DateUtils.format(cellData.getNumberValue(), + contentProperty.getDateTimeFormatProperty().getUse1904windowing(), contentProperty.getDateTimeFormatProperty().getFormat()); } // If there are "NumberFormat", read as number diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/ReadDefaultReturnEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/ReadDefaultReturnEnum.java new file mode 100644 index 00000000..c0340d83 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/ReadDefaultReturnEnum.java @@ -0,0 +1,40 @@ +package com.alibaba.excel.enums; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.excel.util.StringUtils; + +/** + * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type. + * + * @author Jiaju Zhuang + */ +public enum ReadDefaultReturnEnum { + /** + * default.The content of cells into string, is the same as you see in the excel. + */ + STRING, + + /** + * Returns the actual type. + * Will be automatically selected according to the cell contents what return type, will return the following class: + *
    + *
  1. {@link BigDecimal}
  2. + *
  3. {@link Boolean}
  4. + *
  5. {@link String}
  6. + *
  7. {@link LocalDateTime}
  8. + *
      + */ + ACTUAL_DATA, + + /** + * Return to {@link com.alibaba.excel.metadata.data.ReadCellData}, can decide which field you need. + */ + READ_CELL_DATA, + ; + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java index 2f74ea9b..e70228f7 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java @@ -1,6 +1,7 @@ package com.alibaba.excel.metadata.data; import java.math.BigDecimal; +import java.time.LocalDateTime; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.AbstractCell; @@ -57,6 +58,7 @@ public class CellData extends AbstractCell { } switch (type) { case STRING: + case DIRECT_STRING: case ERROR: if (StringUtils.isEmpty(stringValue)) { type = CellDataTypeEnum.EMPTY; @@ -76,5 +78,4 @@ public class CellData extends AbstractCell { } } - } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java index e1bb57ca..9432e994 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java @@ -1,7 +1,9 @@ package com.alibaba.excel.metadata.data; import java.math.BigDecimal; +import java.time.LocalDateTime; +import com.alibaba.excel.constant.EasyExcelConstants; import com.alibaba.excel.enums.CellDataTypeEnum; import lombok.EqualsAndHashCode; @@ -11,6 +13,7 @@ import lombok.Setter; /** * read cell data + *

      * * @author Jiaju Zhuang */ @@ -19,6 +22,29 @@ import lombok.Setter; @EqualsAndHashCode @NoArgsConstructor public class ReadCellData extends CellData { + + /** + * originalNumberValue vs numberValue + *

        + *
      1. + * NUMBER: + * originalNumberValue: Original data and the accuracy of his is 17, but in fact the excel only 15 precision to + * process the data + * numberValue: After correction of the data and the accuracy of his is 15 + * for example, originalNumberValue = `2087.0249999999996` , numberValue = `2087.03` + *
      2. + *
      3. + * DATE: + * originalNumberValue: Storage is a data type double, accurate to milliseconds + * dateValue: Based on double converted to a date format, he will revised date difference, accurate to seconds + * for example, originalNumberValue = `44729.99998836806` ,time is:`2022-06-17 23:59:58.995`, + * But in excel is displayed:` 2022-06-17 23:59:59`, dateValue = `2022-06-17 23:59:59` + *
      4. + *
          + * {@link CellDataTypeEnum#NUMBER} {@link CellDataTypeEnum#DATE} + */ + private BigDecimal originalNumberValue; + /** * data format. */ @@ -107,11 +133,21 @@ public class ReadCellData extends CellData { return cellData; } + public static ReadCellData newInstanceOriginal(BigDecimal numberValue, Integer rowIndex, Integer columnIndex) { + ReadCellData cellData = new ReadCellData<>(numberValue); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + cellData.setOriginalNumberValue(numberValue); + cellData.setNumberValue(numberValue.round(EasyExcelConstants.EXCEL_MATH_CONTEXT)); + return cellData; + } + @Override public ReadCellData clone() { ReadCellData readCellData = new ReadCellData<>(); readCellData.setType(getType()); readCellData.setNumberValue(getNumberValue()); + readCellData.setOriginalNumberValue(getOriginalNumberValue()); readCellData.setStringValue(getStringValue()); readCellData.setBooleanValue(getBooleanValue()); readCellData.setData(getData()); @@ -123,5 +159,4 @@ public class ReadCellData extends CellData { } return readCellData; } - } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java index ba8fee47..1bd591e3 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java @@ -212,10 +212,10 @@ public class DataFormatter { CellFormat cfmt = CellFormat.getInstance(locale, formatStr); // CellFormat requires callers to identify date vs not, so do so Object cellValueO = data; - if (DateUtil.isADateFormat(dataFormat, formatStr) && + if (DateUtils.isADateFormat(dataFormat, formatStr) && // don't try to handle Date value 0, let a 3 or 4-part format take care of it data.doubleValue() != 0.0) { - cellValueO = DateUtil.getJavaDate(data, use1904windowing); + cellValueO = DateUtils.getJavaDate(data, use1904windowing); } // Wrap and return (non-cachable - CellFormat does that) return new CellFormatResultWrapper(cfmt.apply(cellValueO)); @@ -243,6 +243,8 @@ public class DataFormatter { return format; } + + private Format createFormat(Short dataFormat, String dataFormatString) { String formatStr = dataFormatString; @@ -628,7 +630,7 @@ public class DataFormatter { // Hint about the raw excel value ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(data); } - return performDateFormatting(DateUtil.getJavaDate(data, use1904windowing), dateFormat); + return performDateFormatting(DateUtils.getJavaDate(data, use1904windowing), dateFormat); } /** diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java index d2a24b44..59fd7e1f 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java @@ -13,6 +13,7 @@ import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.SyncReadListener; import com.alibaba.excel.read.listener.ModelBuildEventListener; @@ -198,6 +199,17 @@ public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder> cellDataMap, ReadSheetHolder readSheetHolder, + private Object buildNoModel(Map> cellDataMap, ReadSheetHolder readSheetHolder, AnalysisContext context) { int index = 0; - Map map = MapUtils.newLinkedHashMapWithExpectedSize(cellDataMap.size()); + Map map = MapUtils.newLinkedHashMapWithExpectedSize(cellDataMap.size()); for (Map.Entry> entry : cellDataMap.entrySet()) { Integer key = entry.getKey(); ReadCellData cellData = entry.getValue(); @@ -48,9 +57,23 @@ public class ModelBuildEventListener implements IgnoreExceptionReadListener convertedReadCellData = convertReadCellData(cellData, + context.readWorkbookHolder().getReadDefaultReturn(), readSheetHolder, context, key); + if (readDefaultReturn == ReadDefaultReturnEnum.READ_CELL_DATA) { + map.put(key, convertedReadCellData); + } else { + map.put(key, convertedReadCellData.getData()); + } + } } // fix https://github.com/alibaba/easyexcel/issues/2014 int headSize = calculateHeadSize(readSheetHolder); @@ -61,6 +84,38 @@ public class ModelBuildEventListener implements IgnoreExceptionReadListener cellData, ReadDefaultReturnEnum readDefaultReturn, + ReadSheetHolder readSheetHolder, AnalysisContext context, Integer columnIndex) { + Class classGeneric; + switch (cellData.getType()) { + case STRING: + case DIRECT_STRING: + case ERROR: + case EMPTY: + classGeneric = String.class; + break; + case BOOLEAN: + classGeneric = Boolean.class; + break; + case NUMBER: + DataFormatData dataFormatData = cellData.getDataFormatData(); + if (dataFormatData != null && DateUtils.isADateFormat(dataFormatData.getIndex(), + dataFormatData.getFormat())) { + classGeneric = LocalDateTime.class; + } else { + classGeneric = BigDecimal.class; + } + break; + default: + classGeneric = ConverterUtils.defaultClassGeneric; + break; + } + + return (ReadCellData)ConverterUtils.convertToJavaObject(cellData, null, ReadCellData.class, + classGeneric, null, readSheetHolder.converterMap(), context, context.readRowHolder().getRowIndex(), + columnIndex); + } + private int calculateHeadSize(ReadSheetHolder readSheetHolder) { if (readSheetHolder.excelReadHeadProperty().getHeadMap().size() > 0) { return readSheetHolder.excelReadHeadProperty().getHeadMap().size(); diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java index 4f3a3aab..a3713d7c 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java @@ -11,6 +11,7 @@ import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.read.listener.ModelBuildEventListener; import com.alibaba.excel.support.ExcelTypeEnum; @@ -62,7 +63,6 @@ public class ReadWorkbook extends ReadBasicParameter { /** * This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)} * {@link AnalysisContext#getCustom()} - * */ private Object customObject; /** @@ -96,8 +96,18 @@ public class ReadWorkbook extends ReadBasicParameter { * Whether to use the default listener, which is used by default. *

          * The {@link ModelBuildEventListener} is loaded by default to convert the object. + * defualt is true. */ private Boolean useDefaultListener; + + /** + * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type. + * Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`. + * + * @see ReadDefaultReturnEnum + */ + private ReadDefaultReturnEnum readDefaultReturn; + /** * Read some additional fields. None are read by default. * diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java index a84cc315..63f8c1bf 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java @@ -14,6 +14,7 @@ import com.alibaba.excel.cache.selector.SimpleReadCacheSelector; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.read.metadata.ReadSheet; @@ -64,10 +65,20 @@ public class ReadWorkbookHolder extends AbstractReadHolder { * if false, Will transfer 'inputStream' to temporary files to improve efficiency */ private Boolean mandatoryUseInputStream; + /** * Default true */ private Boolean autoCloseStream; + + /** + * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type. + * Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`. + * + * @see ReadDefaultReturnEnum + */ + private ReadDefaultReturnEnum readDefaultReturn; + /** * Excel type */ @@ -146,6 +157,12 @@ public class ReadWorkbookHolder extends AbstractReadHolder { this.autoCloseStream = readWorkbook.getAutoCloseStream(); } + if (readWorkbook.getReadDefaultReturn() == null) { + this.readDefaultReturn = ReadDefaultReturnEnum.STRING; + } else { + this.readDefaultReturn = readWorkbook.getReadDefaultReturn(); + } + this.customObject = readWorkbook.getCustomObject(); if (readWorkbook.getIgnoreEmptyRow() == null) { this.ignoreEmptyRow = Boolean.TRUE; diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java index 02ea4a00..6359a99d 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java @@ -84,24 +84,52 @@ public class ConverterUtils { public static Object convertToJavaObject(ReadCellData cellData, Field field, ExcelContentProperty contentProperty, Map> converterMap, AnalysisContext context, Integer rowIndex, Integer columnIndex) { - Class clazz; - if (field == null) { - clazz = String.class; - } else { - clazz = field.getType(); + return convertToJavaObject(cellData, field, null, null, contentProperty, converterMap, context, rowIndex, + columnIndex); + } + + /** + * Convert it into a Java object + * + * @param cellData + * @param field + * @param clazz + * @param contentProperty + * @param converterMap + * @param context + * @param rowIndex + * @param columnIndex + * @return + */ + public static Object convertToJavaObject(ReadCellData cellData, Field field, Class clazz, + Class classGeneric, ExcelContentProperty contentProperty, Map> converterMap, + AnalysisContext context, Integer rowIndex, Integer columnIndex) { + if (clazz == null) { + if (field == null) { + clazz = String.class; + } else { + clazz = field.getType(); + } } if (clazz == CellData.class || clazz == ReadCellData.class) { - Class classGeneric = getClassGeneric(field.getGenericType()); ReadCellData cellDataReturn = cellData.clone(); - cellDataReturn.setData(doConvertToJavaObject(cellData, classGeneric, contentProperty, converterMap, - context, rowIndex, columnIndex)); + cellDataReturn.setData( + doConvertToJavaObject(cellData, getClassGeneric(field, classGeneric), contentProperty, + converterMap, context, rowIndex, columnIndex)); return cellDataReturn; } return doConvertToJavaObject(cellData, clazz, contentProperty, converterMap, context, rowIndex, columnIndex); } - private static Class getClassGeneric(Type type) { + private static Class getClassGeneric(Field field, Class classGeneric) { + if (classGeneric != null) { + return classGeneric; + } + if (field == null) { + return defaultClassGeneric; + } + Type type = field.getGenericType(); if (!(type instanceof ParameterizedType)) { return defaultClassGeneric; } 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 22d7f3d9..fb19a105 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,9 +1,11 @@ package com.alibaba.excel.util; +import java.math.BigDecimal; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.HashMap; @@ -11,6 +13,8 @@ import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; +import org.apache.poi.ss.usermodel.DateUtil; + /** * Date utils * @@ -184,6 +188,33 @@ public class DateUtils { } } + /** + * Format date + * + * @param date + * @param dateFormat + * @return + */ + public static String format(LocalDateTime date, String dateFormat) { + return format(date, dateFormat, null); + } + + /** + * Format date + * + * @param date + * @param dateFormat + * @return + */ + public static String format(BigDecimal date, Boolean use1904windowing, String dateFormat) { + if (date == null) { + return null; + } + LocalDateTime localDateTime = DateUtil.getLocalDateTime(date.doubleValue(), + BooleanUtils.isTrue(use1904windowing), true); + return format(localDateTime, dateFormat); + } + private static DateFormat getCacheDateFormat(String dateFormat) { Map dateFormatMap = DATE_FORMAT_THREAD_LOCAL.get(); if (dateFormatMap == null) { @@ -200,6 +231,46 @@ public class DateUtils { return simpleDateFormat; } + /** + * Given an Excel date with either 1900 or 1904 date windowing, + * converts it to a java.util.Date. + * + * 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 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()); + } + + /** + * 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 DateUtil.getLocalDateTime(date, use1904windowing, true); + } + /** * Determine if it is a date format. * diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java index eadfd0a8..f4aae4a8 100644 --- a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java @@ -1,10 +1,13 @@ package com.alibaba.easyexcel.test.core.compatibility; +import java.math.BigDecimal; import java.util.List; import java.util.Map; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.constant.EasyExcelConstants; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; @@ -64,4 +67,53 @@ public class CompatibilityTest { Map row0 = list.get(0); Assert.assertEquals("QQSJK28F152A012242S0081", row0.get(5)); } + + @Test + public void t05() { + // https://github.com/alibaba/easyexcel/issues/1956 + // Excel read date needs to be rounded + List> list = EasyExcel + .read(TestFileUtil.getPath() + "compatibility/t05.xlsx") + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assert.assertEquals("2023-01-01 00:00:00", list.get(0).get(0)); + Assert.assertEquals("2023-01-01 00:00:00", list.get(1).get(0)); + Assert.assertEquals("2023-01-01 00:00:00", list.get(2).get(0)); + Assert.assertEquals("2023-01-01 00:00:01", list.get(3).get(0)); + Assert.assertEquals("2023-01-01 00:00:01", list.get(4).get(0)); + } + + @Test + public void t06() { + // Keep error precision digital format + List> list = EasyExcel + .read(TestFileUtil.getPath() + "compatibility/t06.xlsx") + .headRowNumber(0) + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assert.assertEquals("2087.03", list.get(0).get(2)); + } + + @Test + public void t07() { + // https://github.com/alibaba/easyexcel/issues/2805 + // Excel read date needs to be rounded + List> list = EasyExcel + .read(TestFileUtil.getPath() + "compatibility/t07.xlsx") + .readDefaultReturn(ReadDefaultReturnEnum.ACTUAL_DATA) + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assert.assertEquals(0, new BigDecimal("24.1998124").compareTo((BigDecimal)list.get(0).get(11))); + + list = EasyExcel + .read(TestFileUtil.getPath() + "compatibility/t07.xlsx") + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assert.assertEquals("24.20", list.get(0).get(11)); + } + } diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java index 262ae35d..5abbcba7 100644 --- a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java @@ -1,14 +1,20 @@ package com.alibaba.easyexcel.test.core.nomodel; import java.io.File; +import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; +import com.alibaba.excel.metadata.data.ReadCellData; import com.alibaba.excel.util.DateUtils; +import com.alibaba.fastjson2.JSON; +import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.FixMethodOrder; @@ -19,6 +25,7 @@ import org.junit.runners.MethodSorters; * @author Jiaju Zhuang */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Slf4j public class NoModelDataTest { private static File file07; @@ -28,7 +35,6 @@ public class NoModelDataTest { private static File fileRepeat03; private static File fileRepeatCsv; - @BeforeClass public static void init() { file07 = TestFileUtil.createNewFile("noModel07.xlsx"); @@ -41,20 +47,20 @@ public class NoModelDataTest { @Test public void t01ReadAndWrite07() throws Exception { - readAndWrite(file07, fileRepeat07); + readAndWrite(file07, fileRepeat07, false); } @Test public void t02ReadAndWrite03() throws Exception { - readAndWrite(file03, fileRepeat03); + readAndWrite(file03, fileRepeat03, false); } @Test public void t03ReadAndWriteCsv() throws Exception { - readAndWrite(fileCsv, fileRepeatCsv); + readAndWrite(fileCsv, fileRepeatCsv, true); } - private void readAndWrite(File file, File fileRepeat) throws Exception { + private void readAndWrite(File file, File fileRepeat, boolean isCsv) throws Exception { EasyExcel.write(file).sheet().doWrite(data()); List> result = EasyExcel.read(file).headRowNumber(0).sheet().doReadSync(); Assert.assertEquals(10, result.size()); @@ -63,6 +69,42 @@ public class NoModelDataTest { Assert.assertEquals("109", data10.get(1)); Assert.assertEquals("2020-01-01 01:01:01", data10.get(2)); + List> actualDataList = EasyExcel.read(file) + .headRowNumber(0) + .readDefaultReturn(ReadDefaultReturnEnum.ACTUAL_DATA) + .sheet() + .doReadSync(); + log.info("actualDataList:{}", JSON.toJSONString(actualDataList)); + Assert.assertEquals(10, actualDataList.size()); + Map actualData10 = actualDataList.get(9); + Assert.assertEquals("string19", actualData10.get(0)); + if (isCsv) { + // CSV only string type + Assert.assertEquals("109", actualData10.get(1)); + Assert.assertEquals("2020-01-01 01:01:01", actualData10.get(2)); + } else { + Assert.assertEquals(0, new BigDecimal("109").compareTo((BigDecimal)actualData10.get(1))); + Assert.assertEquals(LocalDateTime.of(2020, 1, 1, 1, 1, 1), actualData10.get(2)); + } + + List>> readCellDataList = EasyExcel.read(file) + .headRowNumber(0) + .readDefaultReturn(ReadDefaultReturnEnum.READ_CELL_DATA) + .sheet() + .doReadSync(); + log.info("readCellDataList:{}", JSON.toJSONString(readCellDataList)); + Assert.assertEquals(10, readCellDataList.size()); + Map> readCellData10 = readCellDataList.get(9); + Assert.assertEquals("string19", readCellData10.get(0).getData()); + if (isCsv) { + // CSV only string type + Assert.assertEquals("109", readCellData10.get(1).getData()); + Assert.assertEquals("2020-01-01 01:01:01", readCellData10.get(2).getData()); + } else { + Assert.assertEquals(0, new BigDecimal("109").compareTo((BigDecimal)readCellData10.get(1).getData())); + Assert.assertEquals(LocalDateTime.of(2020, 1, 1, 1, 1, 1), readCellData10.get(2).getData()); + } + EasyExcel.write(fileRepeat).sheet().doWrite(result); result = EasyExcel.read(fileRepeat).headRowNumber(0).sheet().doReadSync(); Assert.assertEquals(10, result.size()); diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java index 76b50584..fc3079a1 100644 --- a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java @@ -24,6 +24,7 @@ public class DemoData { private Date date; @ExcelProperty("数字标题") private Double doubleData; + /** * 忽略这个字段 */ diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData2.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData2.java new file mode 100644 index 00000000..98693438 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData2.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.temp; + +import java.math.BigDecimal; +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class DemoData2 { + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; + @ExcelProperty("数字标题2") + private BigDecimal bigDecimal; + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData3.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData3.java new file mode 100644 index 00000000..0be8ec7a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData3.java @@ -0,0 +1,24 @@ +package com.alibaba.easyexcel.test.temp; + +import java.time.LocalDateTime; +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class DemoData3 { + @ExcelProperty("日期时间标题") + private LocalDateTime localDateTime; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java index 63141ba4..8a782bbe 100644 --- a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java @@ -1,32 +1,49 @@ package com.alibaba.easyexcel.test.temp; import java.io.File; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; import com.alibaba.easyexcel.test.demo.write.DemoData; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.util.NumberDataFormatterUtils; +import com.alibaba.excel.util.NumberUtils; import com.alibaba.excel.util.PositionUtils; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.poi.hssf.util.CellReference; +import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.IndexedColors; +import org.junit.Assert; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; /** * 临时测试 * * @author Jiaju Zhuang **/ +@Slf4j public class Lock2Test { private static final Logger LOGGER = LoggerFactory.getLogger(Lock2Test.class); @@ -37,7 +54,11 @@ public class Lock2Test { // File file = TestFileUtil.readUserHomeFile("test/test6.xls"); File file = new File("/Users/zhuangjiaju/IdeaProjects/easyexcel/src/test/resources/converter/converter07.xlsx"); - List list = EasyExcel.read("/Users/zhuangjiaju/Downloads/测试格式.xlsx").sheet(0).headRowNumber(0).doReadSync(); + List list = EasyExcel.read( + "/Users/zhuangjiaju/IdeaProjects/easyexcel/easyexcel-test/target/test-classes/simpleWrite1674051907397.xlsx") + //.useDefaultListener(false) + .sheet(0) + .headRowNumber(0).doReadSync(); LOGGER.info("数据:{}", list.size()); for (Object data : list) { LOGGER.info("返回数据:{}", CollectionUtils.size(data)); @@ -109,18 +130,6 @@ public class Lock2Test { return list; } - private List data() { - List list = new ArrayList(); - for (int i = 0; i < 10; i++) { - DemoData data = new DemoData(); - data.setString("字符串" + i); - data.setDate(new Date()); - data.setDoubleData(0.56); - list.add(data); - } - return list; - } - @Test public void testc() throws Exception { LOGGER.info("reslut:{}", JSON.toJSONString(new CellReference("B3"))); @@ -151,10 +160,9 @@ public class Lock2Test { @Test public void test335() throws Exception { - - LOGGER.info("reslut:{}", PositionUtils.getCol("A10",null)); + LOGGER.info("reslut:{}", PositionUtils.getCol("A10", null)); LOGGER.info("reslut:{}", PositionUtils.getRow("A10")); - LOGGER.info("reslut:{}", PositionUtils.getCol("AB10",null)); + LOGGER.info("reslut:{}", PositionUtils.getCol("AB10", null)); LOGGER.info("reslut:{}", PositionUtils.getRow("AB10")); //LOGGER.info("reslut:{}", PositionUtils2.getCol("A10",null)); @@ -163,5 +171,279 @@ public class Lock2Test { //LOGGER.info("reslut:{}", PositionUtils2.getRow("AB10")); } + @Test + public void numberforamt() throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44727.99998842592), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + // + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44728.99998842592), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + // + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44729.99998836806), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + // + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44727.99998842592).setScale(10, RoundingMode + // .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + // + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44728.99998842592).setScale(10, RoundingMode + // .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + + //44729.9999883681 + //44729.999988368058 + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44729.999988368058).setScale(10, RoundingMode + // .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + //LOGGER.info("date:{}",BigDecimal.valueOf(44729.999988368058).setScale(10, RoundingMode.HALF_UP).doubleValue + // ()); + + // 2022/6/17 23:59:59 + // 期望 44729.99998842592 + //LOGGER.info("data:{}", DateUtil.getJavaDate(44729.9999883681, true)); + LOGGER.info("data4:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(4, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data5:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(5, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data6:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(6, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data7:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(7, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data8:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(8, RoundingMode.HALF_UP).doubleValue(), false)); + + LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.999988368058, false))); + LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.9999883681, false))); + + LOGGER.info("data:{}", DateUtil.getJavaDate(Double.parseDouble("44729.999988368058"), false)); + LOGGER.info("data:{}", DateUtil.getJavaDate(Double.parseDouble("44729.9999883681"), false)); + + // 44729.999976851854 + // 44729.999988368058 + LOGGER.info("data:{}", DateUtil.getExcelDate(format.parse("2022-06-17 23:59:58"))); + // 44729.99998842592 + LOGGER.info("data:{}", DateUtil.getExcelDate(format.parse("2022-06-17 23:59:59"))); + + LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999976851854) + .setScale(10, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.99998842592) + .setScale(10, RoundingMode.HALF_UP).doubleValue(), false)); + + LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999976851854) + .setScale(5, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.99998842592) + .setScale(5, RoundingMode.HALF_UP).doubleValue(), false)); + } + + @Test + public void testDate() throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + log.info("TT:{}", format.format(new Date(100L))); + log.info("TT:{}", new Date().getTime()); + } + + @Test + public void testDateAll() throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + long dateTime = 0L; + while (true) { + Date date = new Date(dateTime); + double excelDate = DateUtil.getExcelDate(date); + + Assert.assertEquals("测试基本转换错误" + dateTime, format.format(date), + format.format(DateUtil.getJavaDate(excelDate, false))); + Assert.assertEquals("测试精度5转换错误" + dateTime, format.format(date), + format.format(DateUtil.getJavaDate(BigDecimal.valueOf(excelDate) + .setScale(10, RoundingMode.HALF_UP).doubleValue(), false))); + LOGGER.info("date:{}", format2.format(DateUtil.getJavaDate(BigDecimal.valueOf(excelDate) + .setScale(10, RoundingMode.HALF_UP).doubleValue()))); + dateTime += 1000L; + // 30天输出 + if (dateTime % (24 * 60 * 60 * 1000) == 0) { + log.info("{}成功", format.format(date)); + } + if (dateTime > 1673957544750L) { + log.info("结束啦"); + break; + } + } + log.info("结束啦"); + + } + + @Test + public void numberforamt3() throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + List> list = EasyExcel.read("/Users/zhuangjiaju/Downloads/date3.xlsx") + .useDefaultListener(false) + .sheet(0) + .headRowNumber(0).doReadSync(); + LOGGER.info("数据:{}", list.size()); + for (Map readCellDataMap : list) { + ReadCellData data = readCellDataMap.get(0); + LOGGER.info("data:{}", format.format( + DateUtil.getJavaDate(data.getNumberValue().setScale(10, RoundingMode.HALF_UP).doubleValue(), false))); + + } + // + //LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44727.999988425923, false))); + //LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.999988368058, false))); + + } + + @Test + public void numberforamt4() throws Exception { + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData.class) + .sheet("模板") + .doWrite(() -> { + // 分页查询数据 + return data2(); + }); + + } + + @Test + public void numberforamt77() throws Exception { + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData3.class) + .sheet("模板") + .doWrite(() -> { + List list = new ArrayList<>(); + DemoData3 demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 400000000)); + list.add(demoData3); + demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 499000000)); + list.add(demoData3); + demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 500000000)); + list.add(demoData3); + demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 501000000)); + list.add(demoData3); + demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 995000000)); + list.add(demoData3); + return list; + }); + + } + + @Test + public void numberforamt99() throws Exception { + LocalDateTime localDateTime=LocalDateTime.of(2023, 1, 1, 0, 0, 0, 995000000); + log.info("date:{}",localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))); + + + } + + @Test + public void numberforamt5() throws Exception { + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData.class) + .sheet("模板") + .doWrite(() -> { + // 分页查询数据 + return data3(); + }); + + } + + @Test + public void numberforamt6() throws Exception { + DecimalFormat decimalFormat = new DecimalFormat("#.#"); + BigDecimal bigDecimal = new BigDecimal(3101011021236149800L); + log.info("b:{}", bigDecimal); + log.info("b:{}", bigDecimal.setScale(-4, RoundingMode.HALF_UP)); + log.info("b:{}", decimalFormat.format(bigDecimal.setScale(-4, RoundingMode.HALF_UP))); + + } + + @Test + public void numberforamt7() throws Exception { + DecimalFormat decimalFormat = new DecimalFormat("#.#"); + BigDecimal bigDecimal = new BigDecimal(3.1010110212361498E+18).round(new MathContext(15, RoundingMode.HALF_UP)); + //bigDecimal. + + // bigDecimal + log.info("b:{}", bigDecimal); + log.info("b:{}", bigDecimal.setScale(-4, RoundingMode.HALF_UP)); + log.info("b:{}", decimalFormat.format(bigDecimal.setScale(-4, RoundingMode.HALF_UP))); + log.info("b:{}", decimalFormat.format(bigDecimal)); + + } + + private List data3() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + DemoData2 data = new DemoData2(); + data.setString("字符串" + i); + data.setDoubleData(0.56); + data.setBigDecimal(BigDecimal.valueOf(3101011021236149800L)); + list.add(data); + } + return list; + } + + private List data() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + i); + try { + data.setDate(format.parse("2032-01-18 09:00:01.995")); + } catch (ParseException e) { + throw new RuntimeException(e); + } + data.setDoubleData(0.56); + list.add(data); + } + return list; + } + + private List data2() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + i); + try { + data.setDate(format.parse("2032-01-18 09:00:00.")); + } catch (ParseException e) { + throw new RuntimeException(e); + } + data.setDoubleData(0.56); + list.add(data); + } + return list; + } } diff --git a/easyexcel-test/src/test/resources/compatibility/t05.xlsx b/easyexcel-test/src/test/resources/compatibility/t05.xlsx new file mode 100644 index 00000000..248ec7d1 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t05.xlsx differ diff --git a/easyexcel-test/src/test/resources/compatibility/t06.xlsx b/easyexcel-test/src/test/resources/compatibility/t06.xlsx new file mode 100644 index 00000000..b27be027 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t06.xlsx differ diff --git a/easyexcel-test/src/test/resources/compatibility/t07.xlsx b/easyexcel-test/src/test/resources/compatibility/t07.xlsx new file mode 100644 index 00000000..a7b0eac7 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t07.xlsx differ diff --git a/pom.xml b/pom.xml index de5d0681..70153a8e 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 3.1.5 + 3.2.0 UTF-8 1.8 true diff --git a/update.md b/update.md index 782e7a80..36c98000 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,9 @@ +# 3.2.0 + +* 修复部分xlsx读取日期可能相差1秒的bug [Issue #1956](https://github.com/alibaba/easyexcel/issues/1956) +* 修复部分数据精度和excel不匹配的bug [Issue #2805](https://github.com/alibaba/easyexcel/issues/2805) +* 不创建对象的读支持读取原始的数据类型 + # 3.1.5 * 提高xlsx读取兼容性:兼用ns2开头的标签