Java用于匹配日期模式的正则表达式[猿教程]

https://yuanjiaoc.com/tutorial/article/10197

介绍

如果使用得当,正则表达式是匹配各种模式的强大工具。

在这篇文章中,我们将使用java.util.regex包来确定一个给定的String是否包含一个有效的日期。

日期格式概述

我们将定义一个与国际公历有关的有效日期。我们的格式将遵循一般模式。YYYY-MM-DD。

让我们也包括闰年的概念,即包含2月29日这一天的一年。根据公历,除了那些可以被100除以的年份,包括那些可以被400除以的年份,如果该年份的数字可以被4平均除以,我们就称之为闰年。

在所有其他情况下,我们称其为正常年份。

有效日期示例:

  • 2017-12-31
  • 2020-02-29
  • 2400-02-29

无效日期示例:

  • 2017/12/31:不正确的分隔符
  • 2018-1-1:缺少前导零
  • 2018-04-31:四月的天数计算错误
  • 2100-02-29:今年不是闰年,因为值除以 100,所以 2 月限制为 28 天

实施解决方案

由于我们要使用正则表达式匹配日期,让我们首先勾勒出一个接口 DateMatcher,它提供了一个匹配方法:

public interface DateMatcher {
    boolean matches(String date);
}

我们将在下面逐步介绍实现,最终构建完整的解决方案。

匹配广泛的格式

我们将首先创建一个非常简单的原型来处理匹配器的格式约束:

class FormattedDateMatcher implements DateMatcher {

    private static Pattern DATE_PATTERN = Pattern.compile(
      "^\\d{4}-\\d{2}-\\d{2}$");

    @Override
    public boolean matches(String date) {
        return DATE_PATTERN.matcher(date).matches();
    }
}

在这里,我们指定一个有效的日期必须由三组由破折号分隔的整数组成。第一组由四个整数组成,其余两组各有两个整数。

匹配日期:2017-12-31、2018-01-31、0000-00-00、1029-99-72 不匹配日期:2018-01、2018-01-XX、2020/02/29

匹配特定的日期格式

我们的第二个示例接受日期标记范围以及我们的格式约束。为简单起见,我们将兴趣限制在 1900 – 2999 年。

现在我们成功地匹配了我们的一般日期格式,我们需要进一步限制它——以确保日期实际上是正确的:

^((19|2[0-9])[0-9]{2})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$

这里我们介绍了三组需要匹配的整数范围:

  • (19|2[0-9])[0-9]{2} 通过匹配一个以 19 或 2X 开头后跟几个任意数字的数字来覆盖有限的年份范围。
  • 0[1-9]|1[012] 匹配 01-12 范围内的月份数
  • 0[1-9]|[12][0-9]|3[01] 匹配 01-31 范围内的天数

匹配日期:1900-01-01、2205-02-31、2999-12-31 不匹配日期:1899-12-31、2018-05-35、2018-13-05、3000-01-01、2018-01-XX

匹配 2 月 29 日

为了正确匹配闰年,我们必须首先确定遇到闰年的时间,然后确保我们接受 2 月 29 日作为这些年份的有效日期。

由于我们限制范围内的闰年数量足够大,我们应该使用适当的整除规则来过滤它们:

  • 如果一个数的最后两位组成的数能被4整除,则原数能被4整除
  • 如果数字的最后两位是 00,则该数字能被 100 整除

这是一个解决方案:

^((2000|2400|2800|(19|2[0-9])(0[48]|[2468][048]|[13579][26]))-02-29)$

该模式由以下部分组成:

  • 2000|2400|2800 匹配一组闰年,在 1900-2999 的受限范围内,除数为 400
  • 19|20-9) 匹配除数为 4 且没有除数的所有白名单年份组合100 个
  • -02-29 匹配2 月 2 日

匹配二月份的一般日子

除了匹配闰年的 2 月 29 日,我们还需要匹配所有年份中 2 月的所有其他日子(1 - 28):

^(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))$

匹配日期:2018-02-01、2019-02-13、2020-02-25 不匹配日期:2000-02-30、2400-02-62、2018/02/28

匹配 31 天的月份

1 月、3 月、5 月、7 月、8 月、10 月和 12 月应匹配 1 到 31 天:

^(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))$

匹配日期:2018-01-31、2021-07-31、2022-08-31 不匹配日期:2018-01-32、2019-03-64、2018/01/31

匹配 30 天的月份

4 月、6 月、9 月和 11 月应匹配 1 到 30 天:

^(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30))$

匹配日期:2018-04-30、2019-06-30、2020-09-30 不匹配日期:2018-04-31、2019-06-31、2018/04/30

公历日期匹配器

现在我们可以将上面的所有模式组合成一个匹配器,以获得一个完整的 GregorianDateMatcher 满足所有约束:

class GregorianDateMatcher implements DateMatcher {

    private static Pattern DATE_PATTERN = Pattern.compile(
      "^((2000|2400|2800|(19|2[0-9])(0[48]|[2468][048]|[13579][26]))-02-29)$" 
      + "|^(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))$"
      + "|^(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))$" 
      + "|^(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30))$");

    @Override
    public boolean matches(String date) {
        return DATE_PATTERN.matcher(date).matches();
    }
}

我们使用了一个交替字符"|"来匹配四个分支中的至少一个。因此,2月的有效日期要么与闰年2月29日的第一个分支相匹配,要么与1至28日的任何一天的第二个分支相匹配。其余月份的日期则与第三和第四分支相匹配。

由于我们没有对这个模式进行优化以获得更好的可读性,所以可以自由地对它的长度进行试验。

此刻我们已经满足了所有的约束条件,我们在一开始就介绍了。

性能说明

解析复杂的正则表达式可能会大大影响执行流程的性能。本文的主要目的不是为了学习一种有效的方法来测试一个字符串在所有可能的日期集合中的成员资格。

如果需要一个可靠而快速的方法来验证一个日期,请考虑使用Java8提供的LocalDate.parse()。

结论

在这篇文章中,我们已经学会了如何使用正则表达式来匹配公历的严格格式化的日期,同时提供了格式、范围和月份长度的规则。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340