日付正規表現の概要

Nov 29, 2017 am 09:55 AM
要約する 通常 表現

PHP プログラマーとして、私たちは必然的に日付の正規表現に触れることがあります。では、プログラマーとして、日付の正規表現に対する解決策はいくつありますか?この記事では、日付の正規表現について詳しく説明します。

1 概要

日付の正規化は通常、形式要件があり、データがユーザーによって直接入力されない場合に使用されます。アプリケーションシナリオが異なるため、記述される通常のルールも異なり、当然複雑さも異なります。通常の文章では、特定の状況に基づいた詳細な分析が必要です。基本原則は、複雑なものではなく、適切なものだけを書くことです。

日付抽出の場合、非日付と区別できる限り、

\d{4}-\d{2}-\d{2}
ログイン後にコピー

のような最も単純な正規表現を書くだけです

yyyy-MM-dd形式の日付がソース文字列内で一意に特定できる場合は、抽出に使用できます。

検証は文字構成や書式だけを検証するだけではあまり意味がなく、ルールの検証も加える必要があります。うるう年の存在により、日付の検証がより複雑になります。

まず、日付の有効範囲とうるう年について調べてみましょう。


2 日付ルール


2.1 日付の有効範囲

日付の有効範囲は、アプリケーションのシナリオによって異なります。

MSDN で定義されている DateTime オブジェクトの有効範囲は、0001-01-01 00:00:00 ~ 9999-12-31 23:59:59 です。

ISO 8601 仕様によると、UNIX タイムスタンプの 0 は 1970-01-01T00:00:00Z です。

実際のアプリケーションでは、日付範囲は基本的にDateTimeで指定された範囲を超えることはありませんので、通常の検証ではよく使用される日付範囲のみを使用できます。


2.2 閏年とは

(以下、百度百科より引用)

閏年(うるう年)は、人工暦の規制によって生じる年間日数と、暦年との時差を埋めるために設けられました。実際の地球の公転周期。時差が埋められる年は閏年となります。

太陽の周りを回る地球の公転周期は365日5時間48分46秒(365.24219日)で、熱帯年です。 年)。グレゴリオ暦の通常の年は 365 日しかなく、熱帯年よりも約 0.2422 日短いです。 4 年ごとに約 1 日ずつ加算され、2 月の終わり (つまり 2 月 29 日) が加算され、1 年の長さは 366 日になります。

現在のグレゴリオ暦はローマ人の「ユリウス暦」を基にしていることに注意してください。当時は、紀元前 46 年から 16 世紀まで、毎年 0.0078 日余分に計算する必要があることを理解していなかったので、合計 10 日余分に計算されていました。このため、当時の教皇グレゴリウス13世は1582年10月5日を人為的に10月15日と定めた。そして新たな閏年規制が始まりました。つまり、グレゴリオ暦は100年であり、閏年となるためには400の倍数でなければならず、400の倍数でなければ平年となると規定されている。たとえば、1700 年、1800 年、1900 年は平年で、2000 年はうるう年です。それ以来、年間の平均長さは 365.2425 日となり、約 4 年間で 1 日の誤差が生じています。 4 年ごとのうるう年の計算によれば、毎年平均 0.0078 日が追加され、400 年後には約 3 日余分に存在することになります。したがって、400 年ごとに 3 日のうるう年が減ります。閏年の計算は通常どおり要約できます。4 年ごとに閏があり、100 年ごとに閏はなく、400 年ごとにまた閏があります。


2.3 日付形式

言語や文化が異なると、日付のハイフンは通常次の形式になります:

yyyyMMdd

yyyy-MM-dd

yyyy/MM/dd

yyyy.MM.dd


3 日付正規表現の構築


3.1 ルール分析

複雑な正規表現を記述する一般的な方法は、まず無関係な要件を分離し、対応する正規表現を個別に記述し、次にそれらを組み合わせて相互関係と影響を確認することです。基本的には対応する規則性を取得します。

閏年の定義によれば、日付はいくつかの方法で分類できます。

3.1.1 年に関係する日数に応じて 2 つのカテゴリーに分けられますが、年に関係しないカテゴリーは、各月の日数に応じてさらに 2 つのカテゴリーに分けられます。 5、7、8、10、12月は1~31日

4、6、9、11月は1~30日

年に関係するカテゴリ内

平年の2月は1~28日

閏年の 2 月は 1 ~ 29 です

すべての年のすべての月には 1 ~ 28 日が含まれます

2 月を除くすべての年には 29 日と 30 日が含まれます

1、3、5、7、8、10、および 12 月のすべての月には 31 日が含まれます

閏年の 2 月には 29 日が含まれます

3.1.2 日付に応じて4つのカテゴリーに分けることができます

3.1.3 分類方法が

に選ばれるのは、日付分類後の実装が (exp1|exp2|exp3) の分岐構造を通じて実現され、分岐構造が左の分岐から右に向かって開始され、When に一致する分岐が試行されるためです。成功した場合は、右への試行は行われません。そうでない場合は、すべての分岐を試行し、失敗を報告します。

分岐の数と各分岐の複雑さはマッチング効率に影響します。検証された日付の確率分布を考慮すると、そのほとんどは 1 ~ 28 日以内に収まるため、2 番目の分類方法を使用するとマッチング効率が効果的に向上します。 。

3.2 通常の実装

セクション 3.1.2 の分類方法を使用して、対応する通常のルールをルールごとに記述することができます。次の実装は一時的に MM-dd 形式に基づいています。

先考虑与年份无关的前三条规则,年份可统一写作

(?!0000)[0-9]{4}
ログイン後にコピー

下面仅考虑月和日的正则

包括平年在内的所有年份的月份都包含1-28日

(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])

包括平年在内的所有年份除2月外都包含29和30日

(0[13-9]|1[0-2])-(29|30)
ログイン後にコピー

包括平年在内的所有年份1、3、5、7、8、10、12月都包含31日

(0[13578]|1[02])-31)
ログイン後にコピー

合起来就是除闰年的2月29日外的其它所有日期

(?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)
ログイン後にコピー


接下来考虑闰年的实现

闰年2月包含29日

这里的月和日是固定的,就是02-29,只有年是变化的。

可通过以下代码输出所有的闰年年份,考察规则

for (int i = 1; i < 10000; i++){
  if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0){
   richTextBox2.Text += string.Format("{0:0000}", i) + "\n";
  }
}
ログイン後にコピー

根据闰年的规则,很容易整理出规则,四年一闰;

([0-9]{2}(0[48]|[2468][048]|[13579][26])
ログイン後にコピー

百年不闰,四百年再闰。

(0[48]|[2468][048]|[13579][26])00
ログイン後にコピー

合起来就是所有闰年的2月29日

([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)
ログイン後にコピー

四条规则都已实现,且互相间没有影响,合起来就是所有符合DateTime范围的日期的正则

^((?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)|([0-9]{2}(0[48]|[2468]
[048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)$
ログイン後にコピー


考虑到这个正则表达式仅仅是用作验证,所以捕获组没有意义,只会占用资源,影响匹配效率,所以可以使用非捕获组来进行优化。

^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468]
[048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$
ログイン後にコピー

以上正则年份0001-9999,格式yyyy-MM-dd。可以通过以下代码验证正则的有效性和性能

DateTime dt = new DateTime(1, 1, 1);
DateTime endDay = new DateTime(9999, 12, 31);
Stopwatch sw = new Stopwatch();
sw.Start();
   
Regex dateRegex = new Regex(@"^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$");
   
//Regex dateRegex = new Regex(@"^((?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)|([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)$");
Console.WriteLine("开始日期: " + dt.ToString("yyyy-MM-dd"));
while (dt < endDay){
  if (!dateRegex.IsMatch(dt.ToString("yyyy-MM-dd"))){
   Console.WriteLine(dt.ToString("yyyy-MM-dd") + " false");
  }
  dt = dt.AddDays(1);
}
if (!dateRegex.IsMatch(dt.ToString("yyyy-MM-dd"))){
  Console.WriteLine(dt.ToString("yyyy-MM-dd") + " false");
}
Console.WriteLine("结束日期: " + dt.ToString("yyyy-MM-dd"));
sw.Stop();
Console.WriteLine("测试用时: " + sw.ElapsedMilliseconds + "ms");
Console.WriteLine("测试完成!");
Console.ReadLine();
ログイン後にコピー


4 日期正则表达式扩展

4.1 “年月日”形式扩展

  以上实现的是yyyy-MM-dd格式的日期验证,考虑到连字符的不同,以及月和日可能为M和d,即yyyy-M-d的格式,可以对以上正则进行扩展

^(?:(?!0000)[0-9]{4}([-/.]?)(?:(?:0?[1-9]|1[0-2])([-/.]?)(?:0?[1-9]|1[0-9]|2[0-8])|(?:0?[13-9]|1[0-2])([-/.]?)(?:29|30)|(?:0?[13578]|1[02])
([-/.]?)31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)([-/.]?)0?2([-/.]?)29)$
ログイン後にコピー


  使用反向引用进行简化,年份0001-9999,格式yyyy-MM-dd或yyyy-M-d,连字符可以没有或是“-”、“/”、“.”之一。

^(?:(?!0000)[0-9]{4}([-/.]?)(?:(?:0?[1-9]|1[0-2])\1(?:0?[1-9]|1[0-9]|2[0-8])|(?:0?[13-9]|1[0-2])\1(?:29|30)|
(?:0?[13578]|1[02])\1(?:31))|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)([-/.]?)0?2\2(?:29))$
ログイン後にコピー


  这就是“年月日”这种形式最全的一个正则了,不同含义部分以不同颜色标识,可以根据自己的需要进行栽剪。

4.2 其它形式扩展

  了解了以上正则各部分代表的含义,互相间的关系后,就很容易扩展成其它格式的日期正则,如dd/MM/yyyy这种“日月年”格式的日期。

^(?:(?:(?:0?[1-9]|1[0-9]|2[0-8])([-/.]?)(?:0?[1-9]|1[0-2])|(?:29|30)([-/.]?)(?:0?[13-9]|1[0-2])|31([-/.]?)
(?:0?[13578]|1[02]))([-/.]?)(?!0000)[0-9]{4}|29([-/.]?)0?2([-/.]?)
(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00))$
ログイン後にコピー


  这种格式需要注意的就是不能用反向引用来进行优了。连字符等可根据自己的需求栽剪。

4.3 添加时间的扩展

  时间的规格很明确,也很简单,基本上就HH:mm:ss和H:m:s两种形式。

([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]
ログイン後にコピー

合入到日期的正则中,yyyy-MM-dd HH:mm:ss

^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|
(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579]
[26])00)-02-29)\s+([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$
ログイン後にコピー

4.4 年份定制

  以上所有涉及到平年的年份里,使用的是0001-9999。当然,年份也可以根据闰年规则定制。

  如年份1600-9999,格式yyyy-MM-dd或yyyy-M-d,连字符可以没有或是“-”、“/”、“.”之一。

^(?:(?:1[6-9]|[2-9][0-9])[0-9]{2}([-/.]?)(?:(?:0?[1-9]|1[0-2])\1(?:0?[1-9]|1[0-9]|2[0-8])|
(?:0?[13-9]|1[0-2])\1(?:29|30)|(?:0?[13578]|1[02])\1(?:31))|(?:(?:1[6-9]|[2-9][0-9])
(?:0[48]|[2468][048]|[13579][26])|(?:16|[2468][048]|[3579][26])00)([-/.]?)0?2\2(?:29))$
ログイン後にコピー

5 特别说明

  以上正则采用的是最基本的正则语法规则,绝大多数采用传统NFA引擎的语言都可以支持,包括JavaScript、Java、.NET等。

  另外需求说明的是,虽然日期的规则相对明确,可以采用这种方式裁剪来得到符合要求的日期正则,但是并不推荐这样使用正则,正则的强大在于它的灵活性,可以根据需求,量身打造最合适的正则,如果只是用来套用模板,那正则也就不称其为正则了。

  正则的语法规则并不多,而且很容易入门,掌握语法规则,量体裁衣,才是正则之“道”。

6 应用

一、首先看需求

  日期的输入:

  手动输入,可输入两种格式yyyymmdd或yyyy-mm-dd

二、解决思路

用户手动输入日期,需要验证输入的日期格式

用户可能的输入情况可以分为以下几种:

(1).输入为空或者为空格

(2).输入非日期格式

  根据保存到数据库中的日期格式,保存的格式为yyyy-mm-dd,所以用户在输入yyyymmdd后需要进行转换,转换成yyyy-mm-dd。

  思路:

  验证日期格式,首现想到的是VS的验证控件,但是因为需要验证的控件有几十个,使用验证控件就需要一个个的拉控件,如果后期需要修改也很麻烦,而通过JS实现控制,再通过正则表达式对日期进行验证。

三、JS实现

//验证日期
function date(id) {
  var idvalue = document.getElementById(id).value;  //通过查找元素
  var tmpStr = "";
  var strReturn = "";
  //调用trim()去掉空格,因为js不支持trim()
  var iIdNo = trim(idvalue);
  //正则表达式,判断日期格式,包括日期的界限,日期的格式,平年和闰年
  var v = idvalue.match(/^((((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-))$/);
  //输入为空时跳过检测
  if (iIdNo.length == 0) {
    return false;
  }
  //自动更改日期格式为yyyy-mm-dd
  if (iIdNo.length == 8) {
    tmpStr = iIdNo.substring(0, 8);
    tmpStr = tmpStr.substring(0, 4) + "-" + tmpStr.substring(4, 6) + "-" + tmpStr.substring(6, 8)
    document.getElementById(id).value = tmpStr;
    document.getElementById(id).focus();
  }
  //验证,判断日期格式
  if ((iIdNo.length != 8) && !v) {
    strReturn = "日期格式错误,提示:19990101或1999-01-01";
    alert(strReturn);
    document.getElementById(id).select();
    return false;
  }
}
//运用正则表达式去除字符串两端空格(因为js不支持trim()) 
function trim(str) {
  return str.replace(/(^\s*)|(\s*$)/g, "");
}
//前台调用(获得焦点触发)
<input class="txtenterschooldate" size="14" type="text" id="txtenterschooldate" name="txtenterschooldate" onblur="date(&#39;txtenterschooldate&#39;)"/>
ログイン後にコピー

以上内容就是关于日期正则表达式的思路详解,如果大家觉得有用那就赶紧收藏起来吧。

相关推荐:

日期正则表达式详解

关于日期正则表达式解决思路

phpの日付正規表現


以上が日付正規表現の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Linux システムでの system() 関数の使用法の概要 Linux システムでの system() 関数の使用法の概要 Feb 23, 2024 pm 06:45 PM

Linux での system() 関数の概要 Linux システムでは、system() 関数は非常に一般的に使用される関数であり、コマンド ライン コマンドの実行に使用できます。この記事では、system() 関数を詳細に紹介し、いくつかの具体的なコード例を示します。 1. system() 関数の基本的な使用法 system() 関数の宣言は次のとおりです: intsystem(constchar*command); コマンド パラメーターは文字です。

Golang 正規表現を使用して複数の単語または文字列を照合するにはどうすればよいですか? Golang 正規表現を使用して複数の単語または文字列を照合するにはどうすればよいですか? May 31, 2024 am 10:32 AM

Golang の正規表現では、パイプ文字 | を使用して複数の単語または文字列を一致させ、各オプションを論理 OR 式として区切ります。例: 「fox」または「dog」に一致します: fox|dog は「quick」、「brown」または「lazy」に一致します: (quick|brown|lazy) 「Go」、「Python」または「Java」に一致します: Go| Python |Java は単語または 4 桁の郵便番号と一致します: ([a-zA

PHP正規表現で何かで始まる文字列を置換する方法 PHP正規表現で何かで始まる文字列を置換する方法 Mar 24, 2023 pm 02:57 PM

PHP 正規表現は、テキストの処理と変換のための強力なツールです。テキスト コンテンツを解析し、特定のパターンに従って置換またはインターセプトすることで、テキスト情報を効果的に管理できます。その中でも、正規表現の一般的な応用例は、特定の文字で始まる文字列を置換することです。

正規表現を使用してPHPで漢字を削除する方法 正規表現を使用してPHPで漢字を削除する方法 Mar 03, 2023 am 10:12 AM

正規表現を使用して PHP で中国語を削除する方法: 1. PHP サンプル ファイルを作成する; 2. 中国語と英語を含む文字列を定義する; 3. "preg_replace('/([\x80-\xff]*)/i', '',$a);" 通常の方法では、クエリ結果から中国語の文字を削除できます。

通常のマッチングを使用してphpでhtmlタグを削除する方法 通常のマッチングを使用してphpでhtmlタグを削除する方法 Mar 21, 2023 pm 05:17 PM

この記事では、PHP 正規表現を使用して HTML タグを削除し、HTML 文字列からプレーン テキスト コンテンツを抽出する方法を学びます。 HTML タグを削除する方法を示すために、まず HTML タグを含む文字列を定義しましょう。

PHP 正規表現を使用して URL が HTTPS プロトコルであるかどうかを確認する方法 PHP 正規表現を使用して URL が HTTPS プロトコルであるかどうかを確認する方法 Jun 24, 2023 am 08:16 AM

Web サイトのセキュリティはますます注目を集めており、HTTPS プロトコルを使用してデータ送信のセキュリティを確保することが、現在の Web サイト開発の重要な部分となっています。 PHP 開発において、正規表現を使用して URL が HTTPS プロトコルであるかどうかを確認するにはどうすればよいですか?ここで私たちはそれを知りに来ました。正規表現 正規表現は、ルールを記述するために使用される表現です。テキストを処理するための強力なツールであり、テキストの一致、検索、置換に広く使用されています。 PHP 開発では、正規表現を使用して URL 内の http と一致させることができます。

PHP 正規表現を使用して中国語置換関数を実装するためのヒントを共有する PHP 正規表現を使用して中国語置換関数を実装するためのヒントを共有する Mar 24, 2024 pm 05:57 PM

PHP 正規表現を使用して中国語の置換機能を実装するためのヒントを共有する Web 開発では、中国語のコンテンツを置換する必要がある状況によく遭遇します。人気のサーバーサイド スクリプト言語として、PHP は中国語の置換を簡単に実現できる強力な正規表現機能を提供します。この記事では、正規表現を使用して PHP で中国語置換を実装するためのテクニックをいくつか紹介し、具体的なコード例を示します。 1. preg_replace 関数を使用して中国語置換を実装します PHP の preg_replace 関数を使用できます

PHP 定期置換例: 置換スキルを素早くマスター PHP 定期置換例: 置換スキルを素早くマスター Feb 29, 2024 pm 06:33 PM

PHP の定期置き換え例: 置き換えスキルをすぐにマスターする インターネットの発展に伴い、Web サイトの開発はますます一般的になりました。 Web サイトの開発では、文字列の置換が必要になることがよくあります。正規表現は、文字列をすばやく検索して置換できる非常に強力なツールです。この記事では、PHP 言語で正規表現を使用して置換操作を実行する方法を紹介し、読者が置換テクニックをすぐに習得できるように具体的なコード例を示します。 1.PHPのpreg_replace関数、pregを使用できます

See all articles