2014-05-06 12 views
8

Il mio programma sta analizzando una stringa di input su un oggetto LocalDate. Per la maggior parte del tempo la stringa assomiglia a 30.03.2014, ma a volte sembra che sia 3/30/2014. A seconda di quale, ho bisogno di utilizzare un modello diverso per chiamare DateTimeFormatter.ofPattern(String pattern) con. Fondamentalmente, ho bisogno di verificare se la stringa corrisponde al modello dd.MM.yyyy o M/dd/yyyy prima di eseguire l'analisi.Come verificare se la stringa corrisponde al modello di data utilizzando l'API del tempo?

L'approccio regex sarebbe qualcosa di simile:

LocalDate date; 
if (dateString.matches("^\\d?\\d/\\d{2}/\\d{4}$")) { 
    date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("M/dd/yyyy")); 
} else { 
    date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("dd.MM.yyyy")); 
} 

Questo funziona, ma sarebbe bello usare la stringa della data modello quando corrisponde alla stringa anche.

Esistono modi standard per eseguire questa operazione con la nuova API Java 8 time, senza ricorrere alla corrispondenza delle espressioni regolari? Ho esaminato i documenti per DateTimeFormatter ma non sono riuscito a trovare nulla.

+0

Perché non stringa 'replace (" // ",". ")'? –

+0

Non penso che esista un metodo specifico per verificare se una data segue un dato pattern. Vorrei utilizzare il metodo SimpleDateFormat.parse e verificare una ParseException. Se sei sicuro che la data sia corretta ed è in uno dei due modelli, puoi semplicemente controllare se c'è "." nella stringa. – maxvv

+2

@RobertNiestroj Perché mese e giorno non sono posizionati nello stesso posto in entrambi i modelli. –

risposta

8

Ok, sto andando avanti e pubblicandolo come risposta. Un modo è creare la classe che terrà i modelli.

public class Test { 
    public static void main(String[] args){ 
     MyFormatter format = new MyFormatter("dd.MM.yyyy", "M/dd/yyyy"); 
     LocalDate date = format.parse("3/30/2014"); //2014-03-30 
     LocalDate date2 = format.parse("30.03.2014"); //2014-03-30 
    } 
} 

class MyFormatter { 
    private final String[] patterns; 

    public MyFormatter(String... patterns){ 
     this.patterns = patterns; 
    } 

    public LocalDate parse(String text){ 
     for(int i = 0; i < patterns.length; i++){ 
      try{ 
       return LocalDate.parse(text, DateTimeFormatter.ofPattern(patterns[i])); 
      }catch(DateTimeParseException excep){} 
     } 
     throw new IllegalArgumentException("Not able to parse the date for all patterns given"); 
    } 
} 

Si potrebbe migliorare questo come ha fatto con la creazione di @MenoHochschild direttamente una serie di DateTimeFormatter dalla matrice di String si passa nel costruttore.


Un altro modo sarebbe quello di utilizzare un DateTimeFormatterBuilder, aggiungendo i formati desiderati. Potrebbero esserci altri modi per farlo, non ho approfondito la documentazione :-)

DateTimeFormatter dfs = new DateTimeFormatterBuilder() 
          .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd"))                 
          .appendOptional(DateTimeFormatter.ofPattern("dd.MM.yyyy"))                      
          .toFormatter(); 
LocalDate d = LocalDate.parse("2014-05-14", dfs); //2014-05-14 
LocalDate d2 = LocalDate.parse("14.05.2014", dfs); //2014-05-14 
0

L'approccio di @ZouZou è una possibile soluzione.

Al fine di evitare l'uso di eccezioni per la logica del programma, per quanto possibile (anche non così bello performancewise) seguente alternative potrebbe essere considerato:

static final String[] PATTERNS = {"dd.MM.yyyy", "M/dd/yyyy"}; 
static final DateTimeFormatter[] FORMATTERS = new DateTimeFormatter[PATTERNS.length]; 

static { 
    for (int i = 0; i < PATTERNS.length; i++) { 
    FORMATTERS[i] = DateTimeFormatter.ofPattern(PATTERNS[i]); 
    } 
} 

public static LocalDate parse(String input) { 
    ParsePosition pos = new ParsePosition(); 
    for (int i = 0; i < patterns.length; i++) { 
    try { 
     TemporalAccessor tacc = FORMATTERS[i].parseUnresolved(input, pos); 
     if (pos.getErrorIndex < 0) { 
     return LocalDate.from(tacc); // possibly throwing DateTimeException => validation failure 
     } 
    } catch (DateTimeException ex) { // catches also possible DateTimeParseException 
     // go to next pattern 
    } 
    pos.setIndex(0); 
    pos.setErrorIndex(-1); 
    } 
    throw new IllegalArgumentException("Input does not match any pattern: " + input); 
} 

Maggiori spiegazioni sul metodo parseUnresolved():

Questo metodo fa solo la prima fase di analisi, quindi non esiste una seconda fase contenente la convalida preliminare o la combinazione dello sforzo dei campi analizzati. Tuttavia, LocalDate.from() convalida ogni input, quindi penso che sia ancora sufficiente. E il vantaggio è che parseUnresolved() utilizza l'indice di errore di ParsePosition. Ciò è in accordo con il comportamento tradizionale java.text.Format.

Purtroppo il metodo alternativo e più intuitivo DateTimeFormater.parse() primo crea un DateTimeParseException e quindi memorizzare l'indice di errore in questa eccezione. Così ho deciso di non utilizzare questo metodo per evitare la creazione di un'eccezione non necessaria. Per me, questo dettaglio API è una decisione progettuale discutibile.