Mengubah String yang memenuhi ISO 8601 ke java.util.Date

668

Saya mencoba untuk mengubah String berformat ISO 8601 ke ajava.util.Date .

Saya menemukan polanya yyyy-MM-dd'T'HH:mm:ssZ untuk menjadi ISO8601-compliant jika digunakan dengan Lokal (bandingkan sampel).

Namun, dengan menggunakan java.text.SimpleDateFormat, saya tidak dapat mengonversi String yang diformat dengan benar 2010-01-01T12:00:00+01:00. Saya harus mengubahnya terlebih dahulu2010-01-01T12:00:00+0100 , tanpa titik dua.

Jadi, solusi saat ini adalah

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

yang jelas tidak menyenangkan. Apakah saya kehilangan sesuatu atau ada solusi yang lebih baik?


Menjawab

Berkat komentar JuanZe, saya menemukan sihir Joda-Time , juga dijelaskan di sini .

Jadi solusinya adalah

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

Atau lebih sederhana, gunakan parser default melalui konstruktor:

DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;

Bagi saya, ini bagus.

Ice09
sumber
243
Bersiaplah untuk menerima banyak jawaban "Gunakan JodaTime" ...
JuanZe
3
@ Ice09: Jika dokumentasi API untuk DateTimeFormat benar (dokumentasi JoDa bisa menyesatkan, salah atau tidak lengkap), pola yang Anda gunakan dalam "jawaban" Anda sendiri tidak kompatibel dengan ISO8601.
jarnbjo
21
Saya tidak yakin kapan ini ditambahkan, tetapi 'X' muncul untuk menyelesaikan masalah ini di dalam SimpleDateFormat. Pola "yyyy-MM-dd'T'HH: mm: ssX" berhasil mem-parsing contoh dalam pertanyaan.
mlohbihler
12
'X' tersedia sejak Java 7.
Lars Grammel
3
Java 8 membuatnya mudah! Ada permata tersembunyi oleh Adam dalam jawaban di bawah ini: stackoverflow.com/a/27479533/1262901
Fabian Keller

Jawaban:

477

Sayangnya, format zona waktu yang tersedia untuk SimpleDateFormat (Java 6 dan yang lebih lama) tidak sesuai dengan ISO 8601 . SimpleDateFormat memahami string zona waktu seperti "GMT + 01: 00" atau "+0100", yang terakhir menurut RFC # 822 .

Bahkan jika Java 7 menambahkan dukungan untuk deskriptor zona waktu sesuai dengan ISO 8601, SimpleDateFormat masih tidak dapat menguraikan string tanggal yang lengkap, karena tidak memiliki dukungan untuk bagian opsional.

Memformat ulang string input Anda menggunakan regexp tentu merupakan satu kemungkinan, tetapi aturan penggantian tidak sesederhana dalam pertanyaan Anda:

  • Beberapa zona waktu bukan jam penuh UTC , jadi string tidak harus diakhiri dengan ": 00".
  • ISO8601 hanya memungkinkan jumlah jam untuk dimasukkan dalam zona waktu, jadi "+01" sama dengan "+01: 00"
  • ISO8601 memungkinkan penggunaan "Z" untuk menunjukkan UTC, bukan "+00: 00".

Solusi yang lebih mudah adalah dengan menggunakan konverter tipe data di JAXB, karena JAXB harus dapat mengurai string tanggal ISO8601 sesuai dengan spesifikasi XML Schema. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z")akan memberi Anda Calendarobjek dan Anda bisa menggunakan getTime () di atasnya, jika Anda memerlukanDate objek.

Anda mungkin bisa menggunakan Joda-Time juga, tapi saya tidak tahu mengapa Anda harus repot-repot dengan itu.

jarnbjo
sumber
18
Solusi JAXB adalah pendekatan yang sangat kreatif! Ini juga berfungsi, saya telah mengujinya dengan sampel saya. Namun, bagi siapa pun yang menghadapi masalah dan diizinkan menggunakan JodaTime, saya akan menyarankan untuk menggunakannya, karena rasanya lebih alami. Tetapi solusi Anda tidak memerlukan pustaka tambahan (setidaknya dengan Java 6).
Ice09
36
Ini kebalikannya: Kalender c = GregorianCalendar.getInstance (); c.setTime (aDate); mengembalikan javax.xml.bind.DatatypeConverter.printDateTime (c);
Alexander Ljungberg
4
Sebenarnya itu tidak begitu sederhana b / c Anda harus menginisialisasi datatypeConverter jaxb. Saya akhirnya menggunakan DatatypeFactory sendiri seperti yang dilakukan DataTypeConverterImpl secara internal. Sakit kepala.
gtrak
3
@Simon: Tidak, zona waktu tentu saja tidak diabaikan. Anda pasti melakukan sesuatu yang salah. Jika Anda mengetik lebih dari beberapa karakter dan memberi tahu kami apa yang sebenarnya Anda lakukan, seseorang mungkin menjelaskan kepada Anda apa.
jarnbjo
4
@jarnbjo Anda adalah orang pertama dan satu-satunya yang saya temui yang lebih suka standar, pra 1.8, kelas tanggal java, daripada joda-time. Saya menemukan joda-waktu kesenangan harfiah untuk digunakan, terutama bila dibandingkan dengan api standar yang merupakan kekejian.
NimChimpsky
245

Cara yang diberkati oleh dokumentasi Java 7 :

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

Anda dapat menemukan lebih banyak contoh di bagian Contoh di javadoc SimpleDateFormat .

UPD 02/13/2020: Ada cara yang sama sekali baru untuk melakukan ini di Java 8

Antonio
sumber
7
Jawaban Anda membantu saya mengonversi ISODate MongoDB ke tanggal lokal. Salam.
Langit Biru
9
@ b.long Java menambahkan lebih dari konstanta untuk format yang sesuai dengan ISO 8601. Java mendapatkan seluruh kerangka kerja baru untuk pekerjaan tanggal yang mencakup dukungan bawaan bawaan untuk format tersebut. Lihat java.timekerangka kerja baru di Java 8, terinspirasi oleh Joda-Time , menggantikan kelas java.util.Date, .Calendar, dan SimpleDateFormat yang merepotkan.
Basil Bourque
2
Bukankah ini berarti Anda harus mengetahui format tanggal sebelumnya? Bagaimana jika Anda harus menerima string1dan string2tetapi tidak tahu mana yang akan Anda dapatkan.
Timmmm
16
'Z' perlu di kutip
kervin
7
@kervin Jika Z ada dalam tanda kutip, bukankah pemformat akan mencari karakter Z secara spesifik, tidak semua string offset yang dapat diwakilinya? Sepertinya mengutip Z hanya akan bekerja secara kebetulan, jika string tanggal Anda kebetulan berada di UTC.
spaaarky21
201

Oke, pertanyaan ini sudah dijawab, tetapi saya tetap akan memberikan jawaban. Mungkin membantu seseorang.

Saya sudah mencari solusi untuk Android (API 7).

  • Joda keluar dari pertanyaan - besar dan menderita inisialisasi lambat. Tampaknya juga suatu pembunuhan besar-besaran untuk tujuan tertentu itu.
  • Jawaban yang melibatkan javax.xmltidak akan berfungsi di Android API 7.

Akhirnya menerapkan kelas sederhana ini. Ini hanya mencakup bentuk paling umum dari string ISO 8601, tetapi ini harus cukup dalam beberapa kasus (ketika Anda cukup yakin bahwa input akan berada dalam format ini ).

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

Catatan kinerja: Saya instantiate SimpleDateFormat baru setiap saat sebagai cara untuk menghindari bug di Android 2.1. Jika Anda sama takjubnya dengan saya, lihatlah teka-teki ini . Untuk mesin Java lainnya, Anda dapat menyimpan instance di bidang statis pribadi (menggunakan ThreadLocal, agar aman thread).

wrygiel
sumber
2
Mungkin ini seharusnya dibuat menjadi pertanyaan sendiri, dengan jawabannya sendiri?
Thorbear
5
Ini adalah halaman pertama yang saya temui ketika saya mencari jawabannya, jadi sepertinya cocok. Bagi kebanyakan pengembang Java, Android bukanlah Java. Namun, dalam kebanyakan kasus, satu berfungsi sama dengan yang lain, sehingga banyak pengembang Android akan mencari "java" ketika mencari ini.
wrygiel
1
Perhatikan bahwa ini tidak memperhitungkan resolusi milidetik. Ini mudah ditambahkan.
Sky Kelsey
6
saya harus menambahkan .SSS untuk detik pecahan tetapi berfungsi dengan baik thx. Mengapa Anda melakukannya s = s.substring(0, 22) + s.substring(23);- saya tidak mengerti maksudnya
Dori
1
input = input.replaceAll ("[Zz]", "+0000"); akan bekerja juga dan operasi substring dapat dihindari.
Javanator
115

waktu java

The java.time API (dibangun ke Jawa 8 dan kemudian), membuat ini sedikit lebih mudah.

Jika Anda tahu input dalam UTC , seperti Z(untuk Zulu) di akhir, Instantkelas dapat diuraikan.

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

Jika input Anda mungkin merupakan nilai offset-dari-UTC lain daripada UTC yang ditunjukkan oleh Z(Zulu) di bagian akhir, gunakan OffsetDateTimekelas untuk menguraikan.

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

Kemudian ekstrak Instant, dan konversikan ke java.util.Datedengan menelepon from.

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );
Adam
sumber
8
Jawaban ini bekerja terlalu keras. A java.util.Date menurut definisi tidak memiliki zona waktu. Jadi tidak perlu untuk semua kode terkait zona waktu: LocalDateTimedan ZoneIddan atZone. Satu kalimat sederhana ini akan membantu:java.util.Date date = Date.from( ZonedDateTime.parse( "2014-12-12T10:39:40Z" ).toInstant() );
Basil Bourque
5
@ BasilBourque Ini tidak perlu rumit: Date.from(Instant.parse("2014-12-12T10:39:40Z" ));sudah cukup.
assylias
3
@assylias Anda benar tetapi itu hanya akan berfungsi bila string tanggal adalah zona UTC, ISO8601 memungkinkan zona waktu apa pun ...
Adam
2
@ Adam Adam saya - saya tidak menyadari pertanyaan itu lebih umum daripada contoh Anda. Sebagai komentar sampingan OffsetDateTimeakan cukup untuk mem-parsing ISO8601 (yang tidak mengandung informasi zona waktu tetapi hanya offset).
assylias
1
@assylias Terima kasih atas komentar Anda tentang membiarkan Instantmelakukan parsing. Meskipun tidak cukup untuk Pertanyaan khusus ini, ini adalah perbedaan penting yang perlu ditunjukkan. Jadi saya menambahkan contoh kode kedua. Ups, perhatikan saja ini bukan jawaban saya semula; Saya harap Adam menyetujuinya.
Basil Bourque
67

The perpustakaan Jackson-databind juga memiliki kelas ISO8601DateFormat yang melakukan itu (yang sebenarnya implementasi di ISO8601Utils .

ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");
david_p
sumber
Gagal untuk mengurai tanggal ini: 2015-08-11T13:10:00. Saya mengerti String index out of range: 19. Melihat kode itu tampaknya membutuhkan milidetik untuk ditentukan, dan zona waktu. Itu harus opsional.
Timmmm
2
Mengutip dokumentasi, format parse adalah: [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]]. Dengan kata lain, milidetik bersifat opsional tetapi zona waktu wajib.
david_p
2
Ah ya sebenarnya sepertinya Anda benar. Namun, saya cukup yakin ISO8601 memungkinkan Anda untuk menghilangkan zona waktu sehingga masih salah. JodaTime bekerja:new DateTime("2015-08-11T13:10:00").toDate()
Timmmm
3
Kelas itu sekarang sudah usang, yang baru adalah StdDateFormat. Kalau tidak, ia bekerja sama.
JohnEye
51

tl; dr

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

Menggunakan java.time

Paket java.time baru di Java 8 dan yang lebih baru terinspirasi oleh Joda-Time.

The OffsetDateTimekelas merupakan saat di timeline dengan offset-dari-UTC zona waktu tapi tidak.

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

Memanggil toStringmenghasilkan string dalam format standar ISO 8601:

2010-01-01T12: 00 + 01: 00

Untuk melihat nilai yang sama melalui lensa UTC, ekstrak a Instantatau sesuaikan offset dari +01:00ke 00:00.

Instant instant = odt.toInstant();  

…atau…

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

Sesuaikan menjadi zona waktu jika diinginkan. Sebuah zona waktu adalah sejarah offset-dari-UTC nilai untuk suatu daerah, dengan seperangkat aturan untuk penanganan anomali seperti Daylight Saving Time (DST). Jadi, terapkan zona waktu alih-alih hanya offset jika memungkinkan.

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

Tentang java.time

The java.time kerangka dibangun ke Jawa 8 dan kemudian. Kelas-kelas ini menggantikan tua merepotkan warisan kelas tanggal-waktu seperti java.util.Date, Calendar, & SimpleDateFormat.

Proyek Joda-Time , sekarang dalam mode pemeliharaan , menyarankan migrasi ke kelas java.time .

Untuk mempelajari lebih lanjut, lihat Tutorial Oracle . Dan cari Stack Overflow untuk banyak contoh dan penjelasan. Spesifikasi adalah JSR 310 .

Anda dapat bertukar objek java.time secara langsung dengan database Anda. Gunakan driver JDBC yang sesuai dengan JDBC 4.2 atau yang lebih baru. Tidak perlu untuk string, tidak perlu untuk java.sql.*kelas.

Di mana mendapatkan kelas java.time?

Proyek ThreeTen-Extra memperpanjang java.time dengan kelas tambahan. Proyek ini adalah ajang pembuktian untuk kemungkinan penambahan masa depan ke java.time. Anda mungkin menemukan beberapa kelas berguna di sini seperti Interval, YearWeek, YearQuarter, dan lebih .


Basil Bourque
sumber
27

Untuk Java versi 7

Anda dapat mengikuti dokumentasi Oracle: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X - digunakan untuk zona waktu 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);
d.danailov
sumber
Ini berarti zona waktu diperlukan . Menurut ISO 8601 itu adalah opsional. Seperti detik, dll. Jadi ini hanya mem-parsing subset spesifik ISO 8601.
Timmmm
1
Berfungsi bagus dengan Java 1.8
Thiago Pereira
20

Solusi DatatypeConverter tidak berfungsi di semua VM. Berikut ini berfungsi untuk saya:

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

Saya telah menemukan bahwa joda tidak berfungsi di luar kotak (khusus untuk contoh yang saya berikan di atas dengan zona waktu pada tanggal, yang seharusnya valid)

James Scriven
sumber
15

Saya pikir kita harus menggunakan

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

untuk Date 2010-01-01T12:00:00Z

Toby
sumber
5
Mengapa ini jawaban yang lebih baik daripada yang lain, termasuk jawaban yang diterima dengan 76 upvotes?
Erick Robertson
3
@ErickRobertson: Sederhana, di luar kotak, fleksibel, tidak ada konversi, dan kebanyakan orang tidak peduli dengan zona waktu.
TWiStErRob
7
tidak banyak gunanya bekerja dengan waktu jika Anda tidak peduli tentang zona waktu!
Dori
16
Ini IGNORES zona waktu sepenuhnya. Sedang menggunakan ini sampai saya menyadari ini terjadi, jadi saya beralih ke JodaTime.
Joshua Pinter
3
Membuang zona waktu hanya akan menghasilkan kesalahan di beberapa titik.
Bart van Kuik
11

Mulai dari Java 8, ada cara baru yang didukung secara resmi untuk melakukan ini:

    String s = "2020-02-13T18:51:09.840Z";
    TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(s);
    Instant i = Instant.from(ta);
    Date d = Date.from(i);
Antonio
sumber
2
Jika string dalam format instan, dengan trailing Zsebagai offset, kita tidak perlu menentukan ini secara eksplisit. Adil Instant i = Instant.parse(s);. String dalam pertanyaan itu +01:00, dalam hal DateTimeFormatter.ISO_INSTANTini tidak berfungsi (setidaknya tidak pada Java 11 saya).
Ole VV
3
@ OleV.V. Anda dapat menggunakan ISO_OFFSET_DATE_TIMEuntuk memformat tanggal dengan offset, seperti +01:00( docs.oracle.com/javase/8/docs/api/java/time/format/… )
Lucas Basquerotto
1
Itu benar, @LucasBasquerotto. Meskipun tidak menyebutkan formatter itu secara eksplisit, jawaban oleh Adam dan Basil Bourque sudah melakukan hal serupa.
Ole VV
Ini tidak dapat menguraikan "2020-06-01T14: 34: 00-05: 00" yang merupakan string yang dihasilkan oleh metode toISOString () Javascript.
Jose Solorzano
10

Cara lain yang sangat sederhana untuk mengurai cap waktu ISO8601 adalah dengan menggunakan 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());
  }
}
tmandry
sumber
6

waktu java

Perhatikan bahwa di Java 8, Anda bisa menggunakan kelas java.time.ZonedDateTime dan parse(CharSequence text)metode statisnya .

Martin Rust
sumber
String input dalam Pertanyaan hanya memiliki offset-dari-UTC, bukan zona waktu penuh. Jadi Instantdan ZonedDateTimepantas di sini, tidak ZonedDateTime.
Basil Bourque
6

Solusi untuk Java 7+ menggunakan SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

Kode ini dapat mem-parsing format ISO8601 seperti:

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

Tetapi pada Java6, SimpleDateFormattidak mengerti Xkarakter dan akan melempar.
IllegalArgumentException: Unknown pattern character 'X'
Kita perlu menormalkan tanggal ISO8601 ke format yang dapat dibaca di Java 6 dengan 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);
    }
}

Metode di atas untuk mengganti [ Zdengan +0000] atau [ +01:00dengan +0100] ketika kesalahan terjadi pada Java 6 (Anda dapat mendeteksi versi Java dan mengganti pernyataan try / catch with if).

Khang .NT
sumber
Tidak, kelas tanggal tua yang merepotkan seperti Datedan SimpleDateFormattidak dirancang dengan baik, membingungkan, dan cacat. Mereka sekarang warisan, digantikan oleh kelas java.time dibangun ke dalam Java 8 dan yang lebih baru. Untuk Java 6 dan Java 7, sebagian besar fungsi java.time di-porting dalam proyek ThreeTen-Backport . Jauh lebih baik untuk menambahkan perpustakaan itu ke aplikasi Anda daripada menggunakan kelas-kelas sebelumnya. Solusi satu baris di java.time:OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" )
Basil Bourque
5

Saya menghadapi masalah yang sama dan menyelesaikannya dengan kode berikut.

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

Sebelumnya saya menggunakan SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

Tetapi kemudian saya menemukan penyebab utama pengecualian adalah yyyy-MM-dd'T'HH:mm:ss.SSSZ,

Jadi saya menggunakan

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

Ini bekerja dengan baik untukku .

Abhay Kumar
sumber
Hanya apa yang saya butuhkan tanpa harus menggunakan joda-time, XML api atau apa pun. Pola yang benar.
Philippe Gioseffi
4

Java memiliki selusin cara berbeda untuk mem-parse waktu-waktu, seperti yang ditunjukkan oleh jawaban-jawaban yang bagus di sini. Tetapi agak luar biasa, tidak ada kelas waktu Java sepenuhnya menerapkan ISO 8601!

Dengan Java 8, saya akan merekomendasikan:

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

Itu akan menangani contoh dalam UTC dan dengan offset, seperti "2017-09-13T10: 36: 40Z" atau "2017-09-13T10: 36: 40 + 01: 00". Ini akan dilakukan untuk sebagian besar kasus penggunaan.

Tapi itu tidak akan menangani contoh seperti "2017-09-13T10: 36: 40 + 01", yang merupakan tanggal waktu ISO 8601 yang valid.
Itu juga tidak akan menangani tanggal saja, misalnya "2017-09-13".

Jika Anda harus mengatasinya, saya sarankan menggunakan regex terlebih dahulu untuk mengendus sintaks.

Ada daftar contoh bagus ISO 8601 di sini dengan banyak kotak sudut: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ Saya tidak mengetahui adanya kelas Java yang dapat mengatasi semuanya.

Daniel Winterstein
sumber
OffsetDateTimeakan melakukan dan secara konseptual cocok dengan tanggal-waktu dengan offset yang lebih baik.
Ole VV
Hai @ OleV.V. Terima kasih untuk sarannya. Sayangnya tidak ada: OffsetDateTime.parse () akan mengeluarkan pengecualian untuk beberapa string ISO 8601 yang valid, misalnya "2017-09-13T10: 36: 40 + 01" atau "2017-09-13"
Daniel Winterstein
Saya hanya bermaksud mengatakan bahwa OffsetDateTimemenangani contoh yang Anda tangani ZonedDateTime. Saya percaya bahwa itu tidak menangani salah satu contoh yang ZonedDateTimetidak. Dalam hal itu tidak ada perbaikan (tetapi juga tidak lebih buruk). Maaf, saya tidak begitu jelas.
Ole VV
1
Ini harus menjadi jawaban yang diterima pada tahun 2020, mengingat keadaan.
slashCoder
3

Apache Jackrabbit menggunakan format ISO 8601 untuk tanggal yang tetap, dan ada kelas pembantu untuk menguraikannya:

org.apache.jackrabbit.util.ISO8601

Dilengkapi dengan jackrabbit-jcr-commons .

Alexander Klimetschek
sumber
Sementara subset dari Jackrabbit mungkin berfungsi, lebih masuk akal untuk menggunakan perpustakaan yang dibangun secara penuh. Di Jawa itu berarti Joda-Time atau java.time.
Basil Bourque
3

Seperti yang disebutkan orang lain, Android tidak memiliki cara yang baik untuk mendukung parsing / memformat tanggal ISO 8601 menggunakan kelas yang termasuk dalam SDK. Saya telah menulis kode ini beberapa kali sehingga saya akhirnya membuat Gist yang menyertakan kelas DateUtils yang mendukung pemformatan dan penguraian tanggal ISO 8601 dan RFC 1123. Gist juga mencakup test case yang menunjukkan apa yang didukungnya.

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

Matt Accola
sumber
2

SimpleDateFormat untuk JAVA 1.7 memiliki pola keren untuk format ISO 8601.

Kelas SimpleDateFormat

Inilah yang saya lakukan:

Date d = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
         Locale.ENGLISH).format(System.currentTimeMillis());
Eesha
sumber
2
Zdalam format string bukan zona waktu ISO 8601, Anda harus menggunakan X(atau XXatau XXX) jika Anda menginginkan zona waktu ISO 8601
Vojta
d adalah tipe String
Tim Child
1

Lakukan seperti ini:

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

}

Berikut hasilnya:

Rabu 19 Oktober 15:15:36 CST 2016

Yinhaomin
sumber
1

Gunakan string seperti LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

Stepan
sumber
1

Saya terkejut bahwa tidak satu pun perpustakaan java mendukung semua format tanggal ISO 8601 sesuai https://en.wikipedia.org/wiki/ISO_8601 . Joda DateTime mendukung sebagian besar dari mereka, tetapi tidak semua dan karenanya saya menambahkan logika khusus untuk menangani semuanya. Ini implementasi saya.

import java.text.ParseException;
import java.util.Date;

import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.DateTime;

public class ISO8601DateUtils {
	
	/**
	 * It parses all the date time formats from https://en.wikipedia.org/wiki/ISO_8601 and returns Joda DateTime.
	 * Zoda DateTime does not support dates of format 20190531T160233Z, and hence added custom logic to handle this using SimpleDateFormat.
	 * @param dateTimeString ISO 8601 date time string
	 * @return
	 */
	public static DateTime parse(String dateTimeString) {
		try {
			return new DateTime( dateTimeString );
		} catch(Exception e) {
			try {
				Date dateTime = DateUtils.parseDate(dateTimeString, JODA_NOT_SUPPORTED_ISO_DATES);
				return new DateTime(dateTime.getTime());
			} catch (ParseException e1) {
				throw new RuntimeException(String.format("Date %s could not be parsed to ISO date", dateTimeString));
			}
		}
	}
  
  	private static String[] JODA_NOT_SUPPORTED_ISO_DATES = new String[] {
			// upto millis
			"yyyyMMdd'T'HHmmssSSS'Z'",
			"yyyyMMdd'T'HHmmssSSSZ",
			"yyyyMMdd'T'HHmmssSSSXXX",
			
			"yyyy-MM-dd'T'HHmmssSSS'Z'",
			"yyyy-MM-dd'T'HHmmssSSSZ",
			"yyyy-MM-dd'T'HHmmssSSSXXX",
			
			// upto seconds
			"yyyyMMdd'T'HHmmss'Z'",
			"yyyyMMdd'T'HHmmssZ",
			"yyyyMMdd'T'HHmmssXXX",
			
			"yyyy-MM-dd'T'HHmmss'Z'", 
			"yyyy-MM-dd'T'HHmmssZ",
			"yyyy-MM-dd'T'HHmmssXXX",
			
			// upto minutes
			"yyyyMMdd'T'HHmm'Z'",
			"yyyyMMdd'T'HHmmZ",
			"yyyyMMdd'T'HHmmXXX",

			"yyyy-MM-dd'T'HHmm'Z'",
			"yyyy-MM-dd'T'HHmmZ",
			"yyyy-MM-dd'T'HHmmXXX",
			
			//upto hours is already supported by Joda DateTime
	};
}

raok1997
sumber
1

Sebuah tes kecil yang menunjukkan cara mem-parsing tanggal di ISO8601 dan bahwa LocalDateTime tidak menangani DST.

 @Test
    public void shouldHandleDaylightSavingTimes() throws ParseException {

        //ISO8601 UTC date format
        SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

        // 1 hour of difference between 2 dates in UTC happening at the Daylight Saving Time
        Date d1 = utcFormat.parse("2019-10-27T00:30:00.000Z");
        Date d2 = utcFormat.parse("2019-10-27T01:30:00.000Z");

        //Date 2 is before date 2
        Assert.assertTrue(d1.getTime() < d2.getTime());
        // And there is 1 hour difference between the 2 dates
        Assert.assertEquals(1000*60*60, d2.getTime() - d1.getTime());

        //Print the dates in local time
        SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm z Z", Locale.forLanguageTag("fr_CH"));
        localFormat.setTimeZone(TimeZone.getTimeZone("Europe/Zurich"));

        //Both dates are at 02h30 local time (because of DST), but one is CEST +0200 and the other CET +0100 (clock goes backwards)
        Assert.assertEquals("2019-10-27 02:30 CEST +0200", localFormat.format(d1));
        Assert.assertEquals("2019-10-27 02:30 CET +0100", localFormat.format(d2));

        //Small test that shows that LocalDateTime does not handle DST (and should not be used for storing timeseries data)
        LocalDateTime ld1 = LocalDateTime.ofInstant(d1.toInstant(), ZoneId.of("Europe/Zurich"));
        LocalDateTime ld2 = LocalDateTime.ofInstant(d2.toInstant(), ZoneId.of("Europe/Zurich"));

        //Note that a localdatetime does not handle DST, therefore the 2 dates are the same
        Assert.assertEquals(ld1, ld2);

        //They both have the following local values
        Assert.assertEquals(2019, ld1.getYear());
        Assert.assertEquals(27, ld1.getDayOfMonth());
        Assert.assertEquals(10, ld1.getMonthValue());
        Assert.assertEquals(2, ld1.getHour());
        Assert.assertEquals(30, ld1.getMinute());
        Assert.assertEquals(0, ld1.getSecond());

    }
ddtxra
sumber
3
FYI, kelas tanggal-waktu yang sangat merepotkan seperti java.util.Date, java.util.Calendar, dan java.text.SimpleDateFormatsekarang warisan , digantikan oleh java.time kelas dibangun ke Jawa 8 dan kemudian. Lihat Tutorial oleh Oracle .
Basil Bourque
Anda benar yang LocalDateTimetidak menangani waktu musim panas (DST) karena tidak menangani zona waktu sama sekali. Untuk itu kita perlu ZonedDateTime. Mengusulkan Datedan SimpleDateFormat- IMHO buruk.
Ole VV
1
Memang ZonedDateTime berfungsi. Dan java.time.Instant juga merupakan alternatif yang baik untuk menangani DST. Saya tahu bahwa java.util.Date sudah usang dan tidak boleh digunakan, tapi saya baru saja menjawab pertanyaan asli: Bagaimana mengkonversi sebuah String di 8601 ke java.util.date ....
ddtxra
0

Saya memiliki kebutuhan yang sama: Saya harus dapat menguraikan tanggal yang sesuai ISO8601 tanpa mengetahui format yang tepat di muka, dan saya ingin solusi ringan yang juga akan bekerja di Android.

Ketika saya mencari di Google kebutuhan saya, saya menemukan pertanyaan ini, dan memperhatikan bahwa AFAIU, tidak ada jawaban yang sepenuhnya sesuai dengan kebutuhan saya. Jadi saya mengembangkan jISO8601 dan mendorongnya ke pusat maven.

Cukup tambahkan Anda pom.xml:

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

dan kemudian Anda baik untuk pergi:

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

Semoga bisa membantu.

gturri
sumber
-1

Kesopanan Fungsi Dasar: @wrygiel.

Fungsi ini dapat mengkonversi format ISO8601 ke Tanggal Java yang dapat menangani nilai offset. Sesuai definisi ISO 8601 , offset dapat disebutkan dalam format yang berbeda.

±[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

Kelas ini memiliki metode statis untuk dikonversi

  • String ISO8601 ke objek Date (Local TimeZone)
  • Tanggal untuk string ISO8601
  • Penghematan Siang Hari secara otomatis dihitung

Contoh String ISO8601

/*       "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;
}

}

AKh
sumber
2
Awas - DateFormat dan kelas turunannya tidak kompatibel multithread! Menggunakan objek SimpleDateFormat statis seperti DATE_FORMAT_1 dan DATE_FORMAT_2 berarti bahwa banyak utas yang memanggil fungsi ISO8601DateFormatter akan berbagi objek DateFormat yang sama. Ini mengarah pada korupsi data dan tanggal yang salah dikembalikan dari panggilan DateFormat. Untuk memperbaikinya, Anda harus membuat konstanta string pola dan membuat variabel SimpleDateFormat lokal kapan pun diperlukan. Ini akan memastikan bahwa setiap objek hanya digunakan oleh satu utas.
Theo
Perbaikan yang lebih baik untuk keamanan utas adalah dengan menggunakan pustaka waktu-waktu yang dibangun untuk keamanan utas. Di Jawa dunia itu bisa berupa Joda-Time atau java.time.
Basil Bourque
-1

Ini sepertinya bekerja paling baik untuk saya:

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


}

Saya perlu mengonversi string tanggal ke / dari JavaScript ke Java. Saya menemukan karya-karya di atas dengan rekomendasi. Ada beberapa contoh menggunakan SimpleDateFormat yang dekat tetapi mereka tampaknya bukan himpunan bagian seperti yang direkomendasikan oleh:

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

dan didukung oleh PLIST dan JavaScript Strings dan semacam itulah yang saya butuhkan.

Ini tampaknya merupakan bentuk paling umum dari string ISO8601 di luar sana, dan bagian yang baik.

Contoh yang mereka berikan adalah:

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.

Saya juga memiliki versi cepat:

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

Saya belum membandingkannya, tapi saya kira ini akan cukup cepat. Sepertinya berhasil. :)

@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);
}
RickHigh
sumber
-2

Saya pikir apa yang banyak orang ingin lakukan adalah mengurai string tanggal JSON. Ada kemungkinan besar jika Anda mengunjungi halaman ini bahwa Anda mungkin ingin mengonversi tanggal JSON JavaScript ke tanggal Java.

Untuk menampilkan seperti apa string tanggal 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)

String tanggal JSON adalah 2013-12-14T01: 55: 33.412Z.

Tanggal tidak dicakup oleh spesifikasi JSON per katakan, tetapi di atas adalah format ISO 8601 yang sangat spesifik, sedangkan ISO_8601 jauh lebih besar dan itu hanyalah sebagian kecil meskipun sangat penting.

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

Seperti yang terjadi saya menulis parser JSON dan parser PLIST yang keduanya menggunakan ISO-8601 tetapi tidak bit yang sama.

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

Saya menulis dua cara untuk melakukan ini untuk proyek saya. Satu standar, satu cepat.

Sekali lagi, string tanggal JSON adalah implementasi ISO 8601 yang sangat spesifik ....

(Saya memposting yang lain di jawaban lain yang harus bekerja untuk tanggal PLIST, yang merupakan format ISO 8601 yang berbeda).

Tanggal JSON adalah sebagai berikut:

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


}

File PLIST (ASCII non GNUNext) juga menggunakan ISO 8601 tetapi tidak ada milidetik jadi ... tidak semua tanggal ISO-8601 adalah sama. (Setidaknya saya belum menemukan yang menggunakan milis dan parser yang saya lihat melewati zona waktu sama sekali OMG).

Sekarang untuk versi cepat (Anda dapat menemukannya di Boon).

public static Date fromJsonDate( String string ) {

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

}

Perhatikan bahwa Reflection.toCharArray menggunakan tidak aman jika tersedia tetapi default ke string.toCharArray jika tidak.

(Anda bisa mengeluarkannya dengan mengganti Reflection.toCharArray (string) dengan 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 diimplementasikan sebagai berikut:

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

Ngomong-ngomong ... tebakan saya adalah bahwa beberapa orang yang datang ke sini .. mungkin mencari JSON Date String dan meskipun ini adalah tanggal ISO-8601, itu adalah yang sangat spesifik yang membutuhkan penguraian yang sangat spesifik.

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

Lihat https://github.com/RichardHightower/boon Boon memiliki pengurai PLIST (ASCII) dan pengurai JSON.

Pengurai JSON adalah pengurai Java JSON tercepat yang saya tahu.

Diverifikasi secara independen oleh dudes Kinerja Gatling.

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

Ini memiliki parser JSON tercepat untuk stream, pembaca, byte [], char [], CharSequence (StringBuilder, CharacterBuffer), dan String.

Lihat lebih banyak tolok ukur di:

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

RickHigh
sumber
Jawaban tentang JSON ini di luar topik dari Pertanyaan. Selain itu, Pertanyaan ini salah karena tidak ada yang disebut "tanggal JSON" di antara beberapa tipe data JSON . Dan saat ini, semua kode ini dapat diganti dengan panggilan satu baris ke fitur Java bawaan:Instant.parse( "2013-12-14T01:55:33.412Z" )
Basil Bourque