Excel的日期格式约定与解析

Excel的日期格式约定与解析

原理

当Excel单元格内容为=NOW()时,会显示当前的日期/时间,而倘若你将设置为单元格格式改为非日期时间的格式时,会显示一个非负实数。

Excel 支持两个日期系统:1900年日期系统(推荐)和 1904年日期系统。每个日期系统使用日期作为计算的所有其他工作簿中的唯一开始日期。所有版本的 Excel for Windows 都计算基于 1900年日期系统中的日期。Excel 2008 for Mac 和早期 Excel for Mac 版本计算基于 1904年日期系统的日期。Excel 2016 for Mac 和 Excel for Mac 2011 使用 1900年日期系统,保证日期与 Excel for Windows 的兼容性。

就目前而言,正常情况下这个非负实数的整数位表示该日期距离1900年1月1日的天数,小数位表示该日期的当天时间占据整天时间的比例,因此可以很方便的用代码将其解析成我们的日常使用日期系统。

JAVA实现

// excelDateAnalysis.java

package ms.excelDateAnalysis;

import java.util.Calendar;
import java.util.GregorianCalendar;

public class excelDateAnalysis {
    private static final int SECONDS_PER_MINUTE = 60;
    private static final int MINUTES_PER_HOUR = 60;
    private static final int HOURS_PER_DAY = 24;
    private static final int SECONDS_PER_DAY = (HOURS_PER_DAY    *
    		                                    MINUTES_PER_HOUR *
    		                                    SECONDS_PER_MINUTE);
    
    private boolean onlyTime = false;
    
    private double excelDate;
    private int sumDay, sumSecond;
    private int year, month, day, week;
    private int hour, minute, second;
    
    public boolean onlyTime() { return this.onlyTime;  }
    public int getSumDay   () { return this.sumDay;    }
    public int getSumSecond() { return this.sumSecond; }
    public int getYear     () { return this.year;      }
    public int getMonth    () { return this.month;     }
    public int getDay      () { return this.day;       }
    public int getWeek     () { return this.week;      }
    public int getHour     () { return this.hour;      }
    public int getMinute   () { return this.minute;    }
    public int getSecond   () { return this.second;    }
    
    public void test() {
        excelDateAnalysis eDate = new excelDateAnalysis(this.excelDate);
        System.out.println("sum second in one day: " + eDate.getSumSecond());
        System.out.println("sum days: "+eDate.getSumDay());
        if (!eDate.onlyTime) {
            System.out.println("Date: " + eDate.getYear()  +
                                    "/" + eDate.getMonth() +
                                    "/" + eDate.getDay());
        }
        System.out.println("Time: " + eDate.getHour()   +
        		                ":" + eDate.getMinute() +
        		                ":" + eDate.getSecond());                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
    }
    public excelDateAnalysis(double excelDate) {
        this.excelDate = excelDate;
        this.sumDay    = (int)Math.floor(excelDate);
        this.sumSecond = (int)Math.floor((excelDate-(double)this.sumDay)*SECONDS_PER_DAY+0.5);
        sumSecondAnalysis(this.sumSecond);
        sumDayAnalysis(this.sumDay);
    }
    
    private void sumDayAnalysis(int wholeDays) {
        if (wholeDays==0) {
            this.onlyTime = true;
            return;
        }
        Calendar calendar = new GregorianCalendar();
        boolean use1904windowing = false;
        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,0, wholeDays + dayAdjust, 0, 0, 0);
        calendar.set(GregorianCalendar.MILLISECOND, this.sumSecond*1000);
        this.year  = calendar.get(Calendar.YEAR);
        this.month = calendar.get(Calendar.MONTH)+1;
        this.day   = calendar.get(Calendar.DAY_OF_MONTH);
        this.week  = calendar.get(Calendar.DAY_OF_WEEK)-1;
    }
    private void sumSecondAnalysis(int sumSecond) {
        this.hour   = sumSecond / SECONDS_PER_MINUTE / MINUTES_PER_HOUR;
        this.minute = sumSecond / SECONDS_PER_MINUTE - this.hour * MINUTES_PER_HOUR;
        this.second = sumSecond % SECONDS_PER_MINUTE;
    }
}

使用起来也很简单:

// Main.java

import ms.excelDateAnalysis.*;

public class Main {
	public static void main(String[] args) {
		excelDateAnalysis eDate = new excelDateAnalysis(43529.50258);
		eDate.test(); // 测试输出
	}
}

二级办公AOA中的应用

E练习3-03(员工信息表).xlsx 操作要求(2)在Sheet4中,使用函数,将B1中的时间四舍五入到最接近的15分钟的倍数,结果存放在C1单元格中。
Sheet4.B1 <- {自定义}[13:49:38]

利用Excel内建日期函数和四舍五入函数是能解决这道题目的(=TIME(HOUR(B1),15*ROUND(MINUTE(B1)/15,0),SECOND(0))或者=TIME(HOUR(B1),MROUND(MINUTE(B1),"15"),SECOND(0))),但问题是太过繁琐,不够直接,我们用本文提到的原理来解决。

60*24 = 1440

步骤 公式 解释
1 B1*1440 将总分钟数假定为总天数
2 Round(B1*1440/15,0)*15 让总天数变成15分钟的倍数
3 Round(B1*1440/15,0)*15/1440 将变成更改后的总天数变回默认时间约定

Sheet4.C1 <- {自定义}[=Round(B1*1440/15,0)*15/1440]

13:49:38 === 0.576134259

娱乐时间

    excelDateAnalysis eDatePI = new excelDateAnalysis(Math.PI);
    eDatePI.test();
    excelDateAnalysis eDateE = new excelDateAnalysis(Math.E);
    eDateE.test();
sum second in one day: 12234
sum days: 3
Date: 1900/1/3
Time: 3:23:54
sum second in one day: 62060
sum days: 2
Date: 1900/1/2
Time: 17:14:20
posted @ 2019-03-05 12:32  林博士  阅读(1182)  评论(0编辑  收藏  举报