我需要帮助提出正则表达式以确保用户输入有效日期
该字符串的格式为mm/dd/yyyy
到目前为止,我已经提出了这个问题。
/\[1-9]|0[1-9]|1[0-2]\/\d{1,2}\/19|20\d\d/
我已经验证了正则表达式,用户无法输入高于12的日期,而且年份必须以“19”或“20”开头。我遇到麻烦的是找出验证当天的一些逻辑。这一天不应该超过31岁。
答案 0 :(得分:35)
0-31的正则表达式:
(0[1-9]|[12]\d|3[01])
或者如果你不想要前一个零的日子(例如05):
([1-9]|[12]\d|3[01])
答案 1 :(得分:12)
use DateTime;
其他解决方案都很好,可能是有效的,等等。通常情况下,你最终想要多做一点,然后再多做一点,最后你会有一些疯狂的代码和闰年,为什么你自己这样做呢?再次?
DateTime及其formatters是您的解决方案。使用它们!有时它们有点矫枉过正,但通常会在未来发挥作用。
my $dayFormat = new DateTime::Format::Strptime(pattern => '%d/%m/%Y');
my $foo = $dayFormat->parse_datetime($myDateString);
$foo
现在是一个DateTime对象。请享用。
如果您的日期字符串格式不正确,$foo
将为"undef"
而$dayFormat->errstr
会告诉您原因。
答案 2 :(得分:8)
01-31
,那么RegEx就可以了,只要有一些后端逻辑可以验证整个日期,如果愿意的话。我看到10,20的预期答案目前失败。
gawk 'BEGIN{ for(i=0;i<=32;i++){ if (i ~ /^([0-2]?[1-9]|3[01])$/){print i " yes"}else {print i " no"} } }
^([0-2]?[1-9]|3[01]|10|20)$
请考虑以下解决方案......
<强> 1。确定需要匹配的集合:
{01,...,09},{10,...,31}
{10,...,31}
可以拆分为=> {10,...,29},{30,31}
{1,...,31} => {1,...,9},{10,...,31}
<强> 2。每个子集的相应正则表达式:
---------------------------------
Sub-Set | Regular-Expression
---------------------------------
{01,...,09} | [0][1-9]
{10,...,29} | [1-2][0-9]
{30,31} | 3[01]
{1,...,9} | [1-9]
---------------------------------
现在,我们可以将([0][1-9])
和([1-9])
组合为([0]?[1-9])
。其中?
表示模式/符号出现0或1次。 [更新] - 感谢@MattFrear指出。
因此得到的RegEx是:^(([0]?[1-9])|([1-2][0-9])|(3[01]))$
在此处测试:http://regexr.com/?383k1 [更新]
答案 3 :(得分:5)
^(((((((0?[13578])|(1[02]))[\.\-/]?((0?[1-9])|([12]\d)|(3[01])))|(((0?[469])|(11))[\.\-/]?((0?[1-9])|([12]\d)|(30)))|((0?2)[\.\-/]?((0?[1-9])|(1\d)|(2[0-8]))))[\.\-/]?(((19)|(20))?([\d][\d]))))|((0?2)[\.\-/]?(29)[\.\-/]?(((19)|(20))?(([02468][048])|([13579][26])))))$
来自Expressions in category: Dates and Times
验证一个月内的正确天数,看起来甚至可以处理闰年。
您当然可以使用[\.\-/]
更改/
以仅允许斜杠。
答案 4 :(得分:2)
这并不是 很难......
qr#^
(?: 0[1-9] | 1[012] )
/
(?:
0[1-9] | 1[0-9] | 2[0-8]
| (?<! 0[2469]/ | 11/ ) 31
| (?<! 02/ ) 30
| (?<! 02/
(?= ...
(?:
.. (?: [02468][1235679] | [13579][01345789] )
| (?: [02468][1235679] | [13579][01345789] ) 00
)
)
) 29
)
/
[0-9]{4}
\z
#x
答案 5 :(得分:2)
如果您想检查有效日期,您必须做的不仅仅是检查数字和范围。幸运的是,Perl 已经拥有了你需要的一切。 Time::Piece 模块随 Perl 提供,可以解析日期。它知道如何解析日期并进行第一轮检查:
use v5.10;
use Time::Piece; # comes with Perl
my @dates = qw(
01/06/2021 01/37/456 10/6/1582 10/18/1988
2/29/1900 2/29/1996 2/29/2000
);
foreach my $date ( @dates ) {
my $t = eval { Time::Piece->strptime( $date, '%m/%d/%Y' ) };
unless( $t ) {
say "Date <$date> is not valid";
next;
}
say $t;
}
输出很有趣,这里没有其他解决方案可以处理这个问题。为什么 10/6/1582 是无效日期?它在公历中不存在,但这里有一个更简单的原因。 strptime
不处理 1900 年之前的日期。
但也要注意 2/29/1900
变成了 3/1/1900
。这很奇怪,我们应该解决这个问题,但在年份中没有闰年可以被 100 整除。好吧,除非它们可以被 400 整除,这就是 2/29/2000
起作用的原因。
Wed Jan 6 00:00:00 2021
Date <01/37/456> is not valid
Date <10/6/1582> is not valid
Tue Oct 18 00:00:00 1988
Thu Mar 1 00:00:00 1900
Thu Feb 29 00:00:00 1996
Tue Feb 29 00:00:00 2000
但让我们解决闰年问题。 tm
结构进行了一次愚蠢的转换。如果无论月份如何,各个数字都在合理范围内(天数为 0 到 31),那么它将这些天数转换为秒并将它们添加到偏移量中。这就是 2/29/1900 在一天后结束的原因:29 给出的秒数与 3/1/1900 相同。如果日期有效,它应该返回相同。因为我要来回这个,所以我在对它做任何事情之前先确定前导零的日期:
use v5.10;
use Time::Piece; # comes with Perl
my @dates = qw(
01/06/2021 2/29/1900 2/2/2020
);
foreach my $date ( @dates ) {
state $format = '%m/%d/%Y';
$date =~ s/\b(\d)\b/0$1/g; # add leading zeroes to lone digits
my $t = eval { Time::Piece->strptime( $date, $format ) };
unless( $t ) {
say "Date <$date> is not valid";
next;
}
unless( $t->strftime( $format ) eq $date ) {
say "Round trip failed for <$date>: Got <"
. $t->strftime( $format ) . ">";
next;
};
say $t;
}
现在输出是:
Wed Jan 6 00:00:00 2021
Round trip failed for <02/29/1900>: Got <03/01/1900>
Sun Feb 2 00:00:00 2020
这有点长,但这就是我们有子程序的原因:
if( date_is_valid( $date ) ) { ... }
还需要正则表达式吗?好的,让我们使用 (??{...})
构造来决定模式是否应该失败。匹配一堆数字并将其捕获到 $1
中。现在,使用 (??{...})
制作模式的下一部分,使用您喜欢的任何 Perl 代码。如果您接受捕获,则返回空模式。如果拒绝它,则返回模式 (*FAIL)
,这会立即导致整个匹配失败。没有更多棘手的交替。而这个使用了新的 chained comparison in v5.32(虽然我对此仍有疑虑):
use v5.32;
foreach ( qw(-1 0 1 37 31 5 ) ) {
if( /\A(\d+)(??{ (1 <= $1 <= 31) ? '' : '(*FAIL)' })\z/ ) {
say "Value <$1> is between 1 and 31";
}
}
答案 6 :(得分:1)
试一试:
/(0?[1-9]|1[012])\/(0?[1-9]|[12][0-9]|3[01])\/((19|20)\d\d)/
答案 7 :(得分:1)
正则表达必须吗?如果没有,最好使用其他方法,例如DateTime::Format::DateManip
my @dates = (
'04/23/2009',
'01/22/2010 6pm',
'adasdas',
'1010101/12312312/1232132'
);
for my $date ( @dates )
{
my $date = DateTime::Format::DateManip->parse_datetime( $date );
die "Bad Date $date" unless (defined $date);
print "$dt\n";
}
答案 8 :(得分:0)
0-31天的正则表达式:
0 [1-9] | [12] \ d | 3 [01])不带前缀0-当“ 1”,“ 23” ...
([[1-9] | [12] \ d | 3 [01]),带有前缀0-当“ 01”,“ 04”时
(0?[1-9] | [12] \ d | 3 [01])-带有或不带有“ 0”-当“”
答案 9 :(得分:0)
更简单的正则表达式:
([12]\d|3[01]|0?[1-9])
考虑接受的答案和以下表达式:
(0[1-9]|[12]\d|3[01])
此匹配01,但不匹配1
接受的答案中的另一个表达式:
([1-9]|[12]\d|3[01])
这匹配1但不匹配01
不可能添加OR子句使它们都正常工作。
我建议将两者匹配。希望这会有所帮助。
答案 10 :(得分:0)
我已经使用了一段时间,而我想到的最好的正则表达式如下:
\b(0)?(?(1)[1-9]|(3)?(?(2)[01]|[12][0-9]))\b|\b[1-9]\b
它将匹配以下数字:
1 01 10 15 20 25 30 31
它不符合以下条件:
32 40 50 90