web-dev-qa-db-fra.com

Java SimpleDateFormat pour le fuseau horaire avec un séparateur de deux points?

J'ai une date au format suivant: 2010-03-01T00:00:00-08:00

J'ai jeté les SimpleDateFormats suivants pour l'analyser:

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

J'ai une méthode pratique qui utilise ces formats comme suit:

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

Il semble toucher le modèle yyyyMMddHHmm mais renvoie la date sous la forme Thu Dec 03 00:01:00 PST 2009.

Quel est le modèle correct pour analyser cette date? 

UPDATE: Je n'ai pas besoin de l'analyse du fuseau horaire. Je ne prévois pas de problèmes temporels liés au déplacement d'une zone à l'autre, mais comment pourrais-je analyser le format de zone "-08: 00"?

Test de l'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"));
}

Sortie du test unitaire:

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
58
Freiheit

JodaTime 's DateTimeFormat to rescue:

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

(La différence d'heure et de fuseau horaire dans toString() est simplement due au fait que je suis à GMT-4 et que je n'ai pas défini les paramètres régionaux explicitement)

Si vous voulez vous retrouver avec Java.util.Date, utilisez simplement DateTime#toDate():

Date date = dateTime.toDate();

Attendre JDK7 ( JSR-310 ) JSR-310, l'implémentation de référence s'appelle ThreeTen (j'espère que cela passera à Java 8) si vous voulez un meilleur formateur dans l'API Java SE standard. Le SimpleDateFormat actuel ne mange pas les deux points dans la notation du fuseau horaire.

Update: selon la mise à jour, vous n'avez apparemment pas besoin du fuseau horaire. Cela devrait fonctionner avec SimpleDateFormat. Il suffit de l'omettre (le Z) dans le motif.

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

(ce qui est correct selon mon fuseau horaire)

56
BalusC

si vous utilisiez Java 7, vous auriez pu utiliser le modèle Date-heure suivant. On dirait que ce modèle n'est pas pris en charge dans la version antérieure de 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);

Pour plus d'informations, reportez-vous à la documentation SimpleDateFormat .

33
YouEyeK

Voici un extrait que j'ai utilisé - avec un SimpleDateFormat simple. J'espère que quelqu'un d'autre peut en bénéficier:

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

Sortie:

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

Essayez ceci, son travail pour moi:

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

En Java 8:

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

Si vous pouvez utiliser JDK 1.7 ou supérieur, essayez ceci:

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

document: https://docs.Oracle.com/javase/7/docs/api/Java/text/SimpleDateFormat.html qui prend en charge un nouveau format de fuseau horaire "XXX" (par exemple -3: 00 )

JDK 1.6 ne prend en charge que les autres formats de fuseau horaire: "z" (par exemple, NZST), "zzzz" (par exemple, heure normale de la Nouvelle-Zélande), "Z" (par exemple, +1200), etc.

9
Willie Z

tl; dr

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

Détails

La réponse de BalusC est correcte, mais obsolète depuis Java 8.

Java.time

Le framework Java.time est le successeur de la bibliothèque Joda-Time et des anciennes classes gênantes de date et d'heure associées aux versions les plus anciennes de Java (Java.util.Date/.Calendar & Java.text.SimpleDateFormat ). 

ISO 8601

Votre chaîne de données d'entrée est conforme à la norme ISO 8601

Les classes Java.time utilisent les formats ISO 8601 par défaut pour analyser/générer des représentations textuelles de valeurs date-heure. Donc, pas besoin de définir un modèle de formatage.

OffsetDateTime

La classe OffsetDateTime représente un moment sur la ligne de temps ajusté à un décalage par rapport à UTC particulier. Dans votre saisie, le décalage est de 8 heures en retard sur le temps UTC, couramment utilisé sur une grande partie de la côte ouest de l’Amérique du Nord.

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

Vous semblez ne vouloir que la date, auquel cas utilisez la classe LocalDate . Mais gardez à l'esprit que vous supprimez des données, (a) l'heure du jour et (b) le fuseau horaire. Vraiment, une date n'a pas de sens sans le contexte d'un fuseau horaire. Pour un moment donné, la date varie à travers le monde. Par exemple, juste après minuit à Paris, c’est toujours «hier» à Montréal. Donc, bien que je suggère de rester avec les valeurs date-heure, vous pouvez facilement convertir en une variable LocalDate si vous insistez.

LocalDate localDate = odt.toLocalDate();

Fuseau horaire

Si vous connaissez le fuseau horaire prévu, appliquez-le. Un fuseau horaire est un décalage plus les règles à utiliser pour gérer les anomalies telles que Heure d'été . L'application d'une ZoneId nous obtient un objet ZonedDateTime .

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

Générer des chaînes

Pour générer une chaîne au format ISO 8601, appelez toString.

String output = odt.toString();

Si vous avez besoin de chaînes dans d'autres formats, recherchez Stack Overflow pour utiliser le package Java.util.format .

Conversion en Java.util.Date

Mieux vaut éviter Java.util.Date , mais si vous devez le faire, vous pouvez convertir. Appelez les nouvelles méthodes ajoutées aux anciennes classes telles que Java.util.Date.from où vous passez une Instant. Une Instant est un moment sur la timeline dans UTC . Nous pouvons extraire une Instant de notre OffsetDateTime

Java.util.Date utilDate = Java.util.Date( odt.toInstant() );

À propos de Java.time

Le cadre Java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciennes classes gênantes héritées _ date-heure telles que Java.util.Date , Calendar _, & SimpleDateFormat .

Le projet Joda-Time , désormais en mode { mode maintenance }, conseille la migration vers les classes Java.time .

Pour en savoir plus, consultez le Oracle Tutorial . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .

Vous pouvez échanger des objets Java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou une version ultérieure. Pas besoin de chaînes, pas besoin de Java.sql.* classes.

Où obtenir les classes Java.time? 

Le projet ThreeTen-Extra étend Java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour d'éventuels ajouts à Java.time. Vous pouvez y trouver des classes utiles telles que Interval _, YearWeek , YearQuarter _ et plus _.

7
Basil Bourque

Merci acdcjunior pour votre solution. Voici une petite version optimisée pour le formatage et l'analyse :

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
    };
};
4
Bludwarf

Vous pouvez utiliser X dans Java 7.

https://docs.Oracle.com/javase/7/docs/api/Java/text/SimpleDateFormat.html

static final SimpleDateFormat DATE_TIME_FORMAT = 
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

static final SimpleDateFormat JSON_DATE_TIME_FORMAT = 
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");

private String stringDate = "2016-12-01 22:05:30";
private String requiredDate = "2016-12-01T22:05:30+03:00";

@Test
public void parseDateToBinBankFormat() throws ParseException {
    Date date = DATE_TIME_FORMAT.parse(stringDate);
    String jsonDate = JSON_DATE_TIME_FORMAT.format(date);

    System.out.println(jsonDate);
    Assert.assertEquals(jsonDate, requiredDate);
}
2
bigspawn

Essayez setLenient(false)

Addendum: Il semble que vous reconnaissiez des chaînes Date de divers formats. Si vous devez faire une entrée, vous voudrez peut-être regarder cet exemple qui étend InputVerifier.

1
trashgod

Puisqu'aucun exemple d'Apache FastDateFormat (cliquez ici pour les documentations des versions: 2.6 et 3.5 ), j'en ajoute un pour ceux qui peuvent en avoir besoin. La clé ici est le modèle ZZ (2 majuscules Zs).

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

Voici la sortie du code:

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

Remarque: le code ci-dessus est de la langue 3 d’Apache Commons. La classe org.Apache.commons.lang.time.FastDateFormat ne prend pas en charge l'analyse et ne prend en charge que le formatage. Par exemple, la sortie du code suivant:

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

sera ce:

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)
1
Abhishek Oza

Si la chaîne de date ressemble à 2018-07-20T12: 18: 29.802Z Utilisez ceci 

SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
0
HimalayanCoder