2013-03-11 86 views
27

Tôi có ngày ở định dạng chuỗi và tôi muốn phân tích cú pháp đó thành ngày sử dụng.Tại sao SimpleDateFormat phân tích cú pháp ngày không chính xác?

var date ="03/11/2013" 

Tôi phân tích này như:

new SimpleDateFormat("MM/dd/yyyy").parse(date) 

Nhưng điều lạ là, nếu tôi đi qua "03-08- 201.309 hjhkjhk" hoặc "03- -2013 "hoặc -88-201378", nó không ném lỗi, nó phân tích cú pháp nó.

Hiện tại, tôi phải viết mẫu regex để kiểm tra đầu vào của ngày tháng là chính xác hoặc không phải. nhưng tại sao lại như vậy ??

Code:

scala> val date="03/88/201309 hjhkjhk" 
date: java.lang.String = 03/88/201309 hjhkjhk 

scala> new SimpleDateFormat("MM/dd/yyyy").parse(date) 
res5: java.util.Date = Mon May 27 00:00:00 IST 201309 
+3

'var date'? Tôi không nghĩ như vậy, sao chép mã thực sự của bạn vào câu hỏi xin vui lòng. – Perception

+0

'var date' không phải là Java. –

+1

@Lutz Horn. Tôi đang sử dụng scala – Rishi

Trả lời

48

Bạn nên sử dụng DateFormat.setLenient(false):

SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy"); 
df.setLenient(false); 
df.parse("03/88/2013"); // Throws an exception 

Tôi không chắc rằng sẽ bắt mọi thứ bạn muốn - tôi dường như nhớ rằng ngay cả với setLenient(false) đó là khoan dung hơn hơn bạn có thể mong đợi - nhưng nó sẽ bắt số tháng không hợp lệ chẳng hạn.

Tôi không nghĩ rằng nó sẽ bắt được văn bản đuôi, ví dụ: "03/01/2013 sjsjsj". Bạn có khả năng có thể sử dụng sự quá tải của parse mà chấp nhận một ParsePosition, sau đó kiểm tra các chỉ số phân tích hiện tại sau khi phân tích cú pháp đã hoàn thành:

ParsePosition position = new ParsePosition(0); 
Date date = dateFormat.parse(text, position); 
if (position.getIndex() != text.length()) { 
    // Throw an exception or whatever else you want to do 
} 

Bạn cũng nên nhìn vào Joda Time API mà cũng có thể cho phép một sự giải thích chặt chẽ hơn - và là một API ngày/giờ thường sạch hơn.

+1

df.parse ("03/08/2013xskhs"); Không có lỗi ở đây. – Rishi

+0

@Rishi: Vâng, tôi đã chỉnh sửa câu trả lời của tôi để bao gồm phần đó, cùng với các đề xuất khác. (Bây giờ tôi đã chỉnh sửa nó hơn nữa.) –

+0

Nếu bạn muốn bắt văn bản sau đó bạn cần sử dụng phiên bản 'phân tích cú pháp (chuỗi văn bản, ParsePosition pos)' của phương pháp phân tích cú pháp như sau đó bạn có thể kiểm tra xem tất cả đầu vào đã được tiêu thụ chưa. –

3

Jon Skeet’s answer là chính xác và là một câu trả lời tốt khi nó được viết vào năm 2013.

Tuy nhiên, các lớp bạn sử dụng trong câu hỏi của bạn, SimpleDateFormatDate, bây giờ là lỗi thời gian dài, vì vậy nếu một người nào đó có một vấn đề tương tự với chúng ngày hôm nay, IMHO câu trả lời tốt nhất sẽ là thay đổi để sử dụng the modern Java date & time API.

Tôi xin lỗi tôi không thể viết mã Scala, vì vậy bạn sẽ phải sống với Java. Tôi đang sử dụng

private static DateTimeFormatter parseFormatter 
     = DateTimeFormatter.ofPattern("MM/dd/yyyy"); 

Ký tự mẫu định dạng giống như câu hỏi của bạn, mặc dù ý nghĩa hơi khác. DateTimeFormatter nhận số chữ hoa văn theo nghĩa đen, như chúng ta sẽ thấy. Bây giờ chúng ta thử:

 System.out.println(LocalDate.parse(date, parseFormatter)); 

Kết quả:

  • "03/11/2013" được phân tách thành 2013-03-11 như mong đợi. Tôi đã sử dụng lớp LocalDate hiện đại, một lớp đại diện cho một ngày không có thời gian trong ngày, chính xác những gì chúng tôi cần ở đây.
  • Chuyển "03/88/2013 hjhkjhk" cho số DateTimeParseException với thông báo Text '03/88/2013 hjhkjhk' could not be parsed, unparsed text found at index 10. Khá chính xác, phải không?API hiện đại có phương pháp phân tích cú pháp chỉ một phần của chuỗi nếu đó là những gì chúng tôi muốn.
  • "03/88/201309" cho Text '03/88/201309' could not be parsed at index 6. Chúng tôi đã yêu cầu một năm có 4 chữ số và cho nó 6 chữ số, dẫn đến phản đối. Dường như nó phát hiện và báo cáo lỗi này trước khi cố gắng diễn dịch 88 như một ngày trong tháng.
  • Nó cũng phản đối một ngày trong tháng là 88, mặc dù: "03/88/2013" cung cấp Text '03/88/2013' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 88. Một lần nữa, hãy tận hưởng thông điệp như thế nào.
  • "03-08-2013" (có dấu gạch ngang thay vì dấu gạch chéo) cho số Text '03-08-2013' could not be parsed at index 2, không quá ngạc nhiên. Chỉ số 2 là nơi dấu gạch nối đầu tiên là.

Jon Skeet giải thích rằng lỗi thời SimpleDateFormat có thể được khoan dung hoặc không khoan dung. Điều này cũng đúng với cả DateTimeFormatter, trên thực tế nó có 3 thay vì 2 kiểu phân giải, được gọi là ‘khoan dung’, ‘thông minh’ và ‘nghiêm ngặt’. Vì nhiều lập trình viên không nhận thức được điều này, mặc dù, tôi nghĩ rằng họ đã thực hiện một lựa chọn tốt là không làm 'khoan dung' mặc định (‘thông minh’ là).

Điều gì xảy ra nếu chúng tôi muốn làm cho trình định dạng của chúng tôi trở nên khoan dung?

private static DateTimeFormatter parseFormatter 
     = DateTimeFormatter.ofPattern("MM/dd/yyyy") 
       .withResolverStyle(ResolverStyle.LENIENT); 

Bây giờ, nó cũng phân tích cú pháp "03/88/2013", thành 2013-05-27. Tôi tin rằng đây là những gì lớp học cũ cũng đã làm: đếm 88 ngày kể từ đầu tháng 3 cho đến ngày 27 tháng 5. Các thông báo lỗi khác vẫn như cũ. Nói cách khác, nó vẫn đối tượng với văn bản chưa được phân tích, đến năm có 6 chữ số và dấu gạch nối.

Câu hỏi: Tôi có thể sử dụng API hiện đại với phiên bản Java của mình không?

Nếu sử dụng ít nhất Java , bạn có thể.

  • Trong Java 8 trở lên, API mới được tích hợp sẵn.
  • Trong Java 6 và 7 nhận được the ThreeTen Backport, cổng sau của các lớp mới (đó là ThreeTen cho JSR-310, nơi API hiện đại được xác định lần đầu).
  • Trên Android, sử dụng phiên bản Android của ThreeTen Backport. Nó được gọi là ThreeTenABP và tôi nghĩ rằng có một lời giải thích tuyệt vời trong số this question: How to use ThreeTenABP in Android Project.
Các vấn đề liên quan