2010-03-03 14 views
57

Ho un appuntamento nel seguente formato: 2010-03-01T00:00:00-08:00Java SimpleDateFormat per il fuso orario con un separatore di due punti?

ho buttato i seguenti SimpleDateFormats a esso di analizzarlo:

private static final SimpleDateFormat[] FORMATS = { 
     new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"), //ISO8601 long RFC822 zone 
     new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"), //ISO8601 long long form zone 
     new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"), //ignore timezone 
     new SimpleDateFormat("yyyyMMddHHmmssZ"), //ISO8601 short 
     new SimpleDateFormat("yyyyMMddHHmm"), 
     new SimpleDateFormat("yyyyMMdd"), //birthdate from NIST IHE C32 sample 
     new SimpleDateFormat("yyyyMM"), 
     new SimpleDateFormat("yyyy") //just the year 
    }; 

Ho un metodo comodo che utilizza questi formati in questo modo:

public static Date figureOutTheDamnDate(String wtf) { 
    if (wtf == null) { 
     return null; 
    } 
    Date retval = null; 
    for (SimpleDateFormat sdf : FORMATS) { 
     try { 
      sdf.setLenient(false) 
      retval = sdf.parse(wtf); 
      System.out.println("Date:" + wtf + " hit on pattern:" + sdf.toPattern()); 
      break; 
     } catch (ParseException ex) { 
      retval = null; 
      continue; 
     } 
    } 

    return retval; 
} 

Sembra colpire sul modello yyyyMMddHHmm ma restituisce la data come Thu Dec 03 00:01:00 PST 2009.

Qual è lo schema corretto per analizzare questa data?

AGGIORNAMENTO: Non è necessario l'analisi del fuso orario. Non prevedo che i problemi relativi al fattore tempo si spostino tra le zone, ma come posso ottenere il formato di zona "-08: 00" per analizzare ????

prova Unità:

@Test 
public void test_date_parser() { 
    System.out.println("\ntest_date_parser"); 
    //month is zero based, are you effing kidding me 
    Calendar d = new GregorianCalendar(2000, 3, 6, 13, 00, 00); 
    assertEquals(d.getTime(), MyClass.figureOutTheDamnDate("200004061300")); 
    assertEquals(new GregorianCalendar(1950, 0, 1).getTime(), MyClass.figureOutTheDamnDate("1950")); 
    assertEquals(new GregorianCalendar(1997, 0, 1).getTime(), MyClass.figureOutTheDamnDate("199701")); 
    assertEquals(new GregorianCalendar(2010, 1, 25, 15, 19, 44).getTime(), MyClass.figureOutTheDamnDate("20100225151944-0800")); 

    //my machine happens to be in GMT-0800 
    assertEquals(new GregorianCalendar(2010, 1, 15, 13, 15, 00).getTime(),MyClass.figureOutTheDamnDate("2010-02-15T13:15:00-05:00")); 
    assertEquals(new GregorianCalendar(2010, 1, 15, 18, 15, 00).getTime(), MyClass.figureOutTheDamnDate("2010-02-15T18:15:00-05:00")); 

    assertEquals(new GregorianCalendar(2010, 2, 1).getTime(), MyClass.figureOutTheDamnDate("2010-03-01T00:00:00-08:00")); 
    assertEquals(new GregorianCalendar(2010, 2, 1, 17, 0, 0).getTime(), MyClass.figureOutTheDamnDate("2010-03-01T17:00:00-05:00")); 
} 

uscita dal test di unità:

test_date_parser 
Date:200004061300 hit on pattern:yyyyMMddHHmm 
Date:1950 hit on pattern:yyyy 
Date:199701 hit on pattern:yyyyMM 
Date:20100225151944-0800 hit on pattern:yyyyMMddHHmmssZ 
Date:2010-02-15T13:15:00-05:00 hit on pattern:yyyy-MM-dd'T'HH:mm:ss 
Date:2010-02-15T18:15:00-05:00 hit on pattern:yyyy-MM-dd'T'HH:mm:ss 
Date:2010-03-01T00:00:00-08:00 hit on pattern:yyyy-MM-dd'T'HH:mm:ss 
Date:2010-03-01T17:00:00-05:00 hit on pattern:yyyy-MM-dd'T'HH:mm:ss 
+1

Volevo solo attira la tua attenzione sul fatto che JDK 'SimpleDateFormat' non è thread-safe. Pre-instanciating 'SimpleDateFormat' oggetti è un anti-pattern quando viene mantenuto in un campo statico ed eventualmente esposto a più thread. Solo i modelli stessi possono essere una costante. – mwhs

+0

@mwhs Molto vero! Per ulteriori informazioni (e una soluzione semplice), fare riferimento al post sul mio blog su questo stesso argomento: [Come i formati di testo di Java possono sottilmente infrangere il codice] (http://stijndewitt.wordpress.com/2014/07/28/how-javas -text-formats-can-subtly-break-your-code /) –

risposta

54

JodaTime s' DateTimeFormat di salvataggio:

String dateString = "2010-03-01T00:00:00-08:00"; 
String pattern = "yyyy-MM-dd'T'HH:mm:ssZ"; 
DateTimeFormatter dtf = DateTimeFormat.forPattern(pattern); 
DateTime dateTime = dtf.parseDateTime(dateString); 
System.out.println(dateTime); // 2010-03-01T04:00:00.000-04:00 

(tempo e differenza di fuso orario in toString() è solo perché sono in GMT-4 e non ho impostato locale esplicitamente)

Se si desidera per finire con java.util.Date basta usare DateTime#toDate():

Date date = dateTime.toDate(); 

Attendere JDK7 ( JSR-310) JSR-310, l'implementazione del referrence è denominata ThreeTen (si spera che diventi Java 8) se si desidera un formattatore migliore nell'API Java SE standard. L'attuale SimpleDateFormat non mangia i due punti nella notazione del fuso orario.

Aggiornamento: come per l'aggiornamento, apparentemente non è necessario il fuso orario. Questo dovrebbe funzionare con SimpleDateFormat. Basta ometterlo (lo Z) nel pattern.

String dateString = "2010-03-01T00:00:00-08:00"; 
String pattern = "yyyy-MM-dd'T'HH:mm:ss"; 
SimpleDateFormat sdf = new SimpleDateFormat(pattern); 
Date date = sdf.parse(dateString); 
System.out.println(date); // Mon Mar 01 00:00:00 BOT 2010 

(che è corretto come per il mio fuso orario)

+2

Uomo, se potessi aggiornare a JDK7 sarei in paradiso. Ci sono altre cose in 7 che voglio. Vedrò se l'analisi delle zone è un requisito o meno. Continuo a sentire cose buone su Joda e probabilmente dovrei provarlo. – Freiheit

+0

Provalo. Ne vale la pena. Soprattutto se si vuole fare un po 'di più con date/orari rispetto alla memorizzazione, come l'analisi, la formattazione, la modifica, il calcolo, ecc. – BalusC

+1

@BalusC Ho avuto lo stesso problema di Freiheit e l'omissione del fuso orario ha fatto il trucco. Quindi 'SimpleDateFormat' usa il modello come 'inizia con' invece di 'uguale' (quindi qualsiasi altra cosa scritta nella stringa verrà ignorata)? – RaphaelDDL

1

Prova setLenient(false).

Addendum: Sembra che tu stia riconoscendo le stringhe Date variamente formattate. Se si deve fare la voce, si potrebbe voler guardare questo example che si estende InputVerifier.

+0

Hrmm. Più vicino, almeno ha senso per i modelli su cui colpisce. Modifica il post in un momento per riflettere questi cambiamenti. – Freiheit

+0

Elaborazione di dati XML. Spec afferma che è * supposto * usare ISO8601 per ogni campo data, ma questo non è che cosa sta arrivando attraverso il filo. Il resto del sistema è il miglior sforzo, quindi ho bisogno di ottenere qualsiasi formato ragionevole da analizzare. – Freiheit

+0

@Freiheit: altro modo 'qui intorno. '-08: 00' è il modo corretto per ISO8601, ma Java 6 non ha nulla che analizzi correttamente ISO8601. –

29

Ecco un frammento che ho usato - con la pianura SimpleDateFormat.Spero che qualcun altro possa trarre beneficio da esso:

public static void main(String[] args) { 
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") { 
     public StringBuffer format(Date date, StringBuffer toAppendTo, java.text.FieldPosition pos) { 
      StringBuffer toFix = super.format(date, toAppendTo, pos); 
      return toFix.insert(toFix.length()-2, ':'); 
     }; 
    }; 
    // Usage: 
    System.out.println(dateFormat.format(new Date())); 
} 

uscita:

- Usual Output.........: 2013-06-14T10:54:07-0200 
- This snippet's Output: 2013-06-14T10:54:07-02:00 
+0

Funziona alla grande! Grazie! Molto meglio dell'utilizzo di librerie di terze parti! – Graeme

+0

Grazie. Funziona alla grande. Recentemente si è imbattuto in questo problema a supporto di un'app legacy su Java 1.6. – acveer

+0

questo è quello che sto cercando –

4

Grazie acdcjunior per la vostra soluzione. Ecco una versione poco ottimizzata per la formattazione e l'analisi:

public static final SimpleDateFormat XML_SDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.FRANCE) 
{ 
    private static final long serialVersionUID = -8275126788734707527L; 

    public StringBuffer format(Date date, StringBuffer toAppendTo, java.text.FieldPosition pos) 
    {    
     final StringBuffer buf = super.format(date, toAppendTo, pos); 
     buf.insert(buf.length() - 2, ':'); 
     return buf; 
    }; 

    public Date parse(String source) throws java.text.ParseException { 
     final int split = source.length() - 2; 
     return super.parse(source.substring(0, split - 1) + source.substring(split)); // replace ":" du TimeZone 
    }; 
}; 
+1

Solo una nota, si potrebbe voler aggiungere una condizione su 'parse' in modo tale che se' source' è null o meno di 3 caratteri si fa qualcos'altro che si provi a usare 'substring' su un negativo indice ... –

11

Prova questa, il suo lavoro per me:

Date date = javax.xml.bind.DatatypeConverter.parseDateTime("2013-06-01T12:45:01+04:00").getTime(); 

In Java 8:

OffsetDateTime dt = OffsetDateTime.parse("2010-03-01T00:00:00-08:00"); 
+0

Funziona molto bene. Grazie –

+0

Manca da Android :( –

+0

@MooingDuck Gran parte della funzionalità java.time è back-porting su Java 6 e 7 nel progetto [ThreeTen-Backport] (http://www.threeten.org/threetenbp/). ulteriormente ** adattato ad Android ** nel progetto [ThreeTenABP] (https://github.com/JakeWharton/ThreeTenABP). –

26

se è stato utilizzato il Java 7, avresti potuto usare il seguente pattern Date Time. Sembra che questo modello non sia supportato nella versione precedente di java.

String dateTimeString = "2010-03-01T00:00:00-08:00"; 
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); 
Date date = df.parse(dateTimeString); 

Per ulteriori informazioni, fare riferimento a SimpleDateFormat documentation.

+0

Ah, vorrei che funzionasse ma se il fuso orario locale è GMT, formatta una Z. Questa Z è un problema poiché dobbiamo analizzare la stringa di data risultante in IE, e IE non mi piace Z –

+0

Questa è, per me, la risposta corretta –

7

Se è possibile utilizzare JDK 1.7 o superiore, provate questo:

public class DateUtil { 
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); 

    public static String format(Date date) { 
     return dateFormat.format(date); 
    } 

    public static Date parse(String dateString) throws AquariusException { 
     try { 
      return dateFormat.parse(dateString); 
     } catch (ParseException e) { 
      throw new AquariusException(e); 
     } 
    } 
} 

documento: https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html che supporta un nuovo formato Time Zone "XXX" (ad esempio, -3: 00)

Mentre JDK 1.6 supporta solo altri formati per Fuso orario, che sono "z" (ad es. NZST), "zzzz" (ad es. New Zealand Standard Time), "Z" (ad es. +1200), ecc.

4

Il answer by BalusC è corretto, ma ora obsoleto a partire da Java 8.

java.time

Il quadro java.time è il successore sia biblioteca Joda-Time e le vecchie classi fastidioso data-ora in bundle con le prime versioni di Java (java.util.Date/.Calendar & java.text .SimpleDateFormat).

ISO 8601

La stringa di input dei dati avviene per conformarsi alla norma ISO 8601.

Le classi java.time utilizzano i formati ISO 8601 per impostazione predefinita durante l'analisi/generazione di rappresentazioni testuali di valori di data e ora. Quindi non c'è bisogno di definire un modello di formattazione.

OffsetDateTime

La classe OffsetDateTime rappresenta un momento sulla linea del tempo regolato a qualche particolare offset-from-UTC. Nel tuo input, l'offset è di 8 ore dietro l'UTC, comunemente usato su gran parte della costa occidentale del Nord America.

OffsetDateTime odt = OffsetDateTime.parse("2010-03-01T00:00:00-08:00"); 

È sembrano volere la data-only, in questo caso utilizzare la classe LocalDate. Ma tieni presente che stai scartando i dati, (a) l'ora del giorno e (b) il fuso orario. Davvero, una data non ha significato senza il contesto di un fuso orario. Per un dato momento la data varia in tutto il mondo.Ad esempio, subito dopo la mezzanotte a Parigi è ancora "ieri" a Montréal. Quindi, mentre suggerisco di attenersi ai valori della data-ora, puoi facilmente convertirlo in uno LocalDate se insisti.

LocalDate localDate = odt.toLocalDate(); 

Time Zone

Se si conosce il fuso orario previsto, applicarlo. Un fuso orario è uno scostamento più le regole da utilizzare per la gestione di anomalie come Daylight Saving Time (DST). L'applicazione di ZoneId ci ottiene un oggetto ZonedDateTime.

ZoneId zoneId = ZoneId.of("America/Los_Angeles"); 
ZonedDateTime zdt = odt.atZoneSameInstant(zoneId); 

Generazione di stringhe

per generare una stringa in formato ISO 8601, chiamare toString.

String output = odt.toString(); 

Se avete bisogno di stringhe in altri formati, cercare Stack Overflow per l'utilizzo del pacchetto java.util.format.

Conversione in java.util.Date

Meglio evitare java.util.Date, ma se è necessario, è possibile convertire. Chiama i nuovi metodi aggiunti alle vecchie classi come java.util.Date.from dove si passa uno Instant. Un Instant è un momento sulla timeline in UTC. Siamo in grado di estrarre uno Instant dal nostro OffsetDateTime.

java.util.Date utilDate = java.util.Date(odt.toInstant()); 
1

Dato un esempio di Apache FastDateFormat (clicca per la documentazione delle versioni: 2.6 e 3.5) manca qui, sto aggiungendo uno per coloro che potrebbero averne bisogno. La chiave qui è il modello ZZ (2 capitale Z s).

import java.text.ParseException 
import java.util.Date; 
import org.apache.commons.lang3.time.FastDateFormat; 
public class DateFormatTest throws ParseException { 
    public static void main(String[] args) { 
     String stringDateFormat = "yyyy-MM-dd'T'HH:mm:ssZZ"; 
     FastDateFormat fastDateFormat = FastDateFormat.getInstance(stringDateFormat); 
     System.out.println("Date formatted into String:"); 
     System.out.println(fastDateFormat.format(new Date())); 
     String stringFormattedDate = "2016-11-22T14:30:14+05:30"; 
     System.out.println("String parsed into Date:"); 
     System.out.println(fastDateFormat.parse(stringFormattedDate)); 
    } 
} 

Ecco l'output del codice:

Date formatted into String: 
2016-11-22T14:52:17+05:30 
String parsed into Date: 
Tue Nov 22 14:30:14 IST 2016 

Nota: Il codice di cui sopra è di lang3 Apache Commons. La classe org.apache.commons.lang.time.FastDateFormat non supporta l'analisi e supporta solo la formattazione. Ad esempio, l'output del seguente codice:

import java.text.ParseException; 
import java.util.Date; 
import org.apache.commons.lang.time.FastDateFormat; 
public class DateFormatTest { 
    public static void main(String[] args) throws ParseException { 
     String stringDateFormat = "yyyy-MM-dd'T'HH:mm:ssZZ"; 
     FastDateFormat fastDateFormat = FastDateFormat.getInstance(stringDateFormat); 
     System.out.println("Date formatted into String:"); 
     System.out.println(fastDateFormat.format(new Date())); 
     String stringFormattedDate = "2016-11-22T14:30:14+05:30"; 
     System.out.println("String parsed into Date:"); 
     System.out.println(fastDateFormat.parseObject(stringFormattedDate)); 
    } 
} 

sarà questo:

Date formatted into String: 
2016-11-22T14:55:56+05:30 
String parsed into Date: 
Exception in thread "main" java.text.ParseException: Format.parseObject(String) failed 
    at java.text.Format.parseObject(Format.java:228) 
    at DateFormatTest.main(DateFormatTest.java:12) 
0

È possibile utilizzare X in Java 7.

https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

public class DateTest { 

    public static final SimpleDateFormat JSON_DATE_TIME_FORMAT = 
     new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); 
    private String date = "2016-12-01 22:05:30"; 
    private String requireDate = "2016-12-01T22:05:30+03:00"; 

    @Test 
    public void parseDateToBinBankFormat() throws ParseException { 
    String format = JSON_DATE_TIME_FORMAT.format(DATE_TIME_FORMAT.parse(date)); 
    System.out.println(format); 
    Assert.assertEquals(format, requireDate); 
    } 
} 
Problemi correlati