Ký tự mẫu bất hợp pháp 'T' khi phân tích chuỗi ngày thành java.util.Date


182

Tôi có một chuỗi ngày và tôi muốn phân tích nó thành ngày bình thường bằng cách sử dụng API ngày java, sau đây là mã của tôi:

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

Tuy nhiên tôi có một ngoại lệ: java.lang.IllegalArgumentException: Illegal pattern character 'T'

Vì vậy, tôi tự hỏi nếu tôi phải tách chuỗi và phân tích cú pháp bằng tay?

BTW, tôi đã cố gắng thêm một ký tự trích dẫn ở hai bên của T:

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

Nó cũng không hoạt động.


1
Bạn có biết chữ "T" này có nghĩa là gì không? Theo như tôi nghĩ, toàn bộ ngày cùng với 'T' nên được chuyển đổi thành ngày (không bỏ qua '
Dấu ngoặc

9
Các Tđại diện cho thời gian và là một phần của ISO8601 tiêu chuẩn cho thời gian ngày quốc tế định dạng.

1
Thật ki quặc. Gói các Ttrích dẫn duy nhất làm việc cho tôi (ít nhất là về mặt giải quyết lỗi phân tích cú pháp).
Búa búa Agi

Câu trả lời:


203

Cập nhật cho Java 8 trở lên

Bây giờ bạn có thể chỉ cần làm Instant.parse("2015-04-28T14:23:38.521Z")và nhận được những điều đúng bây giờ, đặc biệt là kể từ khi bạn nên sử dụng Instantthay vì bị phá vỡ java.util.Datevới hầu hết các phiên bản gần đây của Java.

Bạn nên sử dụng DateTimeFormatterthay vì SimpleDateFormatterlà tốt.

Câu trả lời gốc:

Giải thích dưới đây vẫn hợp lệ như những gì định dạng đại diện. Nhưng nó đã được viết trước khi Java 8 có mặt ở khắp nơi nên nó sử dụng các lớp cũ mà bạn không nên sử dụng nếu bạn đang sử dụng Java 8 trở lên.

Điều này hoạt động với đầu vào với dấu Znhư đã trình bày:

Trong mô hình Tđược thoát với 'hai bên.

Mẫu Zcuối cùng thực sự XXXnhư được ghi lại trong JavaDoc SimpleDateFormat, nó thực sự không rõ ràng về cách sử dụng nó vì nó Zcũng là điểm đánh dấu cho TimeZonethông tin cũ .

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

Tạo đầu ra sau:

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

Sử dụng java.time.Instantlớp để phân tích văn bản theo định dạng ISO 8601 tiêu chuẩn, thể hiện một khoảnh khắc trong UTC.

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

ISO 8601

Định dạng đó được xác định theo tiêu chuẩn ISO 8601 cho các định dạng chuỗi thời gian.

Cả hai:

Theo mặc định, sử dụng các định dạng ISO 8601 để phân tích cú pháp và tạo chuỗi.

Nói chung, bạn nên tránh sử dụng các lớp java.util.Date /.CalWiki & java.text.SimpleDateFormat cũ vì chúng rất rắc rối, khó hiểu và thiếu sót. Nếu cần để tương tác, bạn có thể chuyển đổi qua lại.

java.time

Được tích hợp vào Java 8 trở lên là khung công tác java.time mới . Lấy cảm hứng từ Joda-Time , được định nghĩa bởi JSR 310 và được mở rộng bởi dự án ThreeTen-Extra .

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

Chuyển sang lớp cũ.

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

Múi giờ

Nếu cần, bạn có thể chỉ định múi giờ.

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.

Đổi.

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

Joda-Thời gian

CẬP NHẬT: Dự án Joda-Time hiện đang ở chế độ bảo trì. Nhóm tư vấn di chuyển đến các lớp java.time .

Dưới đây là một số mã ví dụ trong 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.

Chuyển sang lớp cũ. Lưu ý rằng múi giờ được chỉ định bị mất khi chuyển đổi, vì juDate không thể được chỉ định múi giờ.

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

Múi giờ

Nếu cần, bạn có thể chỉ định múi giờ.

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

Bảng các loại thời gian trong Java, cả hiện đại và di sản.


Giới thiệu về java.time

Các java.time khung được xây dựng vào Java 8 và sau đó. Những lớp học thay thế cái cũ phiền hà di sản lớp học ngày thời gian như java.util.Date, Calendar, & SimpleDateFormat.

Các Joda thời gian dự án, bây giờ trong chế độ bảo trì , khuyên chuyển đổi sang các java.time lớp.

Để tìm hiểu thêm, xem Hướng dẫn Oracle . Và tìm kiếm Stack Overflow cho nhiều ví dụ và giải thích. Đặc điểm kỹ thuật là JSR 310 .

Bạn có thể trao đổi các đối tượng java.time trực tiếp với cơ sở dữ liệu của bạn. Sử dụng trình điều khiển JDBC tương thích với JDBC 4.2 trở lên. Không cần chuỗi, không cần java.sql.*lớp.

Nơi để có được các lớp java.time?

  • Java SE 8 , Java SE 9 và mới hơn
    • Được xây dựng trong.
    • Một phần của API Java tiêu chuẩn với việc triển khai đi kèm.
    • Java 9 thêm một số tính năng nhỏ và sửa lỗi.
  • Java SE 6 Java SE 7
    • Phần lớn chức năng java.time được chuyển ngược lại sang Java 6 & 7 trong ThreeTen-Backport .
  • Android
    • Các phiên bản sau của triển khai gói Android của các lớp java.time.
    • Đối với Android trước đó (<26), các ThreeTenABP dự án thích nghi ThreeTen-backport (nêu trên). Xem Cách sử dụng ThreeTenABP .

Bảng mà thư viện java.time sẽ sử dụng với phiên bản Java hoặc Android nào.

Các ThreeTen-Extra dự án mở rộng java.time với các lớp bổ sung. Dự án này là một nền tảng chứng minh cho các bổ sung có thể trong tương lai cho java.time. Bạn có thể tìm thấy một số các lớp học hữu ích ở đây chẳng hạn như Interval, YearWeek, YearQuarter, và nhiều hơn nữa .


1
@AalapPatel Hãy lưu ý rằng dự án Joda-Time hiện đang ở chế độ bảo trì. Nhóm tư vấn di chuyển đến các lớp java.time. Cả hai nỗ lực được dẫn dắt bởi cùng một người đàn ông, Stephen Colebourne.
Basil Bourque

Cảm ơn bạn, tôi sử dụng nó để nhận được milisecond từ máy chủ trả về thời gian UTC và / hoặc thời gian địa phương và hoạt động tốt cho tôi trong trường hợp này ..
Aalap Patel

Instant.parse cần tối thiểu api cấp 26 trong Android
Naveed Ahmad

1
@NaveedAhmad Xem các viên đạn ở cuối Câu trả lời về các dự án ThreeTen-BackportThreeTenABP .
Basil Bourque

0

Có hai câu trả lời cho đến nay và chúng đều dài (và tl; dr quá ngắn IMHO), vì vậy tôi viết tóm tắt từ kinh nghiệm của tôi bắt đầu sử dụng thư viện java.time mới (áp dụng như đã nêu trong các câu trả lời khác cho phiên bản Java 8+). ISO 8601 thiết lập cách tiêu chuẩn để viết ngày: YYYY-MM-DDvì vậy định dạng ngày giờ chỉ như bên dưới (có thể là 0, 3, 6 hoặc 9 chữ số cho mili giây) và không cần chuỗi định dạng:

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

Tôi không cần nó, nhưng khi nhận được năm từ mã từ câu hỏi, thì
nó khó hơn, không thể thực hiện Instanttrực tiếp, có thể được thực hiện thông qua Calendarcách đặt câu hỏi Lấy giá trị nguyên của năm hiện tại trong JavaChuyển đổi java. thời gian để Lịch nhưng định dạng IMHO là chuỗi con cố định dễ sử dụng hơn:

myDate.toString().substring(0,4);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.