QML中日期和时间的正则表达式(DD/MM/YYYY hh:mm:ss)

Regular Expression for date and time(DD/MM/YYYY hh:mm:ss) in QML

在 QML2 中,我没有找到任何 Calender 控件,我已经实现了一个将日期和时间作为输入的控件,我正在使用正则表达式进行验证,该表达式匹配包括闰年和其他日期在内的日期验证。

主要问题是space/backspace也应该算一个有效例如:

\s\s/\s\s/\s\s \s\s:\s\s:\s\s

代码如下:

TextField{
    id:textEditDate

    width:parent.width * 0.50
    height:parent.height
    text : "01/01/2017 00:00:00"

    inputMask: "99/99/9999 99:99:99"

    validator: RegExpValidator { regExp: /^(((([0\s][1-9\s]|[1\s][0-9\s]|[2\s][0-8\s])[\/]([0\s][1-9\s]|[1\s][012\s]))|((29|30|31)[\/]([0\s][13578\s]|[1\s][02\s]))|((29|30)[\/]([0\s][4,6,9]|11)))[\/]([19\s[2-9\s][0-9\s])\d\d|(^29[\/]02[\/]([19\s]|[2-9\s][0-9\s])(00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)))\s([0-1\s]?[0-9\s]|2[0-3\s]):([0-5\s][0-9\s]):([0-5\s][0-9\s])$/}

    horizontalAlignment: Text.AlignHCenter
    inputMethodHints: Qt.ImhDigitsOnly
}

现在,除年份外一切正常,我无法匹配 backspace/space 年份,用户无法清除年份。

你能建议如何实现这一目标吗?或者有没有其他方法可以做到这一点。

您匹配年份的顺序是:

([19\s[2-9\s][0-9\s])\d\d

看起来格式不正确,因为括号不匹配。

此外,两个数字的存在(使用\d)意味着表达式将不匹配白色space。

回答

简介

所以我决定制作一个真正适用于闰年的非常好的正则表达式!然后我添加了您需要的其余逻辑,瞧,太美了!


代码

See regex in use here

(?(DEFINE)
  (?# Date )
    (?# Day ranges )
    (?<d_day28>0[1-9]|1\d|2[0-8])
    (?<d_day29>0[1-9]|1\d|2\d)
    (?<d_day30>0[1-9]|1\d|2\d|30)
    (?<d_day31>0[1-9]|1\d|2\d|3[01])
    (?# Month specifications )
    (?<d_month28>02)
    (?<d_month29>02)
    (?<d_month30>0[469]|11)
    (?<d_month31>0[13578]|1[02])
    (?# Year specifications )
    (?<d_year>\d+)
    (?<d_yearLeap>(?:\d*?(?:(?:(?!00)[02468][048]|[13579][26])|(?:(?:[02468][048]|[13579][26])00))|[48]00|[48])(?=\D))
    (?# Valid date formats )
    (?<d_format>
      (?&d_day28)\/(?&d_month28)\/(?&d_year)|
      (?&d_day29)\/(?&d_month29)\/(?&d_yearLeap)|
      (?&d_day30)\/(?&d_month30)\/(?&d_year)|
      (?&d_day31)\/(?&d_month31)\/(?&d_year)
    )

  (?# Time )
    (?# Time properties )
    (?<t_period12>(?i)[ap]m|[ap]\.m\.(?-i))
    (?# Hours )
    (?<t_hours12>0\d|1[01])
    (?<t_hours24>[01]\d|2[0-3])
    (?# Minutes )
    (?<t_minutes>[0-5]\d)
    (?# Seconds )
    (?<t_seconds>[0-5]\d)
    (?# Milliseconds )
    (?<t_milliseconds>\d{3})
    (?# Valid time formats )
    (?<t_format>
      (?&t_hours12):(?&t_minutes):(?&t_seconds)(?:\.(?&t_milliseconds))?\ ?(?&t_period12)|
      (?&t_hours24):(?&t_minutes):(?&t_seconds)(?:\.(?&t_milliseconds))?
    )

  (?# Datetime )
    (?<dt_format>(?&d_format)\ (?&t_format))
)
\b(?&dt_format)\b

或者在一行中...

See regex in use here

\b(?:(?:0[1-9]|1\d|2[0-8])\/(?:02)\/(?:\d+)|(?:0[1-9]|1\d|2\d)\/(?:02)\/(?:(?:\d*?(?:(?:(?!00)[02468][048]|[13579][26])|(?:(?:[02468][048]|[13579][26])00))|[48]00|[48])(?=\D))|(?:0[1-9]|1\d|2\d|30)\/(?:0[469]|11)\/(?:\d+)|(?:0[1-9]|1\d|2\d|3[01])\/(?:0[13578]|1[02])\/(?:\d+))\ (?:(?:0\d|1[01]):(?:[0-5]\d):(?:[0-5]\d)(?:\.(?:\d{3}))?\ ?(?:(?i)[ap]m|[ap]\.m\.(?-i))|(?:[01]\d|2[0-3]):(?:[0-5]\d):(?:[0-5]\d)(?:\.(?:\d{3}))?)\b

说明

我将解释第一个版本,因为第二个版本只是它的精简版。请注意,可以轻松更改正则表达式以适应更多格式(只接受一种略有变化的格式,但这是一个非常可定制的正则表达式)。

  • d_days28:匹配从0128
  • 的任意数字
  • d_days29:匹配从0129
  • 的任意数字
  • d_days30:匹配从0130
  • 的任意数字
  • d_days31:匹配从0131
  • 的任意数字
  • d_month28:匹配可能只有 28 天的月份(二月 - 因此 02
  • d_month29:匹配可能只有 29 天的月份(二月 - 因此 02
  • d_month30:匹配只有 30 天的月份(四月、六月、九月、十一月 - 因此 04, 06, 09, 11)
  • d_month31:匹配只有31天的月份(一月、三月、五月、七月、八月、十月, 十二月 - 因此 01, 03, 05, 07, 08, 10, 12)
  • d_year:匹配任意年份(必须至少有一个数字\d
  • d_yearLeap:为了更清楚起见,我将把它分成多个部分
    • \d*?
      • 匹配任意数量的数字,但尽可能少
    • 符合下列其中一项
      • (?:(?:(?!00)[02468][048]|[13579][26])|(?:(?:[02468][048]|[13579][26])00)) - 匹配以下其中一项
      • (?:(?!00)[02468][048]|[13579][26]) - 匹配以下其中一项
        • 02468之一,后跟048之一,但不是00
        • 13579之一,后跟26
        • 之一
      • (?:(?:[02468][048]|[13579][26])00) - 匹配以下其中一项,后跟 00
        • 02468之一,后跟048
        • 之一
        • 13579之一,后跟26
        • 之一
      • [48]00 - 匹配 400800
      • [48] - 匹配 48
    • (?=\D|\b) - 确保后面是非数字字符 \D 或单词边界字符 \b
  • d_format:这指向前面的组,以确保月份格式正确并匹配 days/month 和 days/year(leap year) 要求,以便我们确保正确的日期验证
  • t_period:添加此内容是为了防止其他人出于验证目的需要此内容
    • 确保句点是 am, pm, a.m, p.m 或其各自的大写版本(包括使用多个大小写的 a.M 等)
  • t_hours12:匹配从0011
  • 的任何时间
  • t_hours24:匹配从0023
  • 的任何时间
  • t_minutes:匹配从0059
  • 的任意分钟
  • t_seconds:匹配从0059
  • 的任意秒数
  • t_milliseconds:匹配任意3位数字(000999
  • t_format:这指向前面的组以确保时间格式正确。我已经添加了一个额外的时间设置(以及一个包括毫秒和时间段的附加项供其他人使用)
  • dt_format:要检查的日期时间格式(在您的情况下是 date time - 用 space [=198= 分隔]字符)
  • 定义块之后是\b(?&dt_format)\b,它简单地匹配上面指定的dt_format,确保它之前和取代它的是单词边界字符(或没有字符)\b

闰年

进一步了解正则表达式的闰年部分...

我假设如下:

  • 所有年份都是 不是 闰年,除非下列情况为真
    • ((年份模 40) AND (年份模 100 不是 0)) OR(年模 4000
    • 来源:leap year calculation
    • 闰年一直存在(至少从第 1 年开始)- 因为我不想开始假设并做更多研究。

正则表达式的工作原理是确保:

  1. 所有以 0, 4, 8 结尾的闰年 之前有一个 0, 2, 4, 6, 8(所有这些都会在模数后产生 0 ->即 24 % 4 = 0)
  2. 所有以 2, 6 结尾的闰年 之前有一个 1, 3, 5, 7, 9(所有这些都会在模数后产生 0 ->即 32 % 4 = 0)
  3. 对于 1. 和 2.,所有以 00 结尾的闰年都被取反((?!00) 这样做)
  4. 所有以 00 结尾的闰年 之前 1。和 2.(与 4 * 100 = 400 完全相同 - 除了最后两位数字外无需更改)
  5. 添加年份400, 800, 4, 8,因为它们不满足上述任何条件


编辑

2017 年 10 月 25 日

感谢 @sln for the input on the leap year's functionality. The regex below performs slightly faster due to changes provided in the comments of this answer by sln(关于一个单独的问题)。在闰年部分将 (?:(?!00)[02468][048]|[13579][26]) 更改为 (?:0[48]|[13579][26]|[2468][048])

See regex in use here

(?(DEFINE)
  (?# Date )
    (?# Day ranges )
    (?<d_day28>0[1-9]|1\d|2[0-8])
    (?<d_day29>0[1-9]|1\d|2\d)
    (?<d_day30>0[1-9]|1\d|2\d|30)
    (?<d_day31>0[1-9]|1\d|2\d|3[01])
    (?# Month specifications )
    (?<d_month28>02)
    (?<d_month29>02)
    (?<d_month30>0[469]|11)
    (?<d_month31>0[13578]|1[02])
    (?# Year specifications )
    (?<d_year>\d+)
    (?<d_yearLeap>(?:\d*?(?:(?:0[48]|[13579][26]|[2468][048])|(?:(?:[02468][048]|[13579][26])00))|[48]00|[48])(?=\D|\b))
    (?# Valid date formats )
    (?<d_format>
      (?&d_day28)\/(?&d_month28)\/(?&d_year)|
      (?&d_day29)\/(?&d_month29)\/(?&d_yearLeap)|
      (?&d_day30)\/(?&d_month30)\/(?&d_year)|
      (?&d_day31)\/(?&d_month31)\/(?&d_year)
    )

  (?# Time )
    (?# Time properties )
    (?<t_period12>(?i)[ap]m|[ap]\.m\.(?-i))
    (?# Hours )
    (?<t_hours12>0\d|1[01])
    (?<t_hours24>[01]\d|2[0-3])
    (?# Minutes )
    (?<t_minutes>[0-5]\d)
    (?# Seconds )
    (?<t_seconds>[0-5]\d)
    (?# Milliseconds )
    (?<t_milliseconds>\d{3})
    (?# Valid time formats )
    (?<t_format>
      (?&t_hours12):(?&t_minutes):(?&t_seconds)(?:\.(?&t_milliseconds))?\ ?(?&t_period12)|
      (?&t_hours24):(?&t_minutes):(?&t_seconds)(?:\.(?&t_milliseconds))?
    )

  (?# Datetime )
    (?<dt_format>(?&d_format)\ (?&t_format))
)
\b(?&dt_format)\b