发新话题
移动帖子 加入精华 加入置顶 加入收藏 关注此帖

正则表达式验证日期



正则表达式验证日期

String str = "2008-02-29";
    System.out.println(str.matches("((^((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/\\._])(10|12|0?[13578])([-\\/\\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/\\._])(11|0?[469])([-\\/\\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/\\._])(0?2)([-\\/\\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\\/\\._])(0?2)([-\\/\\._])(29)$)|(^([3579][26]00)([-\\/\\._])(0?2)([-\\/\\._])(29)$)|(^([1][89][0][48])([-\\/\\._])(0?2)([-\\/\\._])(29)$)|(^([2-9][0-9][0][48])([-\\/\\._])(0?2)([-\\/\\._])(29)$)|(^([1][89][2468][048])([-\\/\\._])(0?2)([-\\/\\._])(29)$)|(^([2-9][0-9][2468][048])([-\\/\\._])(0?2)([-\\/\\._])(29)$)|(^([1][89][13579][26])([-\\/\\._])(0?2)([-\\/\\._])(29)$)|(^([2-9][0-9][13579][26])([-\\/\\._])(0?2)([-\\/\\._])(29)$))"));
这个日期正则表达式支持
YYYY-MM-DD
YYYY/MM/DD
YYYY_MM_DD
YYYY.MM.DD的形式


一、简单的日期判断(YYYY/MM/DD):
^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$
二、演化的日期判断(YYYY/MM/DD| YY/MM/DD):

^(^(\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2}$)|(^\d{4}\d{1,2}\d{1,2}$)$
三、加入闰年的判断的:
实例:

^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$
分析:
1、什么是合法的日期范围?对于不同的应用场景,这个问题有不同的解释。这里采纳MSDN中的约定:

DateTime值类型表示在公元(基督元)0001 1 1 日午夜 12:00:00 到公元 (C.E.) 9999 12 31 11:59:59 的日期和时间

2、关于闰年的阐释。

于公历闰年是这样规定的:地球太阳公一周叫做一回年,一回365548 46秒。因此,公历规定有平年和年,平年一年有365日,比回年短0.2422日,四年共短0.9688日,故四年增加一日,一年有366日,就年。但四年增加一日比四个回年又多0.0312,400年后将多3.12,故在400年中少3,也就是在400年中只97年,年的平均度与回年就相近似了。由此定:年份是整百数的必400的倍数才是年,例如1900年、2100年就不是

首先需要验证年份,然,年份范围为 0001 - 9999,匹配YYYY的正表达式
[0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}
其中 [0-9] 也可以表示为 \d,但 \d 不如 [0-9] 直观,因此下面我将一直采用 [0-9]

用正则表达式验证日期的难点有二:一是大小月份的天数不同,二是闰年的考虑。
对于第一个难点,我们首先不考虑闰年,假设2月份都是28天,这样,月份和日期可以分成三种情况:

1、月份为 1, 3, 5, 7, 8, 10, 12,天数范围为 01 - 31,匹配MM-DD的正则表达式为:
(0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])
2、月份为 4, 6, 9, 11,天数范围为 01-30,匹配MM-DD的正则表达式为:
(0[469]|11)-(0[1-9]|[12][0-9]|30)
3、月份为 2,考虑平年情况,匹配MM-DD的正则表达式为:
02-(0[1-9]|[1][0-9]|2[0-8])
根据上面的成果,我们可以得到匹配平年日期格式为YYYY-MM-DD的正则表达式:

([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8])))

接着我们来解决第二个难点:闰年的考虑。根据闰年的定义,我们可以将闰年分为两类:

1、能被4整除但不能被100整除的年份。寻找后两位的变化规律,可以很快得到下面的正则匹配:
([0-9]{2})(0[48]|[2468][048]|[13579][26])
2、能被400整除的年份。能被400整除的数肯定能被100整除,因此后两位肯定是00,我们只要保证前两位能被4整除即可,相应的正则表达式为:
(0[48]|[2468][048]|[3579][26])00
2.强验证日期的正表达式,添加了年的验证
个日期正表达式支持
YYYY-MM-DD
YYYY/MM/DD
YYYY_MM_DD
YYYY.MM.DD
的形式

match :
2008-2-29 2008/02/29
not match : 2008-2-30 2007-2-29

完整的正表达式如下:
((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))
年的2月份有29天,因此匹配年日期格式YYYY-MM-DD的正表达式

(([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29[i]最后,将平年和年的日期验证表达式合并,我得到最验证日期格式YYYY-MM-DD的正表达式

[i](([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)


DD/MM/YYYY
[/i]格式的正则验证表达式

[i](((0[1-9]|[12][0-9]|3[01])/((0[13578]|1[02]))|((0[1-9]|[12][0-9]|30)/(0[469]|11))|(0[1-9]|[1][0-9]|2[0-8])/(02))/([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}))|(29/02/(([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00)))
[/i][/i]
快乐渡过每一天,减肥坚持每一天
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

闰年的2月份有29天,因此匹配闰年日期格式为YYYY-MM-DD的正则表达式为:
(([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29
最后,将平年和闰年的日期验证表达式合并,我们得到最终的验证日期格式为YYYY-MM-DD的正则表达式为:
(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)

为什么输入2008-02-29 不对呢??????
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

这个是正确的~~~~ 上面哪个YYYY-MM-DD不正确~!!!
DD/MM/YYYY 格式的正则验证表达式为:
(((0[1-9]|[12][0-9]|3[01])/((0[13578]|1[02]))|((0[1-9]|[12][0-9]|30)/(0[469]|11))|(0[1-9]|[1][0-9]|2[0-8])/(02))/([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}))|(29/02/(([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00)))
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

编辑 回复 快速回复 TOP

Re:正则表达式验证日期

sorry.. 我看错了......... 是正确的..........
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

呵呵!这里的代码我基本都验证过的!
快乐渡过每一天,减肥坚持每一天
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

我失误了~~~ 哈哈哈~~
东西挺不错~~ 支持一个~~
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

(?:[0-9]{1,4}(?<!^0?0?0?0))-(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8]|(?:(?<=(?:0[13578]|1[02])-)(?:29|3[01]))|(?:(?<=(?:0[469]|11)-)(?:29|30))|(?:(?<=(?:(?:[0-9]{0,2}(?!0?0)(?:[02468]?(?<![13579])[048]|[13579][26]))|(?:(?:[02468]?[048]|[13579][26])00))-02-)(?:29)))

我弄了大半天,也弄了一个,比较短一些,266个字符,匹配 yyyy-MM-dd, yyy-MM-dd, yy-MM-dd, y-MM-dd

yyyy 从 1~9999,因为没有公元0年。
/+〆=う
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

怎么变成脸了?应该是“: (”,当中没有空格。
/+〆=う
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

需要禁用表情,我替你修改了!
快乐渡过每一天,减肥坚持每一天
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

都是高手~~
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

正则表达式好喜欢 就是没好点资料看看
java技术空间:hi.baidu.com/xhz12345 
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

极其不喜欢,不过貌似好多人都会,只好学习下
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

很好的总结啊!!
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

谢谢老紫竹帮我改正了,我8楼的那个是工作模下的,下面这个是带有注释的,可以看得更清楚一些。
把注释、换行和空格去掉就是 8 楼的那个样子了。

从我的 blog 里面搬来的,反正放在那里也没人看。
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DateRegex {

    public static void main(String[] args) {
        String[] str = {                
                "2001-00-00", "2001-01-01", "2001-01-30", "2001-01-31",
                "2001-01-32", "2001-02-01", "2001-02-27", "2001-02-28",
                "2004-02-29", "2001-02-29", "2001-02-30", "2001-02-31",
                "2001-02-32", "2001-03-01", "2001-03-10", "2001-03-29",
                "2001-03-30", "2001-03-31", "2001-03-32", "2001-04-01",
                "2001-04-10", "2001-04-29", "2001-04-30", "2001-04-31",
                "2001-04-32", "2001-05-01", "2001-05-10", "2001-05-29",
                "2001-05-30", "2001-05-31", "2001-05-32", "2001-06-01",
                "2001-06-10", "2001-06-29", "2001-06-30", "2001-06-31",
                "2001-06-32", "2001-07-01", "2001-07-10", "2001-07-29",
                "2001-07-30", "2001-07-31", "2001-07-32", "2001-08-01",
                "2001-08-10", "2001-08-29", "2001-08-30", "2001-08-31",
                "2001-08-32", "2001-09-01", "2001-09-10", "2001-09-29",
                "2001-09-30", "2001-09-31", "2001-09-32", "2001-10-01",
                "2001-10-10", "2001-10-29", "2001-10-30", "2001-10-31",
                "2001-10-32", "2001-11-01", "2001-11-10", "2001-11-29",
                "2001-11-30", "2001-11-31", "2001-11-32", "2001-12-01",
                "2001-12-10", "2001-12-29", "2001-12-30", "2001-12-31",
                "2001-12-32", "2001-13-01", "2001-13-10", "2001-13-29",
                "2001-13-30", "2001-13-31", "2001-13-32", "245-12-04",
                "100-02-29" , "200-02-29" , "300-02-29" , "400-02-29",
                "500-02-29" , "800-02-29" , "900-02-29" , "2008-02-29",
                "1900-02-29", "2000-02-29", "1-01-01"   , "1-02-28",
                "0-01-01"   , "1-12-31"   , "351-02-29" , "352-02-29",
                "353-02-29" , "354-02-29" , "355-02-29" , "356-02-29",
                "357-02-29" , "358-02-29" , "350-02-29" , "1-02-29",
                "2-02-29"   , "3-02-29"   , "4-02-29"   , "5-02-29",
                "6-02-29"   , "7-02-29"   , "8-02-29"   , "9-02-29",
                "10-02-29"  , "11-02-29"  , "12-02-29"  , "13-02-29",
                "14-02-29"  , "15-02-29"  , "16-02-29"  , "17-02-29",
                "18-02-29"  , "19-02-29"  , "20-02-29"  , "21-02-29",
                "22-02-29"  , "23-02-29"  , "24-02-29"  , "25-02-29",
                "26-02-29"  , "27-02-29"  , "28-02-29"  , "29-02-29",
                "0-01-12"   , "00-01-12"  , "000-01-12" , "0000-01-12",
                "0028-02-29"
            };

        String moreMonth = 
                "(?:                         \n" +
                "   ## 日期前是大月              \n" +
                "   ## 01 03 05 07 08        \n" +
                "   ## 10 12 月                 \n" +
                "   (?<=                     \n" +
                "       (?:                  \n" +                
                "           0[13578]         \n" +
                "           |                \n" +
                "           1[02]            \n" +
                "       )                    \n" +
                "       -                    \n" +   // 分隔符
                  "   )                        \n" +
                "   (?:                      \n" +
                "       ## 大月的日期 29      \n" +
                "       29                   \n" +
                "       |                    \n" +
                "       ## 大月的日期 30~31   \n" +
                "       3[01]                \n" +
                "   )                        \n" +
                ")                           \n";
    
        String lesserMonth = 
                "(?:                         \n" +
                "   ## 日期前是小月             \n" +
                "   ## 04 06 09 11 月          \n" +
                "   (?<=                     \n" +
                "       (?:                  \n" +
                "           0[469]           \n" +
                "           |                \n" +
                "           11               \n" +
                "       )                    \n" +
                "       -                    \n" +   // 分隔符
                   "   )                        \n" +
                "   (?:                      \n" +
                "       ## 小月的日期 29      \n" +
                "       29                   \n" +
                "       |                    \n" +
                "       ## 小月的日期 30~31   \n" +
                "       30                   \n" +
                "   )                        \n" +
                ")                           \n";
        
        String feburary =
                "(?:                                             \n" +
                "   ## 29 号必须满足的条件,闰年                       \n" +
                "   (?<=                                         \n" +
                "       (?:                                      \n" +
                "           ## 被 4 整除的年份                        \n" +
                "           ## 除去最后两位为 0 的年份                \n" +
                "           ## 最后两位数一定是:                     \n" +
                "           ##    20 40 60 80                    \n" +
                "           ## 04 24 44 64 84                    \n" +
                "           ## 08 28 48 68 88                    \n" +
                "           ## 12 32 52 72 92                    \n" +
                "           ## 16 36 56 76 96                    \n" +
                "           (?:                                  \n" +
                "               [0-9]{0,2}(?!0?0)                \n" +
                "               (?:                              \n" +
                "                   ## 当尾数为[048]时前面不允是    \n" +
                "                   ## [13579]中的任何一个          \n" +
                "                   [02468]?(?<![13579])[048]    \n" +
                "                   |                            \n" +
                "                   [13579][26]                  \n" +
                "               )                                \n" +
                "           )                                    \n" +
                "           |                                    \n" +
                "           ## 能被 400 整除的年份                    \n" +
                "           ## 其头两位数字规定同上                    \n" +
                "           (?:                                  \n" +
                "               (?:                              \n" +            
                "                   [02468]?[048]                \n" +
                "                   |                            \n" +
                "                   [13579][26]                  \n" +
                "               )                                \n" +
                "               00                               \n" +
                "           )                                    \n" +
                "       )                                        \n" +
                "       -02-                                     \n" +   // 02 前后为分隔符
                   "   )                                            \n" +
                "   (?:29)                                       \n" +
                ")                                               \n";
        
        String debugRegex =
                "(?x:                                \n" +
                "   ## 年份 0001~9999,并忽略0年        \n" +
                "   (?:[0-9]{1,4}(?<!^0?0?0?0))      \n" +
                "   -                                \n" +   // 分隔符
                   "   ## 月份                               \n" +
                "   (?:                              \n" +
                "       0[1-9]                       \n" +
                "       |                            \n" +
                "       1[0-2]                       \n" +
                "   )                                \n" +
                "   -                                \n" +   // 分隔符
                   "   (?:                              \n" +
                "       ## 公共日期 01~09             \n" +
                "       0[1-9]                       \n" +
                "       |                            \n" +
                "       ## 公共日期 10~19             \n" +
                "       1[0-9]                       \n" +
                "       |                            \n" +
                "       ## 公共日期 20~28             \n" +
                "       2[0-8]                       \n" +
                "       |                            \n" +
                            moreMonth                    +
                "       |                            \n" +
                            lesserMonth                  +
                "       |                            \n" +
                            feburary                     +
                "   )                                \n" +
                ")";
        Pattern pattern = Pattern.compile(debugRegex);
        Matcher matcher = pattern.matcher("");
        for(String s : str) {
            matcher.reset(s);
            System.out.println(s + " " + matcher.matches());
        }
    }
}
/+〆=う
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

不过我的观点是像日期这种牵涉到逻辑的最好不要使用正则表达式来验证,太浪费了。那么长的表达式工作量是很大的,建议用正则表达式验证 9999-99-99 之类的数字格式,至于具体的数字用程序逻辑来实现。

像验证日期的这种正则表达式仅适合于正则表达式练习之用。
/+〆=う
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

QUOTE: 12楼 root
正则表达式好喜欢 就是没好点资料看看
________________________________

我在 CSDN 上发过一个帖子,有一些资料,有兴趣的话可以去看看。
http://topic.csdn.net/u/20080306/17/f37a1818-3968-49b4-8f79-e5564486d63e.html
/+〆=う
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

LS果子
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

顶一个~~~~
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

学生学习了。好好练习。
编辑 回复 快速回复 TOP

Re:正则表达式验证日期

来学习一下
编辑 回复 快速回复 TOP
发新话题