【问题标题】:Oracle Regular Expression for Date日期的 Oracle 正则表达式
【发布时间】:2020-05-31 14:20:42
【问题描述】:

我有以下匿名块。我写了一个小的正则表达式来接受日期。它能够验证日、月和年。 现在我想考虑唯一的世纪年,正则表达式也应该允许 RR 格式的年(世纪年)。另外,我想接受前缀中没有零的一天;目前,它仅在当月接受这个。请看以下场景:

> 01/12/2154 (Do not accept, It's not a century year)
> 11/12/1996 (Do not accept, It's not a century year)
> 11/2/24 (Accept, and a plsql logic in IF condition could change it to 2024; Add 20 to it)
> 1/9/2020 (Accept)
> 01/02/2020 (Accept)
> 1/29/20 (Accept)
> 01/11/25 (Accept)
> 1/09/2021 (Accept)

**

declare
date_v varchar2(40):= '01/01/2025';
date_n date;
begin
if regexp_like (date_v, '([0-9]|1[0-2])/([0-2][0-9]|3[0-1])/[0-9]{4}') then
-- logic
date_n:= TO_DATE(date_v,'MM/DD/YYYY');
dbms_output.put_line(date_n);
end if;
end;

【问题讨论】:

  • 一年内你不应该允许 两位 表示 YY,这就是引入 Y2K 错误 的原因。始终使用YYYY。坚持使用固定格式 'YYYY-MM-DD'ANSI 日期文字
  • 谢谢,拉利特。但是我怎样才能允许这种格式的日期 - 2020 年 1 月 1 日?你能看看我的正则表达式吗?

标签: sql oracle plsql


【解决方案1】:

您可以对日期执行正则表达式,但这不是检查日期是否有效的好方法,它只是检查格式是否正常。例如 30-FEB-2020 将通过您的正则表达式,但这不是有效日期。数据库可以代替您完成这项工作。我通常使用这样的东西:

FUNCTION is_valid_date (date_str_i VARCHAR2, format_i VARCHAR2) RETURN VARCHAR2
/* check if date is valid */
AS
  l_dummy_dt DATE;
  date_not_valid_for_m EXCEPTION;
  PRAGMA EXCEPTION_INIT(date_not_valid_for_m, -01839);  
BEGIN
  l_dummy_dt := TO_DATE(date_str_i,format_i);
  RETURN 'Y';
EXCEPTION WHEN date_not_valid_for_m THEN
  RETURN 'N';
END; 

请注意,仅当 TO_DATE 返回 ora-01839: date not valid for month specified 时才会失败,您可以轻松添加其他可能的错误,或者只使用 WHEN OTHERS THEN RETURN 'N';

【讨论】:

  • 为什么使用SELECT 语句而不是简单的l_dummy_dt := TO_DATE(date_str_i,format_i)
【解决方案2】:

Oracle 足够聪明,可以识别日期格式中带或不带 0 的字符串。

所以您可以直接将此字符串转换为TO_DATE('11/2/24', 'mm/dd/rrrr'),并且为了识别当前世纪,您可以使用'CC'TRUNC 转换为世纪,如下所示:

SQL> SELECT
  2      TO_DATE('11/2/24', 'mm/dd/rrrr') YOUR_DATE,
  3      CASE
  4          WHEN TRUNC(TO_DATE('11/2/24', 'mm/dd/rrrr'), 'CC') = TRUNC(SYSDATE, 'CC') THEN
  5              'ACCEPT'
  6          ELSE
  7              'REJECT'
  8      END RESULT
  9  FROM
 10      DUAL;

YOUR_DATE   RESULT
----------- ------
02-NOV-2024 ACCEPT

SQL>

上个世纪的另一个日期的查询结果:

SQL> SELECT
  2      TO_DATE('11/12/1996', 'mm/dd/rrrr') YOUR_DATE,
  3      CASE
  4          WHEN TRUNC(TO_DATE('11/12/1996', 'mm/dd/rrrr'), 'CC') = TRUNC(SYSDATE, 'CC') THEN
  5              'ACCEPT'
  6          ELSE
  7              'REJECT'
  8      END RESULT
  9  FROM
 10      DUAL;

YOUR_DATE   RESULT
----------- ------
12-NOV-1996 REJECT

SQL>

注意:我已经使用了一年 'rrrr',因为它将任何两位数年份转换为 1950-2049 年。

【讨论】:

    【解决方案3】:

    试试这个:

    with t(date_col) as (
    select '01/01/2014' from dual
    union all
    select '1/2/2014' from dual
    union all
    select '01/3/2014' from dual
    union all
    select '1/04/2014' from dual
    union all
    select '11/1/14' from dual)
    select date_col,
           case
             when regexp_instr(date_col, '^\d/\d/\d{4}$') = 1 then
              'd/m/yyyy'
             when regexp_instr(date_col, '^\d{2}/\d/\d{4}$') = 1 then
              'dd/m/yyyy'
             when regexp_instr(date_col, '^\d/\d{2}/\d{4}$') = 1 then
              'd/mm/yyyy'
             when regexp_instr(date_col, '^\d{2}/\d{2}/\d{4}$') = 1 then
              'dd/mm/yyyy'
             else
              'Unknown format'
           end date_format
      from t;
    
    DATE_COL   DATE_FORMAT
    ---------- --------------
    01/01/2014 dd/mm/yyyy
    1/2/2014   d/m/yyyy
    01/3/2014  dd/m/yyyy
    1/04/2014  d/mm/yyyy
    11/1/14    Unknown format
    

    参考这个链接:

    Regular expression on Dates in Oracle

    【讨论】:

      【解决方案4】:

      用正则表达式验证日期很困难,非常困难。问题是月份有不同的天数,而 29-2 月仅在某些年份有效。您的要求是使多种有效格式大大复合。
      以下函数验证 ISO-8601 格式 (YYYY-MM-DD) 的日期,并且仅验证该格式,但它确实验证日期 0001-01-01 到 9999-12-31 的每个月的正确天数,包括有效的闰日(2 月 29 日)。但又只是一种格式。建议不要尝试使用正则表达式验证日期,尤其是多种格式。

      create or replace function valid_iso_date (iso_date_string varchar2)
         return date
      is
         iso_date_pattern constant varchar2(256) := 
             '^((0[0-9]{2}[1-9]|[1-9][0-9]{3})-((0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01])|02-(0[1-9]|1[0-9]|2[0-8])|(0[469]|11)-(0[1-9]|[12][0-9]|30))$)|^(\d\d(0[48]|[2468][048]|[13579][26])-02-29$)';
      
         result_date date := null;
      begin 
         if regexp_like(iso_date_string, iso_data_pattern)
         then 
            result_date := to_date(iso_date_string,'yyyy-mm-dd');
         end if; 
         return result_date;
      end valid_iso_date; 
      

      【讨论】:

        猜你喜欢
        • 2014-04-16
        • 1970-01-01
        • 2012-12-17
        • 2020-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-09
        相关资源
        最近更新 更多