Pregunta

Estoy intentando escribir una expresión regular que valide una fecha.La expresión regular debe coincidir con lo siguiente

  • M/D/AAAA
  • DD/MM/AAAA
  • Los meses de un solo dígito pueden comenzar con un cero a la izquierda (por ejemplo:12/03/2008)
  • Los días de un solo dígito pueden comenzar con un cero a la izquierda (por ejemplo:3/02/2008)
  • NO PUEDE incluir el 30 de febrero o el 31 de febrero (por ejemplo:31/02/2008)

hasta ahora tengo

^(([1-9]|1[012])[-/.]([1-9]|[12][0-9]|3[01])[-/.](19|20)\d\d)|((1[012]|0[1-9])(3[01]|2\d|1\d|0[1-9])(19|20)\d\d)|((1[012]|0[1-9])[-/.](3[01]|2\d|1\d|0[1-9])[-/.](19|20)\d\d)$

Esto coincide correctamente EXCEPTO que todavía incluye el 30/02/2008 y el 31/02/2008.

¿Alguien tiene una sugerencia mejor?

Editar: encontré la respuesta en RegExLib

^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$

Coincide con todos los meses válidos que siguen el formato MM/DD/AAAA.

Gracias a todos por la ayuda.

¿Fue útil?

Solución

Este no es un uso apropiado de expresiones regulares.Sería mejor que usaras

[0-9]{2}/[0-9]{2}/[0-9]{4}

y luego verificar rangos en un lenguaje de nivel superior.

Otros consejos

Aquí está el Reg ex que coincide con todas las fechas válidas, incluidos los años bisiestos.Formatos aceptados mm/dd/aaaa o mm-dd-aaaa o mm.dd.aaaa

^(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

cortesía Asiq Ahamed

Llegué aquí porque el título de esta pregunta es amplio y estaba buscando una expresión regular que pudiera usar para hacer coincidir un formato de fecha específico (como el OP).Pero luego descubrí, como lo han destacado ampliamente muchas de las respuestas y comentarios, que existen muchos obstáculos que hacen que la construcción de un patrón efectivo sea muy complicada al extraer fechas que se mezclan con datos de origen no estructurados o de mala calidad.

En mi exploración de los problemas, se me ocurrió un sistema que le permite crear una expresión regular organizando cuatro subexpresiones más simples que coinciden en el delimitador y rangos válidos para los campos de año, mes y día en el orden. Necesitas.

Estos son :-

Delimetros

[^\w\d\r\n:] 

Esto coincidirá con cualquier cosa que no sea un carácter de palabra, un carácter de dígito, un retorno de carro, una nueva línea o dos puntos.Los dos puntos deben estar allí para evitar coincidencias en horas que parecen fechas (consulte los datos de mi prueba)

Puede optimizar esta parte del patrón para acelerar la coincidencia, pero es una buena base para detectar la mayoría de los delimitadores válidos.

Tenga en cuenta sin embargo;Coincidirá con una cadena con delimitadores mixtos como este 12/2-73 que en realidad puede no ser una fecha válida.

Valores del año

(\d{4}|\d{2})

Esto coincide con un grupo de dos o 4 dígitos, en la mayoría de los casos esto es aceptable, pero si está tratando con datos de los años 0 a 999 o más allá de 9999, debe decidir cómo manejar eso porque en la mayoría de los casos un 1, 3. o >4 ​​dígitos del año es basura.

Valores mensuales

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

Coincide con cualquier número entre 1 y 12 con o sin un cero a la izquierda. Nota:0 y 00 no coinciden.

Valores de fecha

(0?[1-9]|[12]\d|30|31)

Coincide con cualquier número entre 1 y 31 con o sin un cero a la izquierda. Nota:0 y 00 no coinciden.

Esta expresión coincide con fechas formateadas como Fecha, Mes y Año.

(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})

Pero también coincidirá con algunos de los de Año y Fecha de Mes.También se debe reservar con los operadores de límite para garantizar que se seleccione toda la cadena de fecha y evitar que se extraigan subfechas válidas de datos que no están bien formados, es decir,sin etiquetas de límites 20/12/194 coincide con 20/12/19 y 101/12/1974 coincide con 01/12/1974

Compare los resultados de la siguiente expresión con la anterior con los datos de prueba en la sección sin sentido (a continuación)

\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b

No hay validación en esta expresión regular, por lo que se coincidiría con una fecha bien formada pero no válida, como 31/02/2001.Ese es un problema de calidad de los datos y, como han dicho otros, no debería ser necesario que su expresión regular valide los datos.

Debido a que usted (como desarrollador) no puede garantizar la calidad de los datos de origen, necesita realizar y manejar una validación adicional en su código, si intenta hacer coincidir y validar los datos en RegEx se vuelve muy complicado y resulta difícil de soportar sin muy documentación concisa.

Basura dentro basura fuera.

Dicho esto, si tiene formatos mixtos donde los valores de fecha varían y tiene que extraer tanto como pueda;Puedes combinar un par de expresiones de esta manera;

Esta expresión (desastrosa) coincide con las fechas DMY y YMD

(\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b)|(\b(0?[1-9]|1[0-2])[^\w\d\r\n:](0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](\d{4}|\d{2})\b)

PERO no podrás saber si fechas como el 9/6/1973 son el 6 de septiembre o el 9 de junio.Me cuesta pensar en un escenario en el que eso no vaya a causar un problema en algún momento, es una mala práctica y no deberías tener que lidiar con eso de esa manera: encuentra al propietario de los datos y golpéalo con el martillo de gobernanza. .

Finalmente, si desea hacer coincidir una cadena AAAAMMDD sin delimitadores, puede eliminar parte de la incertidumbre y la expresión se ve así

\b(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12]\d|30|31)\b

Pero tenga en cuenta nuevamente que coincidirá con valores bien formados pero no válidos como 20010231 (¡31 de febrero!) :)

Datos de prueba

Al experimentar con las soluciones en este hilo, terminé con un conjunto de datos de prueba que incluye una variedad de fechas válidas y no válidas y algunas situaciones difíciles en las que es posible que desee o no coincidir, es decir,Horas que podrían coincidir como fechas y fechas en varias líneas.

Espero que esto sea útil para alguien.

Valid Dates in various formats

Day, month, year
2/11/73
02/11/1973
2/1/73
02/01/73
31/1/1973
02/1/1973
31.1.2011
31-1-2001
29/2/1973
29/02/1976 
03/06/2010
12/6/90

month, day, year
02/24/1975 
06/19/66 
03.31.1991
2.29.2003
02-29-55
03-13-55
03-13-1955
12\24\1974
12\30\1974
1\31\1974
03/31/2001
01/21/2001
12/13/2001

Match both DMY and MDY
12/12/1978
6/6/78
06/6/1978
6/06/1978

using whitespace as a delimiter

13 11 2001
11 13 2001
11 13 01 
13 11 01
1 1 01
1 1 2001

Year Month Day order
76/02/02
1976/02/29
1976/2/13
76/09/31

YYYYMMDD sortable format
19741213
19750101

Valid dates before Epoch
12/1/10
12/01/660
12/01/00
12/01/0000

Valid date after 2038

01/01/2039
01/01/39

Valid date beyond the year 9999

01/01/10000

Dates with leading or trailing characters

12/31/21/
31/12/1921AD
31/12/1921.10:55
12/10/2016  8:26:00.39
wfuwdf12/11/74iuhwf
fwefew13/11/1974
01/12/1974vdwdfwe
01/01/99werwer
12321301/01/99

Times that look like dates

12:13:56
13:12:01
1:12:01PM
1:12:01 AM

Dates that runs across two lines

1/12/19
74

01/12/19
74/13/1946

31/12/20
08:13

Invalid, corrupted or nonsense dates

0/1/2001
1/0/2001
00/01/2100
01/0/2001
0101/2001
01/131/2001
31/31/2001
101/12/1974
56/56/56
00/00/0000
0/0/1999
12/01/0
12/10/-100
74/2/29
12/32/45
20/12/194

2/12-73

Versión mantenible de Perl 5.10

/
  (?:
      (?<month> (?&mon_29)) [\/] (?<day>(?&day_29))
    | (?<month> (?&mon_30)) [\/] (?<day>(?&day_30))
    | (?<month> (?&mon_31)) [\/] (?<day>(?&day_31))
  )
  [\/]
  (?<year> [0-9]{4})

  (?(DEFINE)
    (?<mon_29> 0?2 )
    (?<mon_30> 0?[469]   | (11) )
    (?<mon_31> 0?[13578] | 1[02] )

    (?<day_29> 0?[1-9] | [1-2]?[0-9] )
    (?<day_30> 0?[1-9] | [1-2]?[0-9] | 30 )
    (?<day_31> 0?[1-9] | [1-2]?[0-9] | 3[01] )
  )
/x

Puede recuperar los elementos por nombre en esta versión.

say "Month=$+{month} Day=$+{day} Year=$+{year}";

(No se ha intentado restringir los valores para el año.)

Para controlar la validez de una fecha bajo el siguiente formato:

AAAA/MM/DD o AAAA-MM-DD

Te recomendaría que utilices la siguiente expresión regular:

(((19|20)([2468][048]|[13579][26]|0[48])|2000)[/-]02[/-]29|((19|20)[0-9]{2}[/-](0[4678]|1[02])[/-](0[1-9]|[12][0-9]|30)|(19|20)[0-9]{2}[/-](0[1359]|11)[/-](0[1-9]|[12][0-9]|3[01])|(19|20)[0-9]{2}[/-]02[/-](0[1-9]|1[0-9]|2[0-8])))

Partidos

2016-02-29 | 2012-04-30 | 2019/09/31

No coincidencias

2016-02-30 | 2012-04-31 | 2019/09/35

Puede personalizarlo si desea permitir solo separadores '/' o '-'.Este RegEx controla estrictamente la validez de la fecha y verifica los meses de 28,30 y 31 días, incluso los años bisiestos con el mes 29/02.

Pruébelo, funciona muy bien y evita que su código tenga muchos errores.

Para su información:Hice una variante para la fecha y hora de SQL.Lo encontrarás allí (busca mi nombre): Expresión regular para validar una marca de tiempo.

Los comentarios son bienvenidos :)

Parece que estás extendiendo demasiado las expresiones regulares para este propósito.Lo que haría es usar una expresión regular para hacer coincidir algunos formatos de fecha y luego usar una función separada para validar los valores de los campos de fecha así extraídos.

Versión ampliada de Perl

Tenga en cuenta el uso de /x modificador.

/^(
      (
        ( # 31 day months
            (0[13578])
          | ([13578])
          | (1[02])
        )
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
          | (3[01])
        )
      )
    | (
        ( # 30 day months
            (0[469])
          | ([469])
          | (11)
        )
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
          | (30)
        )
      )
    | ( # 29 day month (Feb)
        (2|02)
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
        )
      )
    )
    [\/]
    # year
    \d{4}$

  | ^\d{4}$ # year only
/x

Original

^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$

Si las sugerencias anteriores no funcionaron, uso esto, ya que obtiene cualquier fecha. Ejecuté esta expresión a través de 50 enlaces y obtuvo todas las fechas en cada página.

^20\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(0[1-9]|[1-2][0-9]|3[01])$ 
    var dtRegex = new RegExp(/[1-9\-]{4}[0-9\-]{2}[0-9\-]{2}/);
    if(dtRegex.test(date) == true){
        var evalDate = date.split('-');
        if(evalDate[0] != '0000' && evalDate[1] != '00' && evalDate[2] != '00'){
            return true;
        }
    }

Esta expresión regular valida fechas entre el 01-01-2000 y el 31-12-2099 con separadores coincidentes.

^(0[1-9]|1[012])([- /.])(0[1-9]|[12][0-9]|3[01])\2(19|20)\d\d$

Regex no estaba destinado a validar rangos de números (este número debe ser del 1 al 5 cuando el número que lo precede es un 2 y el número que lo precede es inferior a 6).Simplemente busque el patrón de colocación de números en expresiones regulares.Si necesita validar las cualidades de una fecha, colóquela en un objeto de fecha js/c#/vb e interrogue los números allí.

Sé que esto no responde a tu pregunta, pero ¿por qué no utilizas una rutina de manejo de fechas para verificar si es una fecha válida?Incluso si modifica la expresión regular con una afirmación de anticipación negativa como (?!31/0?2) (es decir, no coincide con 31/2 o 31/02), seguirá teniendo el problema de aceptar 29 02 en años no bisiestos. y sobre un formato de fecha con separador único.

El problema no es fácil si realmente quieres validar una fecha, mira esto hilo del foro.

Para ver un ejemplo o una forma mejor, en C#, consulte este enlace

Si estás utilizando otra plataforma/idioma, háznoslo saber

versión Perl 6

Después de usar esto para verificar la entrada, los valores están disponibles en $/ o individualmente como $<month>, $<day>, $<year>.(esas son solo sintaxis para acceder a valores en $/ )

No se ha intentado comprobar el año, ni que no coincida con el 29 de febrero en años no bisiestos.

Si vas a insistir en hacer esto con una expresión regular, te recomiendo algo como:

( (0?1|0?3| <...> |10|11|12) / (0?1| <...> |30|31) |
  0?2 / (0?1| <...> |28|29) ) 
/ (19|20)[0-9]{2}

Este podría hacer posible leer y comprender.

Un enfoque ligeramente diferente que puede resultarle útil o no.

Estoy en php.

El proyecto al que nos referimos nunca tendrá una fecha anterior al 1 de enero de 2008.Entonces, tomo la 'fecha' ingresada y uso strtotime().Si la respuesta es >= 1199167200 entonces tengo una fecha que me resulta útil.Si se ingresa algo que no parece una fecha, se devuelve -1.Si se ingresa nulo, devuelve el número de fecha de hoy, por lo que primero necesita verificar una entrada que no sea nula.

Funciona para mi situación, ¿quizás la tuya también?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top