Question

J'essaie d'écrire une expression régulière qui valide une date.L'expression régulière doit correspondre à ce qui suit

  • M/J/AAAA
  • MM/JJ/AAAA
  • Les mois à un chiffre peuvent commencer par un zéro non significatif (par exemple :03/12/2008)
  • Les jours à un chiffre peuvent commencer par un zéro non significatif (par exemple :3/02/2008)
  • NE PEUT PAS inclure le 30 février ou le 31 février (par exemple :31/02/2008)

Jusqu'à présent, j'ai

^(([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)$

Cela correspond correctement SAUF qu'il inclut toujours le 30/02/2008 et le 31/02/2008.

Quelqu'un a-t-il une meilleure suggestion ?

Modifier: j'ai trouvé la réponse sur 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}$

Il correspond à tous les mois valides qui suivent le format MM/JJ/AAAA.

Merci à tous pour l'aide.

Était-ce utile?

La solution

Ce n'est pas une utilisation appropriée des expressions régulières.Tu ferais mieux d'utiliser

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

puis vérifier les plages dans un langage de niveau supérieur.

Autres conseils

Voici le Reg ex qui correspond à toutes les dates valides, y compris les années bissextiles.Formats acceptés format mm/jj/aaaa ou mm-jj-aaaa ou mm.jj.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})$

courtoisie Asiq Ahamed

J'ai atterri ici parce que le titre de cette question est large et je cherchais une expression régulière que je pourrais utiliser pour correspondre à un format de date spécifique (comme l'OP).Mais j'ai ensuite découvert, comme de nombreuses réponses et commentaires l'ont largement souligné, qu'il existe de nombreux pièges qui rendent la construction d'un modèle efficace très délicate lors de l'extraction de dates mélangées à des données sources de mauvaise qualité ou non structurées.

Dans mon exploration des problèmes, j'ai mis au point un système qui vous permet de créer une expression régulière en organisant ensemble quatre sous-expressions plus simples qui correspondent sur le délimiteur et des plages valides pour les champs année, mois et jour dans l'ordre vous avez besoin.

Ceux-ci sont :-

Délimiteurs

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

Cela correspondra à tout ce qui n'est pas un caractère de mot, un caractère numérique, un retour chariot, une nouvelle ligne ou deux points.Les deux points doivent être là pour empêcher la correspondance avec des heures qui ressemblent à des dates (voir mes données de test)

Vous pouvez optimiser cette partie du modèle pour accélérer la correspondance, mais il s'agit d'une bonne base qui détecte la plupart des délimiteurs valides.

Notez cependant ;Il correspondra à une chaîne avec des délimiteurs mixtes comme celui-ci 2/12-73 qui peut ne pas être en réalité une date valide.

Valeurs de l'année

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

Cela correspond à un groupe de deux ou 4 chiffres, dans la plupart des cas cela est acceptable, mais si vous traitez des données des années 0 à 999 ou au-delà de 9999, vous devez décider comment gérer cela car dans la plupart des cas, un 1, 3 ou une année > à 4 chiffres est une poubelle.

Valeurs mensuelles

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

Correspond à n'importe quel nombre compris entre 1 et 12, avec ou sans zéro non significatif. Remarque :0 et 00 ne correspondent pas.

Valeurs de dates

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

Correspond à n'importe quel nombre compris entre 1 et 31 avec ou sans zéro non significatif. Remarque :0 et 00 ne correspondent pas.

Cette expression correspond aux dates au format Date, Mois et Année.

(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})

Mais il correspondra également à certains de ceux de l’année, du mois et de la date.Il doit également être réservé avec les opérateurs de limite pour garantir que la chaîne de date entière est sélectionnée et empêcher que des sous-dates valides soient extraites de données qui ne sont pas bien formées, c'est-à-diresans balises de délimitation, le 20/12/194 correspond au 20/12/19 et le 101/12/1974 correspond au 01/12/1974

Comparez les résultats de l'expression suivante à celle ci-dessus avec les données de test dans la section absurde (ci-dessous)

\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

Il n'y a aucune validation dans cette expression régulière, donc une date bien formée mais invalide telle que le 31/02/2001 serait mise en correspondance.C'est un problème de qualité des données et, comme d'autres l'ont dit, votre expression régulière ne devrait pas avoir besoin de valider les données.

Parce que vous (en tant que développeur) ne pouvez pas garantir la qualité des données sources dont vous avez besoin pour effectuer et gérer une validation supplémentaire dans votre code, si vous essayez de faire correspondre et valider les données dans le RegEx, cela devient très compliqué et devient difficile à prendre en charge sans très une documentation concise.

Déchets entrants, déchets sortants.

Cela dit, si vous avez des formats mixtes dans lesquels les valeurs de date varient et que vous devez en extraire autant que possible ;Vous pouvez combiner quelques expressions ensemble comme ceci ;

Cette expression (désastreuse) correspond aux dates DMY et 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)

MAIS vous ne pourrez pas dire si des dates comme le 9/06/1973 sont le 6 septembre ou le 9 juin.J'ai du mal à penser à un scénario dans lequel cela ne posera pas de problème à un moment donné, c'est une mauvaise pratique et vous ne devriez pas avoir à gérer cela comme ça - trouvez le propriétaire des données et frappez-le avec le marteau de gouvernance. .

Enfin, si vous souhaitez faire correspondre une chaîne AAAAMMJJ sans délimiteurs, vous pouvez éliminer une partie de l'incertitude et l'expression ressemble à ceci

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

Mais notez encore une fois, cela correspondra à des valeurs bien formées mais invalides comme 20010231 (31 février !) :)

Données de test

En expérimentant les solutions de ce fil, je me suis retrouvé avec un ensemble de données de test qui comprend une variété de dates valides et non valides et des situations délicates dans lesquelles vous pouvez ou non vouloir faire correspondre, par exempleHeures pouvant correspondre à des dates et à des dates sur plusieurs lignes.

J'espère que cela sera utile à quelqu'un.

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

Version Perl 5.10 maintenable

/
  (?:
      (?<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

Vous pouvez récupérer les éléments par nom dans cette version.

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

( Aucune tentative n'a été faite pour restreindre les valeurs pour l'année.)

Pour contrôler une validité de date sous le format suivant :

AAAA/MM/JJ ou AAAA-MM-JJ

Je vous recommande d'utiliser l'expression régulière suivante :

(((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])))

Allumettes

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

Non-correspondances

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

Vous pouvez le personnaliser si vous souhaitez autoriser uniquement les séparateurs « / » ou « - ».Ce RegEx contrôle strictement la validité de la date et vérifie les mois de 28,30 et 31 jours, même les années bissextiles avec le 29/02 mois.

Essayez-le, cela fonctionne très bien et évitez à votre code beaucoup de bugs !

POUR VOTRE INFORMATION :J'ai fait une variante pour le datetime SQL.Vous le trouverez là (cherchez mon nom) : Expression régulière pour valider un horodatage

Les commentaires sont les bienvenus :)

On dirait que vous étendez trop les expressions régulières à cette fin.Ce que je ferais, c'est utiliser une expression régulière pour faire correspondre quelques formats de date, puis utiliser une fonction distincte pour valider les valeurs des champs de date ainsi extraits.

version étendue de Perl

Notez l'utilisation de /x modificateur.

/^(
      (
        ( # 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 vous n'avez pas fait fonctionner les suggestions ci-dessus, j'utilise ceci, car il obtient n'importe quelle date, j'ai exécuté cette expression via 50 liens, et elle a obtenu toutes les dates sur chaque page.

^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;
        }
    }

Cette expression régulière valide les dates comprises entre le 01-01-2000 et le 31-12-2099 avec les séparateurs correspondants.

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

Regex n'était pas destiné à valider des plages de nombres (ce nombre doit être compris entre 1 et 5 lorsque le nombre qui le précède se trouve être un 2 et que le nombre qui le précède se trouve être inférieur à 6).Recherchez simplement le modèle de placement des nombres dans l'expression régulière.Si vous avez besoin de valider les qualités d'une date, placez-la dans un objet date js/c#/vb et interrogez-y les chiffres.

Je sais que cela ne répond pas à votre question, mais pourquoi n'utilisez-vous pas une routine de gestion des dates pour vérifier s'il s'agit d'une date valide ?Même si vous modifiez l'expression rationnelle avec une assertion d'anticipation négative comme (?!31/0?2) (c'est-à-dire ne correspond pas au 31/2 ou au 31/02), vous aurez toujours le problème d'accepter 29 02 pour les années non bissextiles. et environ un format de date à séparateur unique.

Le problème n'est pas simple si vous voulez vraiment valider une date, cochez ceci fil de discussion.

Pour un exemple ou une meilleure façon, en C#, cochez ce lien

Si vous utilisez une autre plateforme/langue, faites-le-nous savoir

Version Perl6

Après avoir utilisé ceci pour vérifier l'entrée, les valeurs sont disponibles dans $/ ou individuellement comme $<month>, $<day>, $<year>.(ce ne sont que des syntaxes pour accéder aux valeurs dans $/ )

Aucune tentative n'a été faite pour vérifier l'année ou qu'elle ne corresponde pas au 29 février pour les années non bissextiles.

Si vous insistez pour faire cela avec une expression régulière, je recommanderais quelque chose comme :

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

Ce pourrait permettre de lire et de comprendre.

Une approche légèrement différente qui peut vous être utile ou non.

Je suis en php.

Le projet concerné n’aura jamais de date antérieure au 1er janvier 2008.Donc, je prends la « date » saisie et j’utilise strtotime().Si la réponse est >= 1199167200 alors j'ai une date qui m'est utile.Si quelque chose qui ne ressemble pas à une date est saisi, -1 est renvoyé.Si null est entré, il renvoie le numéro de date du jour, vous devez donc d'abord vérifier une entrée non nulle.

Fonctionne pour ma situation, peut-être la vôtre aussi ?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top