Konwersja ciągów zgodnych z ISO 8601 na język java.util.Data

Próbuję przekonwertować ISO 8601 sformatowany Łańcuch znaków na java.util.Date.

Stwierdziłem, że wzór yyyy-MM-dd'T'HH:mm:ssZ jest zgodny z ISO8601, jeśli jest używany z Locale (porównaj próbkę).

Jednakże, używając java.text.SimpleDateFormat, nie mogę przekonwertować poprawnie sformatowanego ciągu 2010-01-01T12:00:00+01:00. Najpierw muszę ją przekształcić na 2010-01-01T12:00:00+0100, bez dwukropka.

Więc obecne rozwiązanie to

SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.GERMANY);
String date = "2010-01-01T12:00:00+01:00".replaceAll("\\+0([0-9]){1}\\:00", "+0$100");
System.out.println(ISO8601DATEFORMAT.parse(date));
Co oczywiście nie jest takie miłe. Czy coś mi umyka, czy jest lepsze rozwiązanie?

Odpowiedź

Dzięki komentarzowi JuanZe znalazłem Joda-CZAS magia, jest ona również opisana tutaj.

Więc rozwiązaniem jest

DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis();
String jtdate = "2010-01-01T12:00:00+01:00";
System.out.println(parser2.parseDateTime(jtdate));

Lub prościej, Użyj domyślnego parsera za pomocą konstruktora:

DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;
Dla mnie to miłe.
Author: wittich, 2010-02-04

25 answers

Niestety, formaty stref czasowych dostępne dla SimpleDateFormat (Java 6 i wcześniejsze) nie są zgodne z ISO 8601 . SimpleDateFormat rozumie ciągi stref czasowych, takie jak "GMT+01:00" lub "+ 0100", ten ostatni zgodnie z RFC # 822.

Nawet jeśli Java 7 dodała obsługę deskryptorów stref czasowych zgodnie z ISO 8601, SimpleDateFormat nadal nie jest w stanie poprawnie parsować pełnego ciągu daty, ponieważ nie obsługuje części opcjonalnych.

Formatowanie Twój łańcuch wejściowy za pomocą regexp jest z pewnością jedną z możliwości, ale reguły zastępcze nie są tak proste, jak w twoim pytaniu:

  • niektóre strefy czasowe nie są pełne godziny wyłączone UTC , więc ciąg nie musi kończyć się na ":00".
  • ISO8601 pozwala na włączenie tylko liczby godzin do strefy czasowej, więc "+01" jest równoważne "+ 01: 00 "
  • ISO8601 pozwala na użycie "Z", aby wskazać UTC zamiast "+00: 00".

Łatwiejszym rozwiązaniem jest możliwe jest użycie konwertera typu danych w JAXB, ponieważ JAXB musi być w stanie parsować ciąg daty ISO8601 zgodnie ze specyfikacją XML Schema. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") da ci obiekt Calendar i możesz po prostu użyć gettime () na nim, jeśli potrzebujesz obiektu Date.

Prawdopodobnie przydałby ci się Joda-Time , ale nie wiem, dlaczego powinieneś się tym przejmować.

 429
Author: jarnbjo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-08 17:50:49

OK, na to pytanie już odpowiedziałam, ale i tak odpowiem. To może komuś pomóc.

Szukałem rozwiązania dla Androida (API 7).

    Joda nie wchodzi w grę - jest ogromna i cierpi na powolną inicjalizację. Wydawało się to również dużą przesadą w tym konkretnym celu.
  • odpowiedzi zawierające javax.xml nie będą działać na Android API 7.

Skończyło się implementacją tej prostej klasy. Obejmuje ona tylko najczęściej tworzy ciągów ISO 8601, ale w niektórych przypadkach powinno to wystarczyć(gdy jesteś pewien, że dane wejściowe będą w tym formacie).

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * Helper class for handling a most common subset of ISO 8601 strings
 * (in the following format: "2008-03-01T13:00:00+01:00"). It supports
 * parsing the "Z" timezone, but many other less-used features are
 * missing.
 */
public final class ISO8601 {
    /** Transform Calendar to ISO 8601 string. */
    public static String fromCalendar(final Calendar calendar) {
        Date date = calendar.getTime();
        String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
            .format(date);
        return formatted.substring(0, 22) + ":" + formatted.substring(22);
    }

    /** Get current date and time formatted as ISO 8601 string. */
    public static String now() {
        return fromCalendar(GregorianCalendar.getInstance());
    }

    /** Transform ISO 8601 string to Calendar. */
    public static Calendar toCalendar(final String iso8601string)
            throws ParseException {
        Calendar calendar = GregorianCalendar.getInstance();
        String s = iso8601string.replace("Z", "+00:00");
        try {
            s = s.substring(0, 22) + s.substring(23);  // to get rid of the ":"
        } catch (IndexOutOfBoundsException e) {
            throw new ParseException("Invalid length", 0);
        }
        Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(s);
        calendar.setTime(date);
        return calendar;
    }
}

Uwaga dotycząca wydajności: za każdym razem uruchamiam nowy SimpleDateFormat, aby uniknąć błędu W Androidzie 2.1. Jeśli jesteś tak zdumiony jak ja, zobacz tę zagadkę . W przypadku innych silników Java instancję można buforować w prywatnym polu statycznym(używając ThreadLocal, aby zabezpieczyć wątek).

 191
Author: wrygiel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:47

The way that is blessed by Java 7 documentation :

DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
String string1 = "2001-07-04T12:08:56.235-0700";
Date result1 = df1.parse(string1);

DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String string2 = "2001-07-04T12:08:56.235-07:00";
Date result2 = df2.parse(string2);

Więcej przykładów można znaleźć w sekcji przykłady pod adresem SimpleDateFormat javadoc .

 176
Author: Antonio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-17 20:24:47

Java.CZAS

Java.time API (wbudowane w Java 8 i nowsze), sprawia, że jest to trochę łatwiejsze.

Jeśli wiesz, że wejście jest w UTC , takie jak Z (dla Zulu) na końcu, Instant klasa może analizować.

java.util.Date date = Date.from( Instant.parse( "2014-12-12T10:39:40Z" ));

Jeśli Twoje dane wejściowe mogą być inne offset-from-UTC, a nie UTC wskazane przez Z (Zulu) na końcu, użyj OffsetDateTime Klasa do analizy.

OffsetDateTime odt = OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" );

Następnie wyodrębnij Instant, i konwertować na java.util.Date dzwoniąc from.

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );
 69
Author: Adam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-18 09:57:46

BibliotekaJackson-databind posiada równieżklasę ISO8601DateFormat , która to robi (rzeczywistą implementację wISO8601Utils .

ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");
 61
Author: david_p,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-05 14:23:43

Tl;dr

OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" )

Korzystanie z Javy.CZAS

Nowa java.pakiet time w Javie 8 i później został zainspirowany przez Joda-Time.

Klasa OffsetDateTime reprezentuje moment na osi czasu z przesunięciem od-UTC , ale nie strefę czasową.

OffsetDateTime odt = OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" );

Wywołanie toString generuje ciąg znaków w standardowym formacie ISO 8601:

2010-01-01t12:00+01: 00

Aby zobaczyć tę samą wartość przez obiektyw UTC, wyodrębnij an Instant lub dostosuj przesunięcie od +01:00 do 00:00.

Instant instant = odt.toInstant();  

...lub...

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC );

Dostosuj do strefy czasowej w razie potrzeby. A Strefa czasowa jest historią offset-from-UTC wartości dla regionu, z zestawem reguł postępowania z anomaliami, takimi jak czas letni (DST). Stosuj więc strefę czasową, a nie zwykłe przesunięcie, o ile to możliwe.

ZonedDateTime zonedDateTimeMontréal = odt.atZoneSameInstant( ZoneId.of( "America/Montreal" ) );

O Javie.czas

The java.czas framework jest wbudowany w Java 8 i nowsze. Te klasy zastępują kłopotliwe stare dziedzictwo klasy date-time, takie jak java.util.Date, Calendar, & SimpleDateFormat.

The Joda-czas projekt, obecnie w trybie konserwacji , radzi migrację do java.CZAS klasy.

Aby dowiedzieć się więcej, Zobacz samouczek Oracle. I wyszukaj przepełnienie stosu dla wielu przykładów i wyjaśnień. Specyfikacja to JSR 310 .

Możesz exchange java.obiekty time bezpośrednio z bazą danych. Użyj sterownika JDBCzgodnego z JDBC 4.2 lub nowszego. Nie ma potrzeby stosowania łańcuchów, nie ma potrzeby stosowania klas java.sql.*.

Skąd pobrać Javę.zajęcia czasowe?

  • Java SE 8, Java SE 9, Java SE 10, i Później
    • Wbudowany.
    • część standardowego API Javy z pakietem wdrożenie.
    • Java 9 dodaje kilka drobnych funkcji i poprawek.
  • Java SE 6 oraz Java SE 7
    • Większość Javy.funkcja czasu powraca-przeportowana do Javy 6 i 7 W ThreeTen-Backport.
  • Android
    • późniejsze wersje Android bundle implementacji java.zajęcia czasowe.
    • dla wcześniejszego Androida (ThreeTenABP projekt dostosowuje ThreeTen-Backport (wspomniano powyżej). Zobacz też Jak stosować ThreeTenABP....

The ThreeTen-Extra projekt rozszerza Javę.czas z dodatkowymi zajęciami. Ten projekt jest poligonem dla potencjalnych przyszłych dodatków do Javy.czas. Możesz znaleźć tutaj kilka przydatnych klas, takich jak Interval, YearWeek, YearQuarter, i więcej.


 33
Author: Basil Bourque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-23 02:09:06

Dla Javy w wersji 7

Możesz śledzić dokumentację Oracle: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X-jest używany dla strefy czasowej ISO 8601

TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());

System.out.println(nowAsISO);

DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
//nowAsISO = "2013-05-31T00:00:00Z";
Date finalResult = df1.parse(nowAsISO);

System.out.println(finalResult);
 26
Author: d.danailov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-08 14:20:28

Rozwiązanie DatatypeConverter nie działa we wszystkich maszynach wirtualnych. Dla mnie działa:

javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()

Odkryłem, że joda nie działa po wyjęciu z pudełka (specjalnie dla przykładu podałem powyżej ze strefą czasową na datę, która powinna być ważna)

 20
Author: James Scriven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-09 11:15:04

Myślę, że powinniśmy użyć

DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")

Dla Daty 2010-01-01T12:00:00Z

 18
Author: Toby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-23 13:56:05

Innym bardzo prostym sposobem parsowania znaczników czasu ISO8601 jest użycie org.apache.commons.lang.time.DateUtils:

import static org.junit.Assert.assertEquals;

import java.text.ParseException;
import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
import org.junit.Test;

public class ISO8601TimestampFormatTest {
  @Test
  public void parse() throws ParseException {
    Date date = DateUtils.parseDate("2010-01-01T12:00:00+01:00", new String[]{ "yyyy-MM-dd'T'HH:mm:ssZZ" });
    assertEquals("Fri Jan 01 12:00:00 CET 2010", date.toString());
  }
}
 8
Author: tmandry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-22 14:05:35

Java.czas

Zauważ, że w Javie 8 możesz użyć java.czas.Klasa ZonedDateTime i jej statyczna metoda parse(CharSequence text).

 6
Author: Martin Rust,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-20 16:59:08

Stanąłem przed tym samym problemem i rozwiązałem go za pomocą następującego kodu .

 public static Calendar getCalendarFromISO(String datestring) {
    Calendar calendar = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault()) ;
    SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());
    try {
        Date date = dateformat.parse(datestring);
        date.setHours(date.getHours() - 1);
        calendar.setTime(date);

        String test = dateformat.format(calendar.getTime());
        Log.e("TEST_TIME", test);

    } catch (ParseException e) {
        e.printStackTrace();
    }

    return calendar;
}

Wcześniej używałem SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

Ale później znalazłem główną przyczyną wyjątku było yyyy-MM-dd'T'HH:mm:ss.SSSZ,

Więc użyłem

SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());

Dla mnie zadziałało .
 4
Author: Abhay Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-10 09:05:26

Możesz również użyć następującej klasy -

org.springframework.extensions.surf.util.ISO8601DateFormat


Date date = ISO8601DateFormat.parse("date in iso8601");

Link do hierarchii java doc - dla pakietu org.springframework.rozszerzenia.surf.maven.plugin.util

 4
Author: Sergey Palyukh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 09:30:16

Apache Jackrabbit używa formatu ISO 8601 do przechowywania dat i istnieje klasa pomocnicza do ich analizy:

Org.Apacz.jackrabbit.util.ISO8601

Pochodzi z jackrabbit-jcr-commons .

 3
Author: Alexander Klimetschek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-22 10:28:33

Jak inni wspomnieli Android nie ma dobrego sposobu na obsługę parsowania / formatowania DAT ISO 8601 przy użyciu klas zawartych w SDK. Pisałem ten kod wiele razy, więc w końcu stworzyłem Gist, który zawiera klasę DateUtils, która obsługuje formatowanie i parsowanie DAT ISO 8601 i RFC 1123. Gist zawiera również przypadek testowy pokazujący, co obsługuje.

Https://gist.github.com/mraccola/702330625fad8eebe7d3

 3
Author: Matt Accola,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-20 05:01:12

Obejście dla Java 7+ polega na użyciu SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

Ten kod może parsować format ISO8601 w następujący sposób:

  • 2017-05-17T06:01:43.785Z
  • 2017-05-13T02:58:21.391+01:00

Ale na Java6, SimpleDateFormat nie rozumie X charakteru i rzuci
IllegalArgumentException: Unknown pattern character 'X'
Musimy znormalizować datę ISO8601 do formatu czytelnego w Javie 6 za pomocą SimpleDateFormat.

public static Date iso8601Format(String formattedDate) throws ParseException {
    try {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);
        return df.parse(formattedDate);
    } catch (IllegalArgumentException ex) {
        // error happen in Java 6: Unknown pattern character 'X'
        if (formattedDate.endsWith("Z")) formattedDate = formattedDate.replace("Z", "+0000");
        else formattedDate = formattedDate.replaceAll("([+-]\\d\\d):(\\d\\d)\\s*$", "$1$2");
        DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
        return df1.parse(formattedDate);
    }
}

Powyższa metoda zastąpienia [Z przez +0000] lub [+01:00 przez +0100] W przypadku wystąpienia błędu w Java 6 (można wykryć Java wersja i zastąp try/catch poleceniem if).

 3
Author: Khang .NT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-16 09:42:19

SimpleDateFormat dla JAVA 1.7 ma fajny wzór dla formatu ISO 8601.

Class SimpleDateFormat

Oto co zrobiłem:

Date d = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
         Locale.ENGLISH).format(System.currentTimeMillis());
 2
Author: Eesha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 09:28:28

Java ma kilkanaście różnych sposobów parsowania daty-czasu, co pokazują doskonałe odpowiedzi tutaj. Ale co nieco zadziwiające, żadna z klas czasu Javy w pełni nie implementuje ISO 8601!

Z Javą 8 polecam:

ZonedDateTime zp = ZonedDateTime.parse(string);
Date date = Date.from(zp.toInstant());

To będzie obsługiwać przykłady zarówno w UTC, jak i z przesunięciem, jak "2017-09-13t10:36: 40Z" lub "2017-09-13T10:36:40+01:00". Będzie to zrobić dla większości przypadków użycia.

Ale nie obsłuży przykładów takich jak "2017-09-13T10:36:40+01", które jest poprawnym ISO 8601 data-czas.
Nie będzie również obsługiwać tylko daty, np. "2017-09-13".

Jeśli musisz się nimi zająć, sugerowałbym najpierw użycie regex, aby wywęszyć składnię.

Jest tu Ładna lista przykładów ISO 8601 z mnóstwem przypadków narożnych: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ nie znam żadnej klasy Javy, która poradziłaby sobie z nimi wszystkimi.

 2
Author: Daniel Winterstein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-13 11:22:54

Use string like LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

 1
Author: Stepan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-18 14:57:35

Miałem podobną potrzebę: musiałem być w stanie przeanalizować dowolną datę zgodną z ISO8601, nie znając dokładnego formatu z góry, i chciałem lekkiego rozwiązania, które będzie działać również na Androidzie.

Kiedy googlowałem moje potrzeby natknąłem się na to pytanie i zauważyłem, że AFAIU, żadna odpowiedź nie pasuje całkowicie do moich potrzeb. Więc opracowałem jISO8601 i wcisnąłem go na Maven central.

Wystarczy dodać pom.xml:

<dependency>
  <groupId>fr.turri</groupId>
  <artifactId>jISO8601</artifactId>
  <version>0.2</version>
</dependency>

A potem możesz iść:

import fr.turri.jiso8601.*;
...
Calendar cal = Iso8601Deserializer.toCalendar("1985-03-04");
Date date = Iso8601Deserializer.toDate("1985-03-04T12:34:56Z");

Hopes it pomocy.

 0
Author: gturri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-29 17:30:57

Aby sformatować taką datę, w aplikacji opartej na Javie 6 zadziałało dla mnie następujące rozwiązanie. W projekcie thymeleaf istnieje klasa DateFormat JacksonThymeleafISO8601DateFormat, która wstawia brakujący dwukropek:

Https://github.com/thymeleaf/thymeleaf/blob/40d27f44df7b52eda47d1bc6f1b3012add6098b3/src/main/java/org/thymeleaf/standard/serializer/StandardJavaScriptSerializer.java

Użyłem go do kompatybilności z formatem daty ECMAScript.

 0
Author: ekip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-09 14:29:20

Zrób to tak:

public static void main(String[] args) throws ParseException {

    String dateStr = "2016-10-19T14:15:36+08:00";
    Date date = javax.xml.bind.DatatypeConverter.parseDateTime(dateStr).getTime();

    System.out.println(date);

}

Oto wyjście:

Śro Paź 19 15:15:36 CST 2016

 0
Author: yinhaomin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-19 07:04:30

Funkcja bazowa dzięki uprzejmości: @wrygiel.

Ta funkcja może konwertować format ISO8601 na datę Java, która może obsługiwać wartości przesunięcia. Zgodnie z definicją ISO 8601 offset może być wymieniony w różnych formatach.

±[hh]:[mm]
±[hh][mm]
±[hh]

Eg:  "18:30Z", "22:30+04", "1130-0700", and "15:00-03:30" all mean the same time. - 06:30PM UTC

Ta klasa ma statyczne metody konwersji

  • iso8601 string to date(Local TimeZone) object
  • Date to iso8601 string
  • Daylight Saving jest automatycznie calc

Próbka ISO8601 Strings

/*       "2013-06-25T14:00:00Z";
         "2013-06-25T140000Z";
         "2013-06-25T14:00:00+04";
         "2013-06-25T14:00:00+0400";
         "2013-06-25T140000+0400";
         "2013-06-25T14:00:00-04";
         "2013-06-25T14:00:00-0400";
         "2013-06-25T140000-0400";*/


public class ISO8601DateFormatter {

private static final DateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
private static final DateFormat DATE_FORMAT_2 = new SimpleDateFormat("yyyy-MM-dd'T'HHmmssZ");
private static final String UTC_PLUS = "+";
private static final String UTC_MINUS = "-";

public static Date toDate(String iso8601string) throws ParseException {
    iso8601string = iso8601string.trim();
    if(iso8601string.toUpperCase().indexOf("Z")>0){
        iso8601string = iso8601string.toUpperCase().replace("Z", "+0000");
    }else if(((iso8601string.indexOf(UTC_PLUS))>0)){
        iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_PLUS));
        iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_PLUS), UTC_PLUS);
    }else if(((iso8601string.indexOf(UTC_MINUS))>0)){
        iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_MINUS));
        iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_MINUS), UTC_MINUS);
    }

    Date date = null;
    if(iso8601string.contains(":"))
        date = DATE_FORMAT_1.parse(iso8601string);
    else{
        date = DATE_FORMAT_2.parse(iso8601string);
    }
    return date;
}

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

private static String replaceColon(String sourceStr, int offsetIndex){
    if(sourceStr.substring(offsetIndex).contains(":"))
        return sourceStr.substring(0, offsetIndex) + sourceStr.substring(offsetIndex).replace(":", "");
    return sourceStr;
}

private static String appendZeros(String sourceStr, int offsetIndex, String offsetChar){
    if((sourceStr.length()-1)-sourceStr.indexOf(offsetChar,offsetIndex)<=2)
        return sourceStr + "00";
    return sourceStr;
}

}

 -1
Author: AKh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-25 23:28:06

To wydawało mi się działać najlepiej dla mnie:

public static Date fromISO8601_( String string ) {

    try {
            return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ssXXX").parse ( string );
    } catch ( ParseException e ) {
        return Exceptions.handle (Date.class, "Not a valid ISO8601", e);
    }


}

Musiałem przekonwertować na / fro JavaScript date strings do Javy. Znalazłem powyższe prace z rekomendacją. Było kilka przykładów z użyciem SimpleDateFormat, które były bliskie, ale nie wydawały się być podzbiorem zgodnie z zaleceniem:

Http://www.w3.org/TR/NOTE-datetime

I wspierane przez ciągi PLIST i JavaScript i takie, czego potrzebowałem.

To wydaje się być najczęstszą formą ISO8601 / align = "left" /

Podane przykłady to:

1994-11-05T08:15:30-05:00 corresponds 
November 5, 1994, 8:15:30 am, US Eastern Standard Time.

 1994-11-05T13:15:30Z corresponds to the same instant.

Mam też szybką wersję:

final static int SHORT_ISO_8601_TIME_LENGTH =  "1994-11-05T08:15:30Z".length ();
                                            // 01234567890123456789012
final static int LONG_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30-05:00".length ();


public static Date fromISO8601( String string ) {
    if (isISO8601 ( string )) {
        char [] charArray = Reflection.toCharArray ( string );//uses unsafe or string.toCharArray if unsafe is not available
        int year = CharScanner.parseIntFromTo ( charArray, 0, 4 );
        int month = CharScanner.parseIntFromTo ( charArray, 5, 7 );
        int day = CharScanner.parseIntFromTo ( charArray, 8, 10 );
        int hour = CharScanner.parseIntFromTo ( charArray, 11, 13 );

        int minute = CharScanner.parseIntFromTo ( charArray, 14, 16 );

        int second = CharScanner.parseIntFromTo ( charArray, 17, 19 );

        TimeZone tz ;

         if (charArray[19] == 'Z') {

             tz = TimeZone.getTimeZone ( "GMT" );
         } else {

             StringBuilder builder = new StringBuilder ( 9 );
             builder.append ( "GMT" );
             builder.append( charArray, 19, LONG_ISO_8601_TIME_LENGTH - 19);
             String tzStr = builder.toString ();
             tz = TimeZone.getTimeZone ( tzStr ) ;

         }
         return toDate ( tz, year, month, day, hour, minute, second );

    }   else {
        return null;
    }

}

...

public static int parseIntFromTo ( char[] digitChars, int offset, int to ) {
    int num = digitChars[ offset ] - '0';
    if ( ++offset < to ) {
        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
        if ( ++offset < to ) {
            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
            if ( ++offset < to ) {
                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                if ( ++offset < to ) {
                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                    if ( ++offset < to ) {
                        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                        if ( ++offset < to ) {
                            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                            if ( ++offset < to ) {
                                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                if ( ++offset < to ) {
                                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return num;
}


public static boolean isISO8601( String string ) {
      boolean valid = true;

      if (string.length () == SHORT_ISO_8601_TIME_LENGTH) {
          valid &=  (string.charAt ( 19 )  == 'Z');

      } else if (string.length () == LONG_ISO_8601_TIME_LENGTH) {
          valid &=  (string.charAt ( 19 )  == '-' || string.charAt ( 19 )  == '+');
          valid &=  (string.charAt ( 22 )  == ':');

      } else {
          return false;
      }

    //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
    // "1 9 9 4 - 1 1 - 0 5 T 0 8 : 1 5 : 3 0 - 0 5 : 0 0

    valid &=  (string.charAt ( 4 )  == '-') &&
                (string.charAt ( 7 )  == '-') &&
                (string.charAt ( 10 ) == 'T') &&
                (string.charAt ( 13 ) == ':') &&
                (string.charAt ( 16 ) == ':');

    return valid;
}
[5]}nie oceniłem go, ale myślę, że będzie dość szybko. Wygląda na to, że działa. :)
@Test
public void testIsoShortDate() {
    String test =  "1994-11-05T08:15:30Z";

    Date date = Dates.fromISO8601 ( test );
    Date date2 = Dates.fromISO8601_ ( test );

    assertEquals(date2.toString (), date.toString ());

    puts (date);
}

@Test
public void testIsoLongDate() {
    String test =  "1994-11-05T08:11:22-05:00";

    Date date = Dates.fromISO8601 ( test );
    Date date2 = Dates.fromISO8601_ ( test );

    assertEquals(date2.toString (), date.toString ());

    puts (date);
}
 -1
Author: RickHigh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-14 00:32:36

Myślę, że to, co Wiele osób chce zrobić, to parse JSON date strings. Istnieje duża szansa, że jeśli wejdziesz na tę stronę, możesz chcieć przekonwertować datę JavaScript JSON na datę Java.

Aby pokazać jak wygląda łańcuch daty JSON:

    var d=new Date();
    var s = JSON.stringify(d);

    document.write(s);
    document.write("<br />"+d);


    "2013-12-14T01:55:33.412Z"
    Fri Dec 13 2013 17:55:33 GMT-0800 (PST)

Ciąg daty JSON to 2013-12-14t01: 55: 33.412 Z.

Daty nie są objęte specyfikacją JSON, ale powyższe jest bardzo specyficznym formatem ISO 8601, podczas gdy ISO_8601 jest znacznie większe i jest to jedynie podzbiór, choć bardzo ważne.

Zobacz http://www.json.org Zobacz http://en.wikipedia.org/wiki/ISO_8601 Zobacz http://www.w3.org/TR/NOTE-datetime

Tak się składa, że napisałem parser JSON i Parser PLIST, które używają ISO-8601, ale nie tych samych bitów.

/*
    var d=new Date();
    var s = JSON.stringify(d);

    document.write(s);
    document.write("<br />"+d);


    "2013-12-14T01:55:33.412Z"
    Fri Dec 13 2013 17:55:33 GMT-0800 (PST)


 */
@Test
public void jsonJavaScriptDate() {
    String test =  "2013-12-14T01:55:33.412Z";

    Date date = Dates.fromJsonDate ( test );
    Date date2 = Dates.fromJsonDate_ ( test );

    assertEquals(date2.toString (), "" + date);

    puts (date);
}

Napisałem dwa sposoby, aby to zrobić dla mojego projektu. Jeden standardowy, jeden szybki.

Ponownie, łańcuch daty JSON jest bardzo specyficzną implementacją ISO 8601....

(zamieściłem drugą w inne odpowiedzi, które powinny działać dla PLIST, które są innym formatem ISO 8601).

Data JSON jest następująca:

public static Date fromJsonDate_( String string ) {

    try {

        return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse ( string );
    } catch ( ParseException e ) {
        return Exceptions.handle (Date.class, "Not a valid JSON date", e);
    }


}

Pliki PLIST (ASCII non GNUNext) również używają ISO 8601, ale nie ma milisekund... nie wszystkie daty ISO-8601 są takie same. (Przynajmniej nie znalazłem takiego, który używa milis jeszcze i parser widziałem pominąć strefę czasową całkowicie OMG).

Teraz dla szybkiej wersji (można ją znaleźć w Boon).

public static Date fromJsonDate( String string ) {

    return fromJsonDate ( Reflection.toCharArray ( string ), 0, string.length () );

}

Zwróć uwagę na to odbicie.toCharArray używa niebezpiecznych jeśli jest dostępny, ale domyślnie ciąg znaków.tochararay jeśli nie.

(możesz wziąć to z przykładu, zastępując Reflection.toCharArray (string ) with string.toCharArray ()).

public static Date fromJsonDate( char[] charArray, int from, int to ) {

    if (isJsonDate ( charArray, from, to )) {
        int year = CharScanner.parseIntFromTo ( charArray, from + 0, from + 4 );
        int month = CharScanner.parseIntFromTo ( charArray,  from +5,  from +7 );
        int day = CharScanner.parseIntFromTo ( charArray,  from +8,  from +10 );
        int hour = CharScanner.parseIntFromTo ( charArray,  from +11,  from +13 );

        int minute = CharScanner.parseIntFromTo ( charArray,  from +14,  from +16 );

        int second = CharScanner.parseIntFromTo ( charArray,  from +17,  from +19 );

        int miliseconds = CharScanner.parseIntFromTo ( charArray,  from +20,  from +23 );

        TimeZone tz = TimeZone.getTimeZone ( "GMT" );


        return toDate ( tz, year, month, day, hour, minute, second, miliseconds );

    }   else {
        return null;
    }

}

Isjsondate jest zaimplementowany w następujący sposób:

public static boolean isJsonDate( char[] charArray, int start, int to ) {
    boolean valid = true;
    final int length = to -start;

    if (length != JSON_TIME_LENGTH) {
        return false;
    }

    valid &=  (charArray [ start + 19 ]  == '.');

    if (!valid) {
        return false;
    }


    valid &=  (charArray[  start +4 ]  == '-') &&
            (charArray[  start +7 ]  == '-') &&
            (charArray[  start +10 ] == 'T') &&
            (charArray[  start +13 ] == ':') &&
            (charArray[  start +16 ] == ':');

    return valid;
}
W każdym razie... domyślam się, że sporo osób tu przychodzi.. może szukamy ciągu daty JSON i chociaż jest to data ISO-8601, jest to bardzo konkretna data, która wymaga bardzo konkretnego parse.
public static int parseIntFromTo ( char[] digitChars, int offset, int to ) {
    int num = digitChars[ offset ] - '0';
    if ( ++offset < to ) {
        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
        if ( ++offset < to ) {
            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
            if ( ++offset < to ) {
                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                if ( ++offset < to ) {
                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                    if ( ++offset < to ) {
                        num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                        if ( ++offset < to ) {
                            num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                            if ( ++offset < to ) {
                                num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                if ( ++offset < to ) {
                                    num = ( num * 10 ) + ( digitChars[ offset ] - '0' );
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return num;
}

Zobacz https://github.com/RichardHightower/boon Boon ma Parser PLIST (ASCII) i parser JSON.

Parser JSON jest najszybszym Parserem JSON Java, jaki znam.

/ Align = "left" /

Https://github.com/gatling/json-parsers-benchmark

Benchmark                               Mode Thr     Count  Sec         Mean   Mean error        Units
BoonCharArrayBenchmark.roundRobin      thrpt  16        10    1   724815,875    54339,825    ops/s
JacksonObjectBenchmark.roundRobin      thrpt  16        10    1   580014,875   145097,700    ops/s
JsonSmartBytesBenchmark.roundRobin     thrpt  16        10    1   575548,435    64202,618    ops/s
JsonSmartStringBenchmark.roundRobin    thrpt  16        10    1   541212,220    45144,815    ops/s
GSONStringBenchmark.roundRobin         thrpt  16        10    1   522947,175    65572,427    ops/s
BoonDirectBytesBenchmark.roundRobin    thrpt  16        10    1   521528,912    41366,197    ops/s
JacksonASTBenchmark.roundRobin         thrpt  16        10    1   512564,205   300704,545    ops/s
GSONReaderBenchmark.roundRobin         thrpt  16        10    1   446322,220    41327,496    ops/s
JsonSmartStreamBenchmark.roundRobin    thrpt  16        10    1   276399,298   130055,340    ops/s
JsonSmartReaderBenchmark.roundRobin    thrpt  16        10    1    86789,825    17690,031    ops/s

Posiada najszybszy parser JSON dla strumieni, czytników, bajtów [], char [], CharSequence (StringBuilder, CharacterBuffer) i String.

Zobacz więcej benchmarks at:

Https://github.com/RichardHightower/json-parsers-benchmark

 -2
Author: RickHigh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-14 02:48:34