Domanda

Sto cercando di scrivere un'espressione regolare che convalidi una data.La regex deve corrispondere a quanto segue

  • M/G/AAAA
  • MM/GG/AAAA
  • I mesi a una cifra possono iniziare con uno zero iniziale (ad esempio:03/12/2008)
  • I giorni a una cifra possono iniziare con uno zero iniziale (ad esempio:3/02/2008)
  • NON PUÒ includere il 30 o il 31 febbraio (ad esempio:31/02/2008)

Finora l'ho fatto

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

Questo corrisponde correttamente TRANNE che include ancora il 30/02/2008 e il 31/02/2008.

Qualcuno ha un suggerimento migliore?

Modificare: ho trovato la risposta su 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}$

Corrisponde a tutti i mesi validi che seguono il formato MM/GG/AAAA.

Grazie a tutti per l'aiuto.

È stato utile?

Soluzione

Questo non è un uso appropriato delle espressioni regolari.Faresti meglio a usarlo

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

e quindi controllare gli intervalli in un linguaggio di livello superiore.

Altri suggerimenti

Ecco il Reg ex che corrisponde a tutte le date valide compresi gli anni bisestili.Formati accettati formato mm/gg/aaaa oppure mm-gg-aaaa oppure mm.gg.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})$

cortesia Asiq Ahamed

Sono arrivato qui perché il titolo di questa domanda è ampio e stavo cercando un'espressione regolare che potessi utilizzare per abbinare un formato di data specifico (come l'OP).Ma poi ho scoperto, come molte risposte e commenti hanno evidenziato in modo esauriente, ci sono molte insidie ​​​​che rendono molto complicata la costruzione di un modello efficace quando si estraggono date mescolate con dati di origine di scarsa qualità o non strutturati.

Nella mia esplorazione dei problemi, ho ideato un sistema che ti consente di creare un'espressione regolare organizzando insieme quattro sottoespressioni più semplici che corrispondono al delimitatore e intervalli validi per i campi anno, mese e giorno nell'ordine hai bisogno.

Questi sono :-

Delimitatori

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

Ciò corrisponderà a tutto ciò che non è un carattere di parola, un carattere di cifra, un ritorno a capo, una nuova riga o due punti.I due punti devono essere presenti per impedire la corrispondenza in orari che sembrano date (vedi i miei dati di test)

Puoi ottimizzare questa parte del modello per accelerare la corrispondenza, ma questa è una buona base per rilevare i delimitatori più validi.

Nota tuttavia;Corrisponderà a una stringa con delimitatori misti come questa 2/12-73 che potrebbe non essere effettivamente una data valida.

Valori dell'anno

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

Corrisponde a un gruppo di due o 4 cifre, nella maggior parte dei casi è accettabile, ma se hai a che fare con dati dagli anni 0-999 o oltre 9999 devi decidere come gestirli perché nella maggior parte dei casi un 1, 3 o un anno >4 cifre è spazzatura.

Valori mensili

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

Corrisponde a qualsiasi numero compreso tra 1 e 12 con o senza zero iniziale - nota:0 e 00 non corrispondono.

Valori della data

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

Corrisponde a qualsiasi numero compreso tra 1 e 31 con o senza zero iniziale - nota:0 e 00 non corrispondono.

Questa espressione corrisponde alle date formattate per data, mese e anno

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

Ma corrisponderà anche ad alcuni di quelli relativi all'anno e alla data del mese.Dovrebbe inoltre essere bloccato con gli operatori di confine per garantire che l'intera stringa di data sia selezionata e impedire che vengano estratte date secondarie valide da dati che non sono ben formati, ad es.senza etichette di confine il 20/12/194 corrisponde al 20/12/19 e il 101/12/1974 corrisponde al 01/12/1974

Confronta i risultati dell'espressione successiva con quella sopra con i dati del test nella sezione senza senso (sotto)

\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

Non c'è convalida in questa espressione regolare, quindi verrebbe abbinata una data ben formata ma non valida come 31/02/2001.Questo è un problema di qualità dei dati e, come altri hanno già detto, la tua espressione regolare non dovrebbe aver bisogno di convalidare i dati.

Poiché tu (come sviluppatore) non puoi garantire la qualità dei dati di origine di cui hai bisogno per eseguire e gestire un'ulteriore convalida nel tuo codice, se provi ad abbinare E convalidare i dati nella RegEx diventa molto complicato e diventa difficile supportarli senza molto documentazione sintetica.

Immondizia dentro, spazzatura fuori.

Detto questo, se disponi di formati misti in cui i valori delle date variano e devi estrarre il più possibile;Puoi combinare un paio di espressioni insieme in questo modo;

Questa (disastrosa) espressione corrisponde alle date DMY e 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)

MA non sarai in grado di dire se date come 6/9/1973 sono il 6 settembre o il 9 giugno.Faccio fatica a pensare a uno scenario in cui ciò non causerà problemi in futuro, è una cattiva pratica e non dovresti affrontarla in quel modo: trova il proprietario dei dati e colpiscilo con il martello della governance .

Infine, se vuoi far corrispondere una stringa AAAAMMGG senza delimitatori puoi eliminare parte dell'incertezza e l'espressione apparirà così

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

Ma nota ancora, corrisponderà a valori ben formati ma non validi come 20010231 (31 febbraio!) :)

Dati di test

Sperimentando le soluzioni in questo thread mi sono ritrovato con un set di dati di test che include una varietà di date valide e non valide e alcune situazioni difficili in cui potresti o meno voler corrispondere, ad es.Orari che potrebbero corrispondere come date e date su più righe.

Spero che questo sia utile a qualcuno.

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

Versione Perl 5.10 manutenibile

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

In questa versione è possibile recuperare gli elementi per nome.

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

(Non è stato fatto alcun tentativo di limitare i valori per l'anno.)

Per controllare la validità di una data nel seguente formato:

AAAA/MM/GG o AAAA-MM-GG

Ti consiglierei di utilizzare la seguente espressione regolare:

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

Partite

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

Non corrispondenze

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

Puoi personalizzarlo se desideri consentire solo i separatori "/" o "-".Questa RegEx controlla rigorosamente la validità della data e verifica i mesi di 28,30 e 31 giorni, anche gli anni bisestili con il mese 29/02.

Provalo, funziona molto bene e previene molti bug nel tuo codice!

PER TUA INFORMAZIONE :Ho creato una variante per il datetime SQL.Lo troverai lì (cerca il mio nome): Espressione regolare per convalidare un timestamp

I feedback sono benvenuti :)

Sembra che tu stia estendendo eccessivamente le espressioni regolari per questo scopo.Quello che farei è utilizzare una regex per abbinare alcuni formati di data e quindi utilizzare una funzione separata per convalidare i valori dei campi data così estratti.

Versione espansa di Perl

Nota l'uso di /x modificatore.

/^(
      (
        ( # 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

Originale

^((((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}$

se non hai funzionato con i suggerimenti di cui sopra, lo uso, poiché ottiene qualsiasi data, ho eseguito questa espressione attraverso 50 collegamenti e ha ottenuto tutte le date su ogni pagina.

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

Questa regex convalida le date comprese tra 01-01-2000 e 12-31-2099 con separatori corrispondenti.

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

Regex non è stato pensato per convalidare intervalli di numeri (questo numero deve essere compreso tra 1 e 5 quando il numero che lo precede sembra essere un 2 e il numero che lo precede è inferiore a 6).Cerca semplicemente lo schema di posizionamento dei numeri nella regex.Se è necessario convalidare le qualità di una data, inserirla in un oggetto data js/c#/vb e interrogare i numeri lì.

So che questo non risponde alla tua domanda, ma perché non usi una routine di gestione della data per verificare se è una data valida?Anche se modifichi l'espressione regolare con un'asserzione lookahead negativa come (?!31/0?2) (ovvero, non corrisponde a 31/2 o 31/02) avrai comunque il problema di accettare 29 02 negli anni non bisestili e su un formato di data con separatore singolo.

Il problema non è semplice se vuoi davvero convalidare una data, controlla questo discussione del forum.

Per un esempio o un modo migliore, in C#, controlla questo link

Se utilizzi un'altra piattaforma/lingua, faccelo sapere

Versione Perl6

Dopo averlo utilizzato per verificare l'input in cui sono disponibili i valori $/ o individualmente come $<month>, $<day>, $<year>.(quelle sono solo la sintassi per accedere ai valori in $/ )

Non è stato fatto alcun tentativo per controllare l'anno o che non corrisponda al 29 febbraio negli anni non bisestili.

Se hai intenzione di insistere per farlo con un'espressione regolare, ti consiglio qualcosa come:

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

Questo Potrebbe permettono di leggere e comprendere.

Un approccio leggermente diverso che potrebbe essere utile o meno per te.

Sono in php.

Il progetto a cui si riferisce non avrà mai una data anteriore al 1 gennaio 2008.Quindi, prendo la "data" immessa e utilizzo strtotime().Se la risposta è >= 1199167200 allora ho una data che mi è utile.Se viene inserito qualcosa che non assomiglia a una data, viene restituito -1.Se viene inserito null, restituisce il numero della data odierna, quindi è necessario prima controllare una voce non nulla.

Funziona per la mia situazione, forse anche per la tua?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top