【问题标题】:Converting number representing a date in Excel to a Java Date object将表示 Excel 中日期的数字转换为 Java Date 对象
【发布时间】:2013-09-26 12:24:44
【问题描述】:

我在 Excel 中有一个日期列,但是当我在我的 Java 应用程序中读取它时,我得到的值是数字。

例子

Excel 日期

1/1/2013

我是这样理解的

41275.00

如何在我的 Java 应用程序中将数字转换为日期?

【问题讨论】:

  • 你读得怎么样?
  • @SotiriosDelimanolis 使用 apache.poi。

标签: java excel


【解决方案1】:

这是一个如何将 Excel 日期转换为 Java 日期的最小工作示例:

        Date javaDate= DateUtil.getJavaDate((double) 41275.00);
        System.out.println(new SimpleDateFormat("MM/dd/yyyy").format(javaDate));

返回

01/01/2013

您还需要导入以下包:

java.text.SimpleDateFormat
java.util.Date

【讨论】:

【解决方案2】:

Apache POI 为 http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/DateUtil.html 提供了一些实用程序,尤其是 http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/DateUtil.html#getJavaDate(double)

注意 Excel 将日期存储为自 1900 年以来的天数(加上小数天数)(在某些情况下,它可以从 1904 年开始)。见http://support.microsoft.com/kb/180162

【讨论】:

    【解决方案3】:

    tl;博士

    使用现代 java.time 类。

    LocalDate                                    // Represent a date-only vaule, without time-of-day and without time zone.
    .of( 1899 , Month.DECEMBER , 30 )            // Specify epoch reference date used by *some* versions of Excel. Beware: Some versions use a 1904 epoch reference. Returns a `LocalDate` object.
    .plusDays(                                   // Add a number of days. 
        (long) Double.parseDouble( "41275.00" )  // Get a number of whole days from your input string.
    )                                            // Returns another instance of a `LocalDate`, per Immutable Objects pattern, rather than altering the original.        
    .toString()                                  // Generate text representing the value of this `LocalDate` in standard ISO 8601 format.
    

    2013-01-01

    java.time

    现代解决方案使用 java.time 类取代了与 Java 的最早版本捆绑在一起的可怕的遗留日期时间类。

    纪元参考日期:1899-12-30

    根据this documentation,来自Microsoft Excel 的值是自UTC 中的1900-01-01 的epoch reference 以来的天数。在内部,实际参考日期是 1899 年 12 月 30 日,如 this Wikipedia page 所记录。

    注意,Excel use a different epoch in 1904 的某些版本(macOS 的旧版本?)。

    在代码中的某处建立纪元引用。

    final static public LocalDate EXCEL_EPOCH_REFERENCE = 
        LocalDate.of( 1899 , Month.DECEMBER , 30 )
    ; // Beware: Some versions of Excel use a 1904 epoch reference.
    

    算算

    将您的输入字符串解析为 BigDecimal 以获得准确性(相对于牺牲准确性以加快执行速度的浮点类型)。

    BigDecimal countFromEpoch = new BigDecimal( "41275.00" );
    

    将整数天数添加到纪元参考日期。

    long days = countFromEpoch.longValue();  // Extract the number of whole days, dropping the fraction.
    LocalDate localDate = EXCEL_EPOCH_REFERENCE.plusDays( days );
    

    localDate.toString(): 2013-01-01


    关于java.time

    java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

    要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

    Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

    您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time

    从哪里获取 java.time 类?

    【讨论】:

      【解决方案4】:

      正如许多用户已经指出的那样,Excel 将日期和时间存储为一个数字,表示自 January 0, 1900 以来的天数,加上一天 24 小时的小数部分:ddddd.tttttt。 这称为序列日期序列日期时间

      但这里的答案仅代表故事的一部分 - 除了指出现有库中的现成可用方法。

      来自 Microsoft 的链接文档并未使其更加清晰。

      MS DATEVALUE function 状态:

      Excel 将日期存储为连续的序列号,以便在计算中使用它们。 默认情况下,1900 年 1 月 1 日是序列号 1,而 2008 年 1 月 1 日是序列号 39448,因为它是 1900 年 1 月 1 日之后的 39,447 天

      好吧,我要检查一下这个声明:

      LocalDate testDate = LocalDate.of(1900, Month.JANUARY, 1).plusDays(39447);
      System.out.println(testDate);// prints 2008-01-02
      

      1900 年 1 月 1 日之后的 39,447 天确实是......2008 年 1 月 2 日

      这是为什么呢?

      Excel 中的日期表示为从一个纪元(1899 年 12 月 30 日或 1900 年 1 月 1 日或 1904 年...)开始的天数,这只是故事的一部分.

      我在这里找到了终极答案:Dates And Times In Excel(某位上帝或任何可能保佑这些人的人)。

      在 Excel 中实现日期例程的开发人员故意引入了一个错误,以便与来自 Lotus 1-2-3 的同一已知问题兼容。

      他们将 1900 年视为闰年,但事实并非如此, 因此,任何超过 1900 年 1 月 28 日的日期都比实际日期多一天。

      这就是 Excel 认为 2008 年 1 月 1 日 由数字 39448 表示的原因:因为它在 1900 年 1 月 0 日之后是 39,448 个单位 (是的,Excel 认为 ) - 即 39,447 天 加上 1900 年 1 月 29 日

      Excel 还可以将序列日期的日期部分视为自 1904 年 1 月 0 日以来的天数;此模式称为 1904-mode1904-system,用于与 Macintosh 系统兼容。

      由于 Excel 日期不包含任何时区信息 - 它只是一个数字 - 最好使用 Java 类,如 LocalDate/LocalDateTime 来表示没有时区信息的此类值。

      嗯,在实践中 - 对于现在的日期 - 人们可能会认为 Excel 纪元从 1900 年 12 月 30 日开始 但事实并非如此。


      Excel 演示 - 日期格式为 dd/mm/yyyy


      日期作为左边的数字插入


      适合所需转换的类:

      public class SerialDate {
      
          //days from 1899-12-31 to Instant.EPOCH (1970-01-01T00:00:00Z)
          public static final long EPOCH = -25568L;
          private long serialDays;
          private double serialTime;
          private long epochDays;
          private long daySeconds;
      
          /**
           * @param date number of Excel-days since <i>January 0, 1899</i>
           */
          public SerialDate(long date) {
              serialDays = date;
              if (date > 59)//Lotus123 bug
                  --date;
              epochDays = EPOCH + date;
          }
                  
          /**
           * @param date number of days since <i>January 0, 1899</i> with a time fraction
           */
          public SerialDate(double date) {
              this((long)date);
              serialTime = date - serialDays;
              daySeconds = Math.round(serialTime * 24 * 60 * 60);
          }
                  
          /**
           * @return days since 1970-01-01
           */
          public long toEpochDays() {
              return epochDays;
          }
                  
          /**
           * @return seconds of the day for this SerialDate
           */
          public long toDaySeconds() {
              return daySeconds;
          }
                  
          /**
           * @return a value suitable for an Excel date
           */
          public double getSerialDate() {
              return serialTime + serialDays;
          }
          
      }
      

      使用示例:

      LocalDate dt = LocalDate.ofEpochDay(new SerialDate(41275).toEpochDays());
      System.out.println(dt);//prints 2013-01-01
      

      SerialDate sd = new SerialDate(33257.415972222225);
      LocalDateTime dt = LocalDateTime.of(
              LocalDate.ofEpochDay(sd.toEpochDays()),
              LocalTime.ofSecondOfDay(sd.toDaySeconds()));
      System.out.println(dt);//prints 1991-01-19T09:59
      

      【讨论】:

        【解决方案5】:

        Excel 的序列化日期是自 1900 年 1 月 1 日以来的天数。为了再次算出日期,我们必须添加对应天数的序列号。

        对于没有任何依赖关系的 Java 8

        ```

          /*
        
            1900-1-0            0
            1900-1-1            1
            1900-1-2            2
            1900-1-3            3
        
        
             */
        
        
            int days = 43323;
            LocalDate start = LocalDate.of(1900, 1, 1);
            LocalDate today = LocalDate.of(2018, 8, 11);
        
        
            // days to date
            LocalDate date = start.plusDays(days).minusDays(2);
        
            System.out.println(date);
        
            // date to days
            long days1 = ChronoUnit.DAYS.between(start, today) + 2;
            System.out.println(days1);
        

        ```

        【讨论】:

          猜你喜欢
          • 2014-09-26
          • 2014-05-23
          • 1970-01-01
          • 2013-01-02
          • 1970-01-01
          • 2018-04-16
          • 2011-10-25
          • 1970-01-01
          相关资源
          最近更新 更多