Excel 日期时间格式讲解

来源:互联网 发布:除螨仪有用吗 知乎 编辑:程序博客网 时间:2024/05/22 10:22

1.Excel中日期时间

1.1 存储格式

  • Excel中日期时间格式单元格类型为Numeric,表示Excel日期时间内部以double数值存储
  • Excel中日期时间以浮点值存储,以1900-01-01 00:00:00为起点
  • 浮点值整数部分代表日期,表示自1900-01-01起的天数
  • 浮点值小数部分代表时间
  • 浮点值1.0表示1900-01-01 00:00:00
  • Excel提供了将浮点值d转换为日期时间date的方法
    a. d在区间(-~, 0) 表示无效日期时间
    b. d在区间[0, 1) 转换为 日期时间区间[1899-12-31 00:00:00, 1900-01-01 00:00:00)
    c. d在区间[1, +~) 转换为 日期时间区间[1900-01-01 00:00:00, yyyy-MM-dd HH:mm:ss)

  • Excel提供了将日期时间date转换为浮点值d的方法
    a. date在区间(-~, 1900-01-01 00:00:00) 转换为 d = -1,表示无效日期时间
    b. date = 1900-01-01 00:00:00 转换为 d = 1.0
    c. date在区间(1900-01-01 00:00:00, +~) 转换为 d > 1.0
    这里写图片描述

1.2 日期时间转double方法

org.apache.poi.ss.usermodel.DateUtil

/** * 将一个给的的日期转换为一个double类型的数值,代表日期在Excel内部的表现形式 * double的整数部分表示自1900年1月1日以来的天数,小数表示时分秒 * @param date : date * @param use1904windowing : true-使用1904年的日期窗口,false-使用1900年的日期窗口 * @return 返回日期的Excel表示(如果错误 -检查小于0.1, 返回-1) * */public static double getExcelDate(Date date) // 默认使用1900窗口public static double getExcelDate(Date date, boolean use1904windowing)

1.3 日期时间转double实例

package org.apache.poi.util;import java.text.SimpleDateFormat;import java.util.Date;import org.apache.poi.ss.usermodel.DateUtil;public class Date2DoubleTest {    public static void main(String[] args) throws Exception {        System.out.println("小于1900-01-01 00:00:00的日期返回-1");        System.out.println("日期1800-01-01 00:00:00 转换为double值为: " + formatDouble("1800-01-01 00:00:00"));        System.out.println("日期1899-01-01 00:00:00 转换为double值为: " + formatDouble("1899-01-01 00:00:00"));        System.out.println("日期1899-12-31 23:24:25 转换为double值为: " + formatDouble("1899-12-31 23:24:25"));        System.out.println("日期1900-01-01 00:00:00 转换为double值为: " + formatDouble("1900-01-01 00:00:00"));        System.out.println("日期1900-01-01 11:11:11 转换为double值为: " + formatDouble("1900-01-01 11:11:11"));        System.out.println("日期1900-01-01 22:22:22 转换为double值为: " + formatDouble("1900-01-01 22:22:22"));        System.out.println("日期1900-01-01 23:22:22 转换为double值为: " + formatDouble("1900-01-01 23:22:22"));        System.out.println("日期1900-01-02 00:00:00 转换为double值为: " + formatDouble("1900-01-02 00:00:00"));        System.out.println("日期1900-01-03 00:00:00 转换为double值为: " + formatDouble("1900-01-03 00:00:00"));        System.out.println("日期2017-11-12 13:14:15 转换为double值为: " + formatDouble("2017-11-12 13:14:15"));    }    public static double formatDouble (String dateStr) throws Exception {        SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");          Date date = dateFormat2.parse(dateStr);         return formatDouble(date);    }    /**     * 将java.util.Date转换为double值     * */    public static double formatDouble (Date date) {        double excelDate = DateUtil.getExcelDate(date);        return excelDate;    }}

执行结果:

小于1900-01-01 00:00:00的日期返回-1日期1800-01-01 00:00:00 转换为double值为: -1.0日期1899-01-01 00:00:00 转换为double值为: -1.0日期1899-12-31 23:24:25 转换为double值为: -1.0日期1900-01-01 00:00:00 转换为double值为: 1.0日期1900-01-01 11:11:11 转换为double值为: 1.466099537037037日期1900-01-01 22:22:22 转换为double值为: 1.9321990740740742日期1900-01-01 23:22:22 转换为double值为: 1.9738657407407407日期1900-01-02 00:00:00 转换为double值为: 2.0日期1900-01-03 00:00:00 转换为double值为: 3.0日期2017-11-12 13:14:15 转换为double值为: 43051.5515625

这里写图片描述

1.4 double转日期时间方法

org.apache.poi.ss.usermodel.DateUtil

/** * 将一个double类型的数值根据Excel内部日期格式转换为一个java.util.Date *  * 注意: * Excel日期和时间存储没有任何时区(TimeZone)信息 * 如果默认的TimeZone使用了夏令时(Daylight Saving Time),则转换回Excel日期可能不会产生相同的值 *  * @param date Excel内部日期值,一个double类型的数值 * @param use1904windowing true-使用1904年的日期窗口,false-使用1900年的日期窗口 * @param tz 对应的时区,如果为null,使用系统默认时区 * @param roundSeconds 是否round最接近的秒,默认false * @return 日期的Java表示形式,如果日期不是有效的Excel日期,则为null * */public static Date getJavaDate(double date)public static Date getJavaDate(double date, boolean use1904windowing)public static Date getJavaDate(double date, TimeZone tz) // 使用时区tz去格式化日期public static Date getJavaDate(double date, boolean use1904windowing, TimeZone tz)public static Date getJavaDate(double date, boolean use1904windowing, TimeZone tz, boolean roundSeconds)

1.5 double转日期时间实例

package org.apache.poi.util;import java.text.SimpleDateFormat;import java.util.Date;import org.apache.poi.ss.usermodel.DateUtil;public class Double2DateTest {    public static void main(String[] args) {        /**         * double转换为java.util.Date         * */        System.out.println("double :    -1.0                转换为日期时间为:  " + formatDate(-1));        System.out.println("double :    0.00000000000001    转换为日期时间为:  " + formatDate(0.00000000000001));        System.out.println("double :    0.55555555555555    转换为日期时间为:  " + formatDate(0.55555555555555));        System.out.println("double :    0.77777777777777    转换为日期时间为:  " + formatDate(0.77777777777777));        System.out.println("double :    1.0                 转换为日期时间为:  " + formatDate(1.0));        System.out.println("double :    1.466099537037037   转换为日期时间为:  " + formatDate(1.466099537037037));        System.out.println("double :    1.9321990740740742  转换为日期时间为:  " + formatDate(1.9321990740740742));        System.out.println("double :    1.9738657407407407  转换为日期时间为:  " + formatDate(1.9738657407407407));        System.out.println("double :    2.0                 转换为日期时间为:  " + formatDate(2.0));        System.out.println("double :    3.0                 转换为日期时间为:  " + formatDate(3.0));        System.out.println("double :    43051.5515625       转换为日期时间为:  " + formatDate(43051.5515625));    }    /**     * 将double值转换为java.util.Date     * 1. 负数               -> 小于0,表示无效日期     * 2. 1                -> 1900-1-1 00:00:00     * 3. 43081.5091898148 -> 2017-12-12 12:13:14     * @param value      * */    public static String formatDate(double value) {        if (!DateUtil.isValidExcelDate(value)) {            // value<0表示是无效日期            return "小于0,表示无效日期";        }        // HH表示24进制,hh是12进制        return formatDate(value, "yyyy-MM-dd HH:mm:ss");    }    public static String formatDate(double value, String formatString) {        SimpleDateFormat sdf = new SimpleDateFormat(formatString);//      Date date = DateUtil.getJavaDate(value);         // 以1900-1-1 00:00::00为起点        Date date = DateUtil.getJavaDate(value, false);        // 以1900-1-1 00:00::00为起点//      Date date = DateUtil.getJavaDate(value, true);        return sdf.format(date);    }}

执行结果:
以1900-1-1 00:00:00为起点

double :    -1.0                转换为日期时间为:  小于0,表示无效日期double :    0.00000000000001    转换为日期时间为:  1899-12-31 00:00:00double :    0.55555555555555    转换为日期时间为:  1899-12-31 13:20:00double :    0.77777777777777    转换为日期时间为:  1899-12-31 18:40:00double :    1.0                 转换为日期时间为:  1900-01-01 00:00:00double :    1.466099537037037   转换为日期时间为:  1900-01-01 11:11:11double :    1.9321990740740742  转换为日期时间为:  1900-01-01 22:22:22double :    1.9738657407407407  转换为日期时间为:  1900-01-01 23:22:22double :    2.0                 转换为日期时间为:  1900-01-02 00:00:00double :    3.0                 转换为日期时间为:  1900-01-03 00:00:00double :    43051.5515625       转换为日期时间为:  2017-11-12 13:14:15

这里写图片描述

2.Excel日期单元格样式

  • Excel支持日期单元格
  • 日期值对应的是Excel内部日期double值的证书部分,表示自1900-1-1以来的天数

2.1 Excel日期样式

使用Excel或WPS设置单元格样式,会看到已经定义了日期样式

2.1.1 对话框-已定义日期样式

这里写图片描述

2.1.2 对话框-自定义日期样式

这里写图片描述

2.2 Excel中已定义日期样式

[$-804]表示国家或地区(语言)编号,一般可以不填。如804 汉语 (中国)809 英语(英国)409 英语(美国)如果电脑上没有装相应的语言(可以设),把804改掉都不会影响显示结果。[$-F800]相对于是 yyyy-m-d"[$-F400]相对于是 h:mm:ss[dbnum1]是将阿拉伯数字转换为汉字,如:123转换为一二三[dbbun2]是转换成大写汉字,如:123转换为壹贰叁[dbnum3]是转换为全角数字,如:123转换为 123d代表日期之中的日,m日期中的月,y日期中的年,h日期中的小时,m日期中的分,s是日期中的秒[$-F800]dddd, mmmm dd, yyyy[DBNum1][$-804]yyyy"年"m"月"d"日";@[DBNum1][$-804]yyyy"年"m"月";@[DBNum1][$-804]m"月"d"日";@yyyy"年"m"月"d"日";@yyyy"年"m"月";@m"月"d"日";@[$-804]aaaa;@[$-804]aaa;@yyyy/m/d;@[$-409]yyyy/m/d h:mm AM/PM;@yyyy/m/d h:mm;@yy/m/d;@m/d;@m/d/yy;@mm/dd/yy;@[$-409]d-mmm;@[$-409]d-mmm-yy;@[$-409]dd-mmm-yy;@[$-409]mmm-yy;@[$-409]mmmm-yy;@[$-409]mmmm-yy;@[$-409]mmmmm;@[$-409]mmmmm-yy;@

2.3 Excel 2007中日期样式存储格式

2.3.1 当前Sheet日期单元格

这里写图片描述

2.3.2 当前Sheet存储XML

这里写图片描述

2.3.3 当前Sheet单元格样式存储XML

这里写图片描述

2.4 验证日期样式字符串

org.apache.poi.ss.usermodel.DateUtil提供方法验证

package org.apache.poi.util;import org.apache.poi.ss.usermodel.DateUtil;/** * 验证Excel中已定义的日期格式 * org.apache.poi.ss.usermodel.DateUtil *  * [$-804]表示国家或地区(语言)编号,一般可以不填。 * 如804 汉语 (中国) *  809 英语(英国) *  409 英语(美国) * 如果电脑上没有装相应的语言(可以设),把804改掉都不会影响显示结果。 *  * [$-F800]相对于是 yyyy-m-d" * [$-F400]相对于是 h:mm:ss *  * [dbnum1]是将阿拉伯数字转换为汉字,如:123转换为一二三 * [dbbun2]是转换成大写汉字,如:123转换为壹贰叁 * [dbnum3]是转换为全角数字,如:123转换为 123 * d代表日期之中的日,m日期中的月,y日期中的年,h日期中的小时,m日期中的分,s是日期中的秒 * */public class DateFormatTest {    public static void main(String[] args) {        System.out.println("字符串 - [$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy  是否是日期格式: " + DateUtil.isADateFormat(176, "[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy"));        System.out.println("字符串 - [DBNum1][$-804]yyyy\"年\"m\"月\"d\"日  是否是日期格式: " + DateUtil.isADateFormat(177, "[DBNum1][$-804]yyyy\"年\"m\"月\"d\"日"));        System.out.println("字符串 - [DBNum1][$-804]yyyy\"年\"m\"月\";@  是否是日期格式: " + DateUtil.isADateFormat(178, "[DBNum1][$-804]yyyy\"年\"m\"月\";@"));        System.out.println("字符串 - [DBNum1][$-804]m\"月\"d\"日\";@  是否是日期格式: " + DateUtil.isADateFormat(179, "[DBNum1][$-804]m\"月\"d\"日\";@"));        System.out.println("字符串 - yyyy\"年\"m\"月\"d\"日\";@       是否是日期格式: " + DateUtil.isADateFormat(180, "yyyy\"年\"m\"月\"d\"日\";@"));        System.out.println("字符串 - yyyy\"年\"m\"月\";@             是否是日期格式: " + DateUtil.isADateFormat(181, "yyyy\"年\"m\"月\";@"));        System.out.println("字符串 - m\"月\"d\"日\";@            是否是日期格式: " + DateUtil.isADateFormat(182, "m\"月\"d\"日\";@"));        System.out.println("字符串 - [$-804]aaaa;@  是否是日期格式: " + DateUtil.isADateFormat(183, "[$-804]aaaa;@"));        System.out.println("字符串 - [$-804]aaa;@  是否是日期格式: " + DateUtil.isADateFormat(184, "[$-804]aaa;@"));        System.out.println("字符串 - yyyy/m/d;@            是否是日期格式: " + DateUtil.isADateFormat(185, "yyyy/m/d;@"));        System.out.println("字符串 - [$-409]yyyy/m/d\\ h:mm\\ AM/PM;@    是否是日期格式: " + DateUtil.isADateFormat(186, "[$-409]yyyy/m/d\\ h:mm\\ AM/PM;@"));        System.out.println("字符串 - yyyy/m/d\\ h:mm;@         是否是日期格式: " + DateUtil.isADateFormat(187, "yyyy/m/d\\ h:mm;@"));        System.out.println("字符串 - yy/m/d;@              是否是日期格式: " + DateUtil.isADateFormat(188, "yy/m/d;@"));        System.out.println("字符串 - m/d;@                 是否是日期格式: " + DateUtil.isADateFormat(189, "m/d;@"));        System.out.println("字符串 - m/d/yy;@              是否是日期格式: " + DateUtil.isADateFormat(190, "m/d/yy;@"));        System.out.println("字符串 - mm/dd/yy;@            是否是日期格式: " + DateUtil.isADateFormat(191, "mm/dd/yy;@"));        System.out.println("字符串 - [$-409]d\\-mmm;@  是否是日期格式: " + DateUtil.isADateFormat(192, "[$-409]d\\-mmm;@"));        System.out.println("字符串 - [$-409]d\\-mmm\\-yy;@  是否是日期格式: " + DateUtil.isADateFormat(193, "[$-409]d\\-mmm\\-yy;@"));        System.out.println("字符串 - [$-409]dd\\-mmm\\-yy;@  是否是日期格式: " + DateUtil.isADateFormat(194, "[$-409]dd\\-mmm\\-yy;@"));        System.out.println("字符串 - [$-409]mmm\\-yy;@  是否是日期格式: " + DateUtil.isADateFormat(195, "[$-409]mmm\\-yy;@"));        System.out.println("字符串 - [$-409]mmmm\\-yy;@  是否是日期格式: " + DateUtil.isADateFormat(196, "[$-409]mmmm\\-yy;@"));        System.out.println("字符串 - [$-409]mmmmm;@  是否是日期格式: " + DateUtil.isADateFormat(197, "[$-409]mmmmm;@"));        System.out.println("字符串 - [$-409]mmmmm\\-yy;@  是否是日期格式: " + DateUtil.isADateFormat(198, "[$-409]mmmmm\\-yy;@"));    }}

执行结果:

字符串 - [$-F800]dddd\,\ mmmm\ dd\,\ yyyy  是否是日期格式: true字符串 - [DBNum1][$-804]yyyy"年"m"月"d"日     是否是日期格式: true字符串 - [DBNum1][$-804]yyyy"年"m"月";@     是否是日期格式: true字符串 - [DBNum1][$-804]m"月"d"日";@    是否是日期格式: true字符串 - yyyy"年"m"月"d"日";@         是否是日期格式: true字符串 - yyyy"年"m"月";@             是否是日期格式: true字符串 - m"月"d"日";@            是否是日期格式: true字符串 - [$-804]aaaa;@            是否是日期格式: false字符串 - [$-804]aaa;@             是否是日期格式: false字符串 - yyyy/m/d;@            是否是日期格式: true字符串 - [$-409]yyyy/m/d\ h:mm\ AM/PM;@    是否是日期格式: true字符串 - yyyy/m/d\ h:mm;@          是否是日期格式: true字符串 - yy/m/d;@              是否是日期格式: true字符串 - m/d;@                 是否是日期格式: true字符串 - m/d/yy;@              是否是日期格式: true字符串 - mm/dd/yy;@            是否是日期格式: true字符串 - [$-409]d\-mmm;@              是否是日期格式: true字符串 - [$-409]d\-mmm\-yy;@          是否是日期格式: true字符串 - [$-409]dd\-mmm\-yy;@         是否是日期格式: true字符串 - [$-409]mmm\-yy;@         是否是日期格式: true字符串 - [$-409]mmmm\-yy;@        是否是日期格式: true字符串 - [$-409]mmmmm;@           是否是日期格式: true字符串 - [$-409]mmmmm\-yy;@       是否是日期格式: true

这里写图片描述

3. Excel 时间单元格样式

  • Excel单元格也支持时间单元格
  • 时间值对应的是Excel内部时间double值的小数部分

3.1 Excel时间样式

使用Excel或WPS设置单元格样式,会看到已经定义了时间样式,我们主要讲解的就是这些时间样式:

3.1.1 对话框-已定义时间样式

这里写图片描述

3.1.2 对话框-自定义时间样式

这里写图片描述

3.2 Excel中已定义时间样式

两种设置时间样式一共有19种已经定义的时间样式,内置的时间样式索引小于50,自定义样式索引从164开始:

 - 索引         时间样式字符串 - 18,       "h:mm AM/PM"   - 19,       "h:mm:ss AM/PM"   - 20,       "h:mm"   - 21,       "h:mm:ss"   - 22,       "m/d/yy h:mm"   - 45,       "mm:ss"   - 46,       "[h]:mm:ss"   - 47,       "mm:ss.0"  -  - 176    [DBNum1][$-804]上午/下午h"时"mm"分";@ - 177    [DBNum1][$-804]h"时"mm"分";@ - 178    [$-409]h:mm:ss\ AM/PM;@ - 179    [$-409]h:mm\ AM/PM;@ - 180    [$-F400]h:mm:ss\ AM/PM - 181    h:mm;@ - 182    h:mm:ss;@ - 183    h"时"mm"分";@ - 184    h"时"mm"分"ss"秒";@ - 185    上午/下午h"时"mm"分";@ - 186    上午/下午h"时"mm"分"ss"秒";@ 

3.3 Excel 2007中时间样式存储格式

3.3.1 当前Sheet时间单元格

这里写图片描述

3.3.2 当前Sheet存储XML

这里写图片描述

3.3.3 当前Sheet单元格样式存储XML

这里写图片描述

3.4 验证时间样式字符串

提供方法实现验证一个单元格是否为一个时间单元格:
- 时间单元格原始值为double
- 时间单元格样式为时间字符串
这里提供方法解析单元格样式字符串,验证其是否为时间样式字符串,工具类:

package org.apache.poi.util;import java.util.regex.Pattern;/** * 主要用于处理导入excel中日期时间格式,  * 参考org.apache.poi.ss.usermodel.DateUtil * */public class DateTimeUtil {    protected DateTimeUtil() {        // no instances of this class    }    // 描述无效的日期//  private static final int BAD_DATE = -1;    public static final int SECONDS_PER_MINUTE = 60;    public static final int MINUTES_PER_HOUR = 60;    public static final int HOURS_PER_DAY = 24;    // 一天等于86400秒    public static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);    // 一天等于86400 * 1000毫秒    public static final long DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L;//  private static final Pattern TIME_SEPARATOR_PATTERN = Pattern.compile(":");    /**     * 符号. : 匹配除换行符 \n 之外的任何单字符     * 符号* : 匹配前面的子表达式零次或多次     * 符号+ : 匹配前面的子表达式一次或多次     * 符号? : 匹配前面的子表达式零次或一次     *      * *、 +限定符都是贪婪的,因为它们会尽可能多的匹配文字     * 通过在 *、 + 或 ? 限定符之后放置 ?(*?、 +?、 ??),该表达式从"贪心"表达式转换为"非贪心"表达式或者最小匹配     * */    // ^[\$\-.*?] : 匹配以[$-开头,中间为除换行符 \n 之外的任何单字符,结尾为]的字符串,非贪心匹配    private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]");    /**     * 红色 : \u7ea2\u8272     * 黑色 :\u9ed1\u8272     * 黄色 :  \u9ec4\u8272     * 绿色 : \u7eff\u8272     * 白色 : \u9ed1\u8272     * 蓝色 : \u84dd\u8272     * 青色 : \u9752\u8272     * 洋红 : \u6d0b\u7ea2     * */    // ^[[a-zA-Z]+] : 匹配以[开头, ]结尾, 中间至少一个字母的字符串,如颜色    private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z\u7ea2\u9ed1\u9ec4\u7eff\u84dd\u9752\u6d0b\u7ea2\u8272]+\\]");    // [yYmMdDhHsS] : 匹配中括号里任一字母, 匹配一个    private static final Pattern date_ptrn3a = Pattern.compile("[mhHsS]");    /**     * 为中文/日文日期格式添加Unicode编码:如2017 \u5e74 2 \u6708 7 \u65e5     * Unicode编码  : 中文     *    \u5e74 : 年     *    \u6708 : 月     *    \u65e5 : 日     *    \u4e0a : 上     *    \u4e0b : 下     *    \u5348 : 午     *         * ^[\[\]mhHsS\-T/时分秒(上午/下午),. :"\\]+0*[ampAMP/]*$     * */    private static final Pattern date_ptrn3b = Pattern.compile("^[\\[\\]yYmMdDhHsS\\-T/\u65f6\u5206\u79d2(\u4e0a\u5348/\u4e0b\u5348),. :\"\\\\]+0*[ampAMP/]*$");    // ^\[([hH]+|[mM]+|[sS]+)] : 匹配以[开头, 以]结尾, 中间为时、分或秒的时间字符    private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)\\]");    // 匹配以[DBNum1]、[DBNum2]或[DBNum3]开头的中文日期    private static final Pattern date_ptrn5 = Pattern.compile("^\\[DBNum(1|2|3)\\]");    /**     * 性能优化的变量:     * 如果一个相同的日期格式字符串被多次传递,避免重复检查DataUtil.isADateFormat(int, String)     * https://issues.apache.org/bugzilla/show_bug.cgi?id=55611     * */    private static ThreadLocal<Integer> lastFormatIndex = new ThreadLocal<Integer>() {        protected Integer initialValue() {            return -1;        }    };    private static ThreadLocal<String> lastFormatString = new ThreadLocal<String>();    private static ThreadLocal<Boolean> lastCachedResult = new ThreadLocal<Boolean>();    /**     * 当前格式是否缓存过     * @param formatString 格式化字符串     * @param formatIndex 格式化索引     * */    private static boolean isCached(String formatString, int formatIndex) {        String cachedFormatString = lastFormatString.get();        return cachedFormatString != null &&                      formatIndex == lastFormatIndex.get() &&                      formatString.equals(cachedFormatString);    }    /**     * 缓存当前格式     * @param formatString 格式化字符串     * @param formatIndex 格式化索引     * @param cached 是否已经缓存过     * */    private static void cache(String formatString, int formatIndex, boolean cached) {        lastFormatIndex.set(formatIndex);        lastFormatString.set(formatString);        lastCachedResult.set(Boolean.valueOf(cached));    }    /**     * 检查给定格式ID及其格式String是否表示时间格式。     * 1. 调用此方法之前org.apache.poi.ss.usermodel.DateUtil.isADateFormat(formatIndex, formatString)     *      首先确保是一个日期格式     * 2. 再检查是否为时间格式     *     * @param formatIndex 格式索引     * @param formatString 格式字符串     */    public static boolean isADateTimeFormat(int formatIndex, String formatString) {        if(isInternalDateTimeFormat(formatIndex)) {            cache(formatString, formatIndex, true);            return true;        }        if(formatString == null || formatString.length() == 0) {            return false;        }        // 检查是否缓存过,提高性能        if (isCached(formatString, formatIndex)) {            return lastCachedResult.get();        }        String fs = formatString;        final int length = fs.length();        StringBuilder sb = new StringBuilder(length);        for (int i = 0; i < length; i++) {            char c = fs.charAt(i);            if (i < length - 1) {                char nc = fs.charAt(i + 1);                if (c == '\\') {// 处理转义字符                    switch (nc) {                        case '-': // \-表示                        case ',':                        case '.':                        case ' ':                        case '\\':                            // skip current '\' and continue to the next char                            continue;                    }                } else if (c == ';' && nc == '@') {// 跳过;@                    i++;                    continue;                }            }            sb.append(c);        }        fs = sb.toString();        // short-circuit if it indicates elapsed time: [h], [m] or [s]        if(date_ptrn4.matcher(fs).matches()){            cache(formatString, formatIndex, true);            return true;        }        // 处理以[DBNum1]、 [DBNum2]或[DBNum3]开头的时间格式:[DBNum1][$-804]h"时"mm"分";@        fs = date_ptrn5.matcher(fs).replaceAll("");        // 处理匹配[$-...]的时间格式:[$-F400]h:mm:ss\ AM/PM        fs = date_ptrn1.matcher(fs).replaceAll("");        // 匹配以[开头, ]结尾, 中间至少一个字母的字符串,如带颜色的时间格式:[Yellow]h:mm:ss AM/PM        fs = date_ptrn2.matcher(fs).replaceAll("");        // 日期格式为dd / mm / yy; [red] dd / mm / yy,只处理第一个        final int separatorIndex = fs.indexOf(';');        if(0 < separatorIndex && separatorIndex < fs.length()-1) {            fs = fs.substring(0, separatorIndex);        }        // 处理时间格式        if (! date_ptrn3a.matcher(fs).find()) {            return false;        }        /**         * 到了这里,检查它只是由以下组成:  m h s - \ /,。 :[] T         * 可选地跟随AM / PM         * */        boolean result = date_ptrn3b.matcher(fs).matches();        cache(formatString, formatIndex, result);        return result;    }    /**     * 给定的日期格式索引是否为Excel内置日期时间格式     * @param format 日期格式索引     */    public static boolean isInternalDateTimeFormat(int format) {        switch(format) {            // Excel内部时间格式            //          0x12,       "h:mm AM/PM"              //          0x13,       "h:mm:ss AM/PM"              //          0x14,       "h:mm"              //          0x15,       "h:mm:ss"              //          0x16,       "m/d/yy h:mm"              //          0x2d,       "mm:ss"              //          0x2e,       "[h]:mm:ss"              //          0x2f,       "mm:ss.0"              case 0x12:            case 0x13:            case 0x14:            case 0x15:            case 0x16:            case 0x2d:            case 0x2e:            case 0x2f:                return true;        }        return false;    }    /**     * 检查给定的double数值是否是有效的Excel日期     * value>=0,表示是有效日期     */    public static boolean isValidExcelDate(double value) {        return (value > -Double.MIN_VALUE);    }    /************************ 以下是org.apache.poi.ss.usermodel.DateUtil常用方法   ******************************/    /**     * 将一个给的的日期转换为一个double类型的数值,代表日期在Excel内部的表现形式     * double的整数部分表示自1900年1月1日以来的天数,小数表示时分秒     * @param date : date     * @param use1904windowing : true-使用1904年的日期窗口,false-使用1900年的日期窗口     * @return 返回日期的Excel表示(如果错误 -检查小于0.1, 返回-1)     * *///  public static double getExcelDate(Date date) // 默认使用1900窗口//  public static double getExcelDate(Date date, boolean use1904windowing)    /**     * 将一个double类型的数值根据Excel内部日期格式转换为一个java.util.Date     *      * 注意:     * Excel日期和时间存储没有任何时区(TimeZone)信息     * 如果默认的TimeZone使用了夏令时(Daylight Saving Time),则转换回Excel日期可能不会产生相同的值     *      * @param date Excel内部日期值,一个double类型的数值     * @param use1904windowing true-使用1904年的日期窗口,false-使用1900年的日期窗口     * @param tz 对应的时区,如果为null,使用系统默认时区     * @param roundSeconds 是否round最接近的秒,默认false     * @return 日期的Java表示形式,如果日期不是有效的Excel日期,则为null     * *///  public static Date getJavaDate(double date)//  public static Date getJavaDate(double date, boolean use1904windowing)//  public static Date getJavaDate(double date, TimeZone tz) // 使用时区tz去格式化日期//  public static Date getJavaDate(double date, boolean use1904windowing, TimeZone tz)//  public static Date getJavaDate(double date, boolean use1904windowing, TimeZone tz, boolean roundSeconds)    /**     * 将一个格式为HH:MM或HH:MM:SS的时间字符串转换为Excel日期格式的double数值,一个小数     * @param timeStr 如12:12、 12:12:12,timeStr长度小于4或大于8都会抛出异常     * @return 返回一个0~1之间的数     *///  public static double convertTime(String timeStr)//  private static double convertTimeInternal(String timeStr)}

测试类:

package org.apache.poi.util;/** *  * 索引         时间样式字符串 * 18,       "h:mm AM/PM"   * 19,       "h:mm:ss AM/PM"   * 20,       "h:mm"   * 21,       "h:mm:ss"   * 22,       "m/d/yy h:mm"   * 45,       "mm:ss"   * 46,       "[h]:mm:ss"   * 47,       "mm:ss.0"  *  * 176    [DBNum1][$-804]上午/下午h"时"mm"分";@ * 177    [DBNum1][$-804]h"时"mm"分";@ * 178    [$-409]h:mm:ss\ AM/PM;@ * 179    [$-409]h:mm\ AM/PM;@ * 180    [$-F400]h:mm:ss\ AM/PM * 181    h:mm;@ * 182    h:mm:ss;@ * 183    h"时"mm"分";@ * 184    h"时"mm"分"ss"秒";@ * 185    上午/下午h"时"mm"分";@ * 186    上午/下午h"时"mm"分"ss"秒";@  * */public class TimeFormatTest {    public static void main(String[] args) {        System.out.println("Excel已定义时间格式字符串: ");        System.out.println("字符串 - h:mm AM/PM                             是否是时间格式: " + DateTimeUtil.isADateTimeFormat(18, "h:mm AM/PM"));        System.out.println("字符串 - h:mm:ss AM/PM                          是否是时间格式: " + DateTimeUtil.isADateTimeFormat(19, "h:mm:ss AM/PM"));        System.out.println("字符串 - h:mm                                   是否是时间格式: " + DateTimeUtil.isADateTimeFormat(20, "h:mm"));        System.out.println("字符串 - h:mm:ss                                是否是时间格式: " + DateTimeUtil.isADateTimeFormat(21, "h:mm:ss"));        System.out.println("字符串 - m/d/yy h:mm                            是否是时间格式: " + DateTimeUtil.isADateTimeFormat(22, "m/d/yy h:mm"));        System.out.println("字符串 - mm:ss                                  是否是时间格式: " + DateTimeUtil.isADateTimeFormat(45, "mm:ss"));        System.out.println("字符串 - [h]:mm:ss                              是否是时间格式: " + DateTimeUtil.isADateTimeFormat(46, "[h]:mm:ss"));        System.out.println("字符串 - mm:ss.0                                                                                           是否是时间格式: " + DateTimeUtil.isADateTimeFormat(47, "mm:ss.0"));        System.out.println("字符串 - [DBNum1][$-804]上午/下午h\"时\"mm\"分\";@  是否是时间格式: " + DateTimeUtil.isADateTimeFormat(176, "[DBNum1][$-804]上午/下午h\"时\"mm\"分\";@"));        System.out.println("字符串 - [DBNum1][$-804]h\"时\"mm\"分\";@          是否是时间格式: " + DateTimeUtil.isADateTimeFormat(177, "[DBNum1][$-804]h\"时\"mm\"分\";@"));        System.out.println("字符串 - [$-409]h:mm:ss\\ AM/PM;@               是否是时间格式: " + DateTimeUtil.isADateTimeFormat(178, "[$-409]h:mm:ss\\ AM/PM;@"));        System.out.println("字符串 - [$-409]h:mm\\ AM/PM;@                  是否是时间格式: " + DateTimeUtil.isADateTimeFormat(179, "[$-409]h:mm\\ AM/PM;@"));        System.out.println("字符串 - [$-F400]h:mm:ss\\ AM/PM                是否是时间格式: " + DateTimeUtil.isADateTimeFormat(180, "[$-F400]h:mm:ss\\ AM/PM"));        System.out.println("字符串 - h:mm;@                                 是否是时间格式: " + DateTimeUtil.isADateTimeFormat(181, "h:mm;@"));        System.out.println("字符串 - h:mm:ss;@                              是否是时间格式: " + DateTimeUtil.isADateTimeFormat(182, "h:mm:ss;@"));        System.out.println("字符串 - h\"时\"mm\"分\";@                        是否是时间格式: " + DateTimeUtil.isADateTimeFormat(183, "h\"时\"mm\"分\";@"));        System.out.println("字符串 - h\"时\"mm\"分\"ss\"秒\";@                是否是时间格式: " + DateTimeUtil.isADateTimeFormat(184, "h\"时\"mm\"分\"ss\"秒\";@"));        System.out.println("字符串 - 上午/下午h\"时\"mm\"分\";@                 是否是时间格式: " + DateTimeUtil.isADateTimeFormat(185, "上午/下午h\"时\"mm\"分\";@"));        System.out.println("字符串 - 上午/下午h\"时\"mm\"分\"ss\"秒\";@          是否是时间格式: " + DateTimeUtil.isADateTimeFormat(186, "上午/下午h\"时\"mm\"分\"ss\"秒\";@"));        System.out.println();        System.out.println("Excel自定义时间格式字符串:");        System.out.println("字符串 - [红色]上午/下午h\"时\"mm\"分\"ss\"秒\";@          是否是时间格式: " + DateTimeUtil.isADateTimeFormat(187, "[红色]上午/下午h\"时\"mm\"分\"ss\"秒\";@"));        System.out.println("字符串 - [绿色]yyyy/mm/dd hh:mm          是否是时间格式: " + DateTimeUtil.isADateTimeFormat(188, "[绿色]yyyy/mm/dd hh:mm"));    }}

执行结果:

Excel已定义时间格式字符串: 字符串 - h:mm AM/PM                            是否是时间格式: true字符串 - h:mm:ss AM/PM                         是否是时间格式: true字符串 - h:mm                                  是否是时间格式: true字符串 - h:mm:ss                               是否是时间格式: true字符串 - m/d/yy h:mm                           是否是时间格式: true字符串 - mm:ss                                 是否是时间格式: true字符串 - [h]:mm:ss                             是否是时间格式: true字符串 - mm:ss.0                                                                                           是否是时间格式: true字符串 - [DBNum1][$-804]上午/下午h"时"mm"分";@  是否是时间格式: true字符串 - [DBNum1][$-804]h"时"mm"分";@          是否是时间格式: true字符串 - [$-409]h:mm:ss\ AM/PM;@              是否是时间格式: true字符串 - [$-409]h:mm\ AM/PM;@                 是否是时间格式: true字符串 - [$-F400]h:mm:ss\ AM/PM               是否是时间格式: true字符串 - h:mm;@                               是否是时间格式: true字符串 - h:mm:ss;@                            是否是时间格式: true字符串 - h"时"mm"分";@                         是否是时间格式: true字符串 - h"时"mm"分"ss"秒";@                   是否是时间格式: true字符串 - 上午/下午h"时"mm"分";@                 是否是时间格式: true字符串 - 上午/下午h"时"mm"分"ss"秒";@           是否是时间格式: trueExcel自定义时间格式字符串:字符串 - [红色]上午/下午h"时"mm"分"ss"秒";@      是否是时间格式: true字符串 - [绿色]yyyy/mm/dd hh:mm                是否是时间格式: true

这里写图片描述

4.注意

4.1 只有时间

  • 在Excel一个Sheet单元格中输入:11:12:13
  • 设置单元格样式为时间:h:mm:ss;@
  • 11:12:13单元格为Numeric类型,以double值存储,不是一个字符串单元格
    因为只有时间,而没有日期,所以double只有小数,整部部分为0,存储的值是一个小于1的数。
    当前Sheet的时间单元格:
    这里写图片描述
    存储:
    这里写图片描述
    这里写图片描述

4.2 日期小于1900-1-1

  • 一个单元格为:1899/12/31,一个为:1900/1/1
  • 设置单元格样式为日期:yyyy”年”m”月”d”日”;@
  • 1899/12/31会以字符串存储,日期样式不起效
  • 1900/1/1会以double值存储,日期样式起效,显示为1900年1月1日
    这里写图片描述
  • sheet1.xml存储Sheet单元格信息
    这里写图片描述
  • SharedStrings.xml存储Excel中所有Sheet字符型单元格的文本值
    这里写图片描述
  • styles.xml存储Excel中所有Sheet的所有单元格样式信息
    这里写图片描述