Apache POI Дата разбора одной секунды - PullRequest
1 голос
/ 02 июля 2019

Я анализирую электронную таблицу Excel с датой в ней.Результаты POI отключены на 1 секунду по сравнению с тем, что отображается в Excel.

Неформатированные данные в Excel: 43261.5027743056 Ячейка в Excel имеет формат: мм / дд / гггг чч: мм: ссполе в Excel отображается как: 10/10/2018 12:04:00

Анализатор POI (версии 4.0.1 и 4.1.0 оба) анализирует его как:

  • Значение: 43261.502774305598
  • Формат: мм / дд / гггг \ чч: мм: сс
  • Результат: 10.06.2008 12:03:59

Вот мой код:

private final DataFormatter formatter;

case NUMBER:
    String n = value.toString();
    if (this.formatString != null) {
      thisStr = formatter.formatRawCellContents(Double.parseDouble(n), this.formatIndex, this.formatString);
    } 
    else thisStr = n;
    break;

Я что-то не так делаю?

Ответы [ 2 ]

1 голос
/ 03 июля 2019

Проблема не в двоичной проблеме с плавающей запятой.Это также существует, но не должно влиять на секунды времени.

Проблема в том, что ваше значение 43261.5027743056 не совсем точное время даты 06/10/2018 12:04:00, но 06/10/2018 12:03:59.700.Так что это 06/10/2018 12:03:59 плюс 700 миллисекунд.Это можно увидеть, если вы отформатируете ячейку в формате DD/MM/YYYY hh:mm:ss.000 в Excel.

. Для таких значений существует расхождение между форматированием даты Excel и apache poi * * 1014.*, который использует формат даты Java.Когда Excel показывает значение даты и времени 06/10/2018 12:03:59,700 без миллисекунд, тогда оно округляется до секунд внутри.Так что 06/10/2018 12:03:59.700 показано как 06/10/2018 12:04:00.Форматеры даты Java не округляются, а просто не показывают миллисекунды.Так 06/10/2018 12:03:59.700 отображается как 06/10/2018 12:03:59.

Apache poi s DateUtil предоставляет методы, которые округляют секунды.Но эти методы, похоже, не используются в DataFormatter.

. В качестве обходного пути мы можем переопределить formatCellValue из DataFormatter, чтобы сделать это.

Полный пример:

Excel:

enter image description here

Код:

import java.io.FileInputStream;

import org.apache.poi.util.LocaleUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;

import java.util.Date;

class ExcelParseCellValues {

 public static void main(String[] args) throws Exception {

  Workbook workbook  = WorkbookFactory.create(new FileInputStream("Excel.xlsx"));

  DataFormatter dataFormatter = new DataFormatter() {
   @Override
   public String formatCellValue(Cell cell, FormulaEvaluator evaluator, ConditionalFormattingEvaluator cfEvaluator) {
    CellType cellType = cell.getCellType();
    if (cellType == CellType.FORMULA) {
     if (evaluator == null) {
      return cell.getCellFormula();
     }
     cellType = evaluator.evaluateFormulaCell(cell);
    }
    if (cellType == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell, cfEvaluator)) { //we have a date
     CellStyle style = cell.getCellStyle();
     String dataFormatString = style.getDataFormatString();
     if (!dataFormatString.matches(".*(s\\.0{1,3}).*")) { //the format string does not show milliseconds
      boolean use1904Windowing = false;
      if ( cell != null && cell.getSheet().getWorkbook() instanceof Date1904Support)
       use1904Windowing = ((Date1904Support)cell.getSheet().getWorkbook()).isDate1904();
      boolean roundSeconds = true; //we round seconds
      Date date = DateUtil.getJavaDate(cell.getNumericCellValue(), use1904Windowing, LocaleUtil.getUserTimeZone(), roundSeconds);
      double value = DateUtil.getExcelDate(date);
      return super.formatRawCellContents(value, style.getDataFormat(), dataFormatString, use1904Windowing);
     }
    }
    return super.formatCellValue(cell, evaluator, cfEvaluator);
   }
  };

  CreationHelper creationHelper = workbook.getCreationHelper();

  FormulaEvaluator formulaEvaluator = creationHelper.createFormulaEvaluator();

  Sheet sheet = workbook.getSheetAt(0);

  for (Row row : sheet) {
   for (Cell cell : row) {
    String cellValue = dataFormatter.formatCellValue(cell, formulaEvaluator);
    System.out.print(cellValue + "\t");
   }
   System.out.println();
  }

  workbook.close();

 }
}

Результат:

Description of value  Floatingpoint value  DD/MM/YYYY hh:mm:ss.000    DD/MM/YYYY hh:mm:ss   
Your example value    43261,5027743056     06/10/2018 12:03:59.700    06/10/2018 12:04:00   
Exact Datetime 12:04  43261,5027777778     06/10/2018 12:04:00.000    06/10/2018 12:04:00   
Exact minus 500 ms    43261,5027719907     06/10/2018 12:03:59.500    06/10/2018 12:04:00   
Exact plus 500 ms     43261,5027835648     06/10/2018 12:04:00.500    06/10/2018 12:04:01   
Exact minus 501 ms    43261,5027719792     06/10/2018 12:03:59.499    06/10/2018 12:03:59   
Exact plus 501 ms     43261,5027835764     06/10/2018 12:04:00.501    06/10/2018 12:04:01   
0 голосов
/ 02 июля 2019

Вы делаете это, когда вы анализируете значение ячейки как двойное. Не все десятичные значения могут быть представлены в точности как double. Ближайшее двойное значение к 43261.5027743056 равно 43261.502774305597995407879352569580078125, что округляет до значения, которое вы видите.

...