อักขระรูปแบบไม่ถูกต้อง 'T' เมื่อวิเคราะห์สตริงวันที่ไปยัง java.util.Date


182

ฉันมีสตริงวันที่และฉันต้องการแยกเป็นวันที่ปกติให้ใช้ Java Date API ต่อไปนี้คือรหัสของฉัน:

public static void main(String[] args) {
    String date="2010-10-02T12:23:23Z";
    String pattern="yyyy-MM-ddThh:mm:ssZ";
    SimpleDateFormat sdf=new SimpleDateFormat(pattern);
    try {
        Date d=sdf.parse(date);
        System.out.println(d.getYear());
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

อย่างไรก็ตามฉันได้รับข้อยกเว้น: java.lang.IllegalArgumentException: Illegal pattern character 'T'

ดังนั้นฉันสงสัยว่าฉันต้องแยกสตริงและแยกมันด้วยตนเองหรือไม่

BTW ฉันพยายามเพิ่มอักขระเครื่องหมายคำพูดเดี่ยวที่ด้านใดด้านหนึ่งของ T:

String pattern="yyyy-MM-dd'T'hh:mm:ssZ";

มันยังไม่ทำงาน


1
คุณรู้หรือไม่ว่า "T" นี้หมายถึงอะไร เท่าที่ฉันคิดว่าวันที่ทั้งหมดพร้อมกับ 'T' ควรถูกแปลงเป็นวันที่ (ไม่ข้ามผ่าน 'Single Quotes' แต่สิ่งที่เราขาดหายไปคือรูปแบบการวิเคราะห์
coding_idiot

9
Tหมายถึงเวลาและเป็นส่วนหนึ่งของISO8601มาตรฐานสำหรับการจัดรูปแบบวันเวลาต่างประเทศ

1
มันแปลกมาก การห่อTเครื่องหมายอัญประกาศเดี่ยวทำงานให้ฉัน (อย่างน้อยก็ในแง่ของการแก้ไขข้อผิดพลาดในการแยกวิเคราะห์)
Agi Hammerthief

คำตอบ:


203

อัปเดตสำหรับ Java 8 และสูงกว่า

ตอนนี้คุณสามารถทำได้Instant.parse("2015-04-28T14:23:38.521Z")และได้รับสิ่งที่ถูกต้องในตอนนี้โดยเฉพาะอย่างยิ่งเนื่องจากคุณควรจะใช้Instantแทนการใช้งานไม่ได้java.util.Dateกับ Java เวอร์ชันล่าสุด

คุณควรใช้DateTimeFormatterแทนSimpleDateFormatterเช่นกัน

คำตอบเดิม:

คำอธิบายด้านล่างยังคงใช้ได้ตามรูปแบบที่ใช้ แต่มันถูกเขียนก่อนที่ Java 8 จะแพร่หลายดังนั้นจึงใช้คลาสเก่าที่คุณไม่ควรใช้หากคุณใช้ Java 8 หรือสูงกว่า

สิ่งนี้ใช้ได้กับอินพุตที่มีส่วนท้ายZดังแสดง:

ในรูปแบบที่Tมีการหลบหนีด้วย'ทั้งสองด้าน

รูปแบบสำหรับZจุดสิ้นสุดเป็นจริงXXXตามเอกสารใน JavaDoc สำหรับSimpleDateFormatมันไม่ชัดเจนมากเกี่ยวกับวิธีการใช้งานจริง ๆ เนื่องจากZเป็นเครื่องหมายสำหรับTimeZoneข้อมูลเก่า เช่นกัน

Q2597083.java

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

public class Q2597083
{
    /**
     * All Dates are normalized to UTC, it is up the client code to convert to the appropriate TimeZone.
     */
    public static final TimeZone UTC;

    /**
     * @see <a href="http://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations">Combined Date and Time Representations</a>
     */
    public static final String ISO_8601_24H_FULL_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";

    /**
     * 0001-01-01T00:00:00.000Z
     */
    public static final Date BEGINNING_OF_TIME;

    /**
     * 292278994-08-17T07:12:55.807Z
     */
    public static final Date END_OF_TIME;

    static
    {
        UTC = TimeZone.getTimeZone("UTC");
        TimeZone.setDefault(UTC);
        final Calendar c = new GregorianCalendar(UTC);
        c.set(1, 0, 1, 0, 0, 0);
        c.set(Calendar.MILLISECOND, 0);
        BEGINNING_OF_TIME = c.getTime();
        c.setTime(new Date(Long.MAX_VALUE));
        END_OF_TIME = c.getTime();
    }

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

        final SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_24H_FULL_FORMAT);
        sdf.setTimeZone(UTC);
        System.out.println("sdf.format(BEGINNING_OF_TIME) = " + sdf.format(BEGINNING_OF_TIME));
        System.out.println("sdf.format(END_OF_TIME) = " + sdf.format(END_OF_TIME));
        System.out.println("sdf.format(new Date()) = " + sdf.format(new Date()));
        System.out.println("sdf.parse(\"2015-04-28T14:23:38.521Z\") = " + sdf.parse("2015-04-28T14:23:38.521Z"));
        System.out.println("sdf.parse(\"0001-01-01T00:00:00.000Z\") = " + sdf.parse("0001-01-01T00:00:00.000Z"));
        System.out.println("sdf.parse(\"292278994-08-17T07:12:55.807Z\") = " + sdf.parse("292278994-08-17T07:12:55.807Z"));
    }
}

สร้างเอาต์พุตต่อไปนี้:

sdf.format(BEGINNING_OF_TIME) = 0001-01-01T00:00:00.000Z
sdf.format(END_OF_TIME) = 292278994-08-17T07:12:55.807Z
sdf.format(new Date()) = 2015-04-28T14:38:25.956Z
sdf.parse("2015-04-28T14:23:38.521Z") = Tue Apr 28 14:23:38 UTC 2015
sdf.parse("0001-01-01T00:00:00.000Z") = Sat Jan 01 00:00:00 UTC 1
sdf.parse("292278994-08-17T07:12:55.807Z") = Sun Aug 17 07:12:55 UTC 292278994

26

TL; DR

ใช้java.time.Instantคลาสเพื่อแยกวิเคราะห์ข้อความในรูปแบบ ISO 8601 มาตรฐานซึ่งแสดงถึงช่วงเวลาใน UTC

Instant.parse( "2010-10-02T12:23:23Z" )

ISO 8601

รูปแบบนั้นถูกกำหนดโดยมาตรฐาน ISO 8601สำหรับรูปแบบสตริงวันที่และเวลา

ทั้งสอง:

…ใช้รูปแบบ ISO 8601 โดยค่าเริ่มต้นสำหรับการแยกและสร้างสตริง

โดยทั่วไปคุณควรหลีกเลี่ยงการใช้คลาสjava.util.Date /.Calendar & java.text.SimpleDateFormat อันเก่าเนื่องจากมีปัญหายุ่งยากสับสนและมีข้อบกพร่อง หากจำเป็นสำหรับการทำงานร่วมกันคุณสามารถแปลงไปมาได้

java.time

สร้างขึ้นใน Java 8 และใหม่กว่าเป็นเฟรมเวิร์กjava.timeใหม่ แรงบันดาลใจจากJoda เวลาที่กำหนดโดยJSR 310และขยายโดยThreeTen-พิเศษโครงการ

Instant instant = Instant.parse( "2010-10-02T12:23:23Z" );  // `Instant` is always in UTC.

แปลงเป็นคลาสเก่า

java.util.Date date = java.util.Date.from( instant );  // Pass an `Instant` to the `from` method.

เขตเวลา

หากจำเป็นคุณสามารถกำหนดเขตเวลาได้

ZoneId zoneId = ZoneId.of( "America/Montreal" ); // Define a time zone rather than rely implicitly on JVM’s current default time zone.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );  // Assign a time zone adjustment from UTC.

แปลง.

java.util.Date date = java.util.Date.from( zdt.toInstant() );  // Extract an `Instant` from the `ZonedDateTime` to pass to the `from` method.

Joda เวลา

UPDATE: โครงการ Joda-Time อยู่ในโหมดบำรุงรักษา ทีมให้คำแนะนำการโยกย้ายไปยังคลาสjava.time

นี่คือตัวอย่างโค้ดใน Joda-Time 2.8

org.joda.time.DateTime dateTime_Utc = new DateTime( "2010-10-02T12:23:23Z" , DateTimeZone.UTC );  // Specifying a time zone to apply, rather than implicitly assigning the JVM’s current default.

แปลงเป็นคลาสเก่า โปรดทราบว่าเขตเวลาที่กำหนดจะสูญหายไปในการแปลงเนื่องจาก juDate ไม่สามารถกำหนดเขตเวลาได้

java.util.Date date = dateTime_Utc.toDate(); // The `toDate` method converts to old class.

เขตเวลา

หากจำเป็นคุณสามารถกำหนดเขตเวลาได้

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTime_Montreal = dateTime_Utc.withZone ( zone );

ตารางประเภทวันที่ - เวลาใน Java ทั้งทันสมัยและดั้งเดิม


เกี่ยวกับjava.time

java.timeกรอบถูกสร้างขึ้นใน Java 8 และต่อมา ชั้นเรียนเหล่านี้แย่งลำบากเก่ามรดกเรียนวันที่เวลาเช่นjava.util.Date, และCalendarSimpleDateFormat

Joda เวลาโครงการขณะนี้อยู่ในโหมดการบำรุงรักษาให้คำแนะนำแก่การโยกย้ายไปยังjava.timeชั้นเรียน

ต้องการเรียนรู้เพิ่มเติมโปรดดูที่ออราเคิลกวดวิชา และค้นหา Stack Overflow สำหรับตัวอย่างและคำอธิบายมากมาย สเปกJSR 310

คุณสามารถแลกเปลี่ยนวัตถุjava.timeโดยตรงกับฐานข้อมูลของคุณ ใช้ไดรเวอร์ JDBC ที่สอดคล้องกับJDBC 4.2หรือใหม่กว่า ไม่จำเป็นต้องใช้สตริงไม่จำเป็นต้องมีjava.sql.*คลาส

จะรับคลาส java.time ได้ที่ไหน?

  • Java SE 8 , Java SE 9และใหม่กว่า
    • Built-in
    • เป็นส่วนหนึ่งของ Java API มาตรฐานที่มีการใช้งานแบบรวม
    • Java 9 เพิ่มคุณสมบัติและการแก้ไขเล็กน้อย
  • Java SE 6และ Java SE 7
    • มากของการทำงาน java.time จะกลับรังเพลิง Java 6 และ 7 ในThreeTen-ย้ายกลับ
  • Android
    • การใช้งานชุดบันเดิล Android รุ่นต่อมาของคลาส java.time
    • สำหรับก่อนหน้านี้ Android (<26) ที่ThreeTenABPโครงการปรับThreeTen-ย้ายกลับ (ดังกล่าวข้างต้น) ดูวิธีการใช้ ThreeTenABP … .

ตารางที่ไลบรารี java.time ใดที่จะใช้กับเวอร์ชันของ Java หรือ Android

โครงการThreeTen-Extraขยาย java.time ด้วยคลาสเพิ่มเติม โครงการนี้เป็นพื้นฐานที่พิสูจน์ได้สำหรับการเพิ่มเติมในอนาคตไปยัง java.time คุณอาจพบว่าการเรียนที่มีประโยชน์บางอย่างที่นี่เช่นInterval, YearWeek, YearQuarterและอื่น ๆ อีกมากมาย


1
@AalapPatel โปรดทราบว่าขณะนี้โครงการ Joda-Time อยู่ในโหมดบำรุงรักษา ทีมให้คำแนะนำการโยกย้ายไปยังคลาส java.time ความพยายามทั้งสองนำโดยสตีเฟ่นโคลบอร์นชายคนเดียวกัน
Basil Bourque

ขอบคุณฉันใช้เพื่อรับ miliseconds จากเซิร์ฟเวอร์คืนเวลา UTC และ / หรือเวลา locale และทำงานได้ดีสำหรับฉันในกรณีนี้ ..
Aalap Patel

Instant.parse ต้องการขั้นต่ำระดับ api 26 ใน Android
Naveed Ahmad

1
@NaveedAhmad ดูกระสุนที่ด้านล่างของคำตอบเกี่ยวกับThreeTen-ย้ายกลับและThreeTenABPโครงการ
Basil Bourque

0

มีสองคำตอบด้านบนเป็นปัจจุบันและมีความยาว (และtl; dr IMHO สั้นเกินไป) ดังนั้นฉันจึงเขียนบทสรุปจากประสบการณ์ของฉันเริ่มที่จะใช้ไลบรารีjava.timeใหม่(ใช้ได้ตามที่ระบุไว้ในคำตอบอื่น ๆ กับเวอร์ชัน Java 8+) ISO 8601กำหนดวิธีมาตรฐานในการเขียนวันที่: YYYY-MM-DDดังนั้นรูปแบบของวันที่และเวลาเป็นดังต่อไปนี้เท่านั้น (อาจเป็น 0, 3, 6 หรือ 9 หลักสำหรับมิลลิวินาที) และไม่จำเป็นต้องใช้สตริงการจัดรูปแบบ:

import java.time.Instant;
public static void main(String[] args) {
    String date="2010-10-02T12:23:23Z";
    try {
        Instant myDate = Instant.parse(date);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

ฉันไม่ต้องการ แต่เนื่องจากการรับปีอยู่ในรหัสจากคำถามจากนั้น:
มันเป็นเรื่องที่ยุ่งยากไม่สามารถทำได้Instantโดยตรงสามารถทำได้ผ่านCalendarทางคำถามรับค่าจำนวนเต็มของปีปัจจุบันใน Javaและการแปลง Java ใช้เวลาในการปฏิทินแต่ IMHO เป็นรูปแบบสตริงย่อยคงที่ง่ายต่อการใช้:

myDate.toString().substring(0,4);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.