Có gì sai với Java Date & Time API? [đóng cửa]


105

Tôi rất thường xuyên gặp phản hồi tiêu cực về Java Datevà các lớp khác liên quan đến ngày-giờ. Là một nhà phát triển .NET, tôi không thể hoàn toàn (nếu chưa sử dụng chúng) hiểu, thực sự có gì sai với chúng.

Ai có thể làm sáng tỏ điều này không?


Câu trả lời:


142

Ah, Datelớp Java . Có lẽ một trong những ví dụ tốt nhất về cách không làm điều gì đó bằng bất kỳ ngôn ngữ nào, ở bất kỳ đâu. Tôi bắt đầu từ đâu?

Đọc JavaDoc có thể khiến người ta nghĩ rằng các nhà phát triển đã thực sự có một số ý tưởng hay. Nó tiếp tục về sự khác biệt giữa UTCGMT về độ dài, mặc dù thực tế là sự khác biệt giữa hai về cơ bản là giây nhuận (điều này khá hiếm khi xảy ra ).

Tuy nhiên, các quyết định thiết kế thực sự không lãng phí bất kỳ suy nghĩ nào về việc trở thành một API được thiết kế tốt. Dưới đây là một số sai lầm yêu thích:

  • Mặc dù được thiết kế vào thập kỷ cuối cùng của thiên niên kỷ, nó đánh giá số năm là hai chữ số kể từ năm 1900. Thực sự có hàng triệu giải pháp thay thế đang thực hiện 1900+ (hoặc 1900-) trong thế giới Java là kết quả của quyết định tầm thường này.
  • Tháng không được lập chỉ mục, để phục vụ cho trường hợp bất thường ngoạn mục có một mảng tháng và không tồn tại với một mảng mười ba phần tử, mảng đầu tiên chứa a null. Kết quả là chúng ta có 0..11 (và hôm nay là tháng 11 của năm 109). Có một số lượng tương tự ++ và - vào các tháng để chuyển đổi thành chuỗi.
  • Chúng có thể thay đổi . Do đó, bất kỳ lúc nào bạn muốn cung cấp lại ngày tháng (giả sử như một cấu trúc thể hiện), bạn cần trả về một bản sao của ngày tháng đó thay vì chính đối tượng ngày tháng (vì nếu không, mọi người có thể thay đổi cấu trúc của bạn).
  • Các Calendarthiết kế để 'sửa chữa' này, thực sự làm cho những sai lầm tương tự. Chúng vẫn có thể thay đổi.
  • Dateđại diện cho a DateTime, nhưng để trì hoãn những thứ đó trong vùng đất SQL, có một lớp con khác java.sql.Date, đại diện cho một ngày duy nhất (mặc dù không có múi giờ liên kết với nó).
  • Không có khoảng nào TimeZoneđược liên kết với a Date, và vì vậy các phạm vi (chẳng hạn như 'cả ngày') thường được biểu thị dưới dạng nửa đêm đến nửa đêm (thường ở một số múi giờ tùy ý)

Cuối cùng, cần lưu ý rằng giây nhuận thường tự điều chỉnh theo đồng hồ hệ thống tốt được cập nhật bằng ntp trong vòng một giờ (xem liên kết bên dưới). Cơ hội để một hệ thống vẫn hoạt động và hoạt động sau hai giây nhuận (tối thiểu sáu tháng một lần, thực tế là vài năm một lần) là khá khó xảy ra, đặc biệt là khi xem xét thực tế là thỉnh thoảng bạn phải triển khai lại các phiên bản mã mới của mình . Ngay cả khi sử dụng một ngôn ngữ động tái tạo các lớp hoặc một thứ gì đó như công cụ WAR cũng sẽ gây ô nhiễm không gian lớp và cuối cùng sẽ hết permgen.


43

JSR 310 , đã thay thế các lớp date-time cũ bằng java.time trong Java 8, tự xác minh trong JSR gốc như sau:

2.5 Đặc điểm kỹ thuật được đề xuất sẽ giải quyết nhu cầu gì của cộng đồng Java?

Hiện tại Java SE có hai API ngày và giờ riêng biệt - java.util.Date và java.util.Calendar. Cả hai API đều được các nhà phát triển Java mô tả là khó sử dụng trên các trang web và diễn đàn. Đáng chú ý, cả hai đều sử dụng chỉ số 0 trong nhiều tháng, đây là nguyên nhân gây ra nhiều lỗi. Lịch cũng gặp phải nhiều lỗi và các vấn đề về hiệu suất trong những năm qua, chủ yếu do lưu trữ trạng thái của nó theo hai cách khác nhau trong nội bộ.

Một lỗi cổ điển (4639407) đã ngăn một số ngày nhất định được tạo trong đối tượng Lịch. Một chuỗi mã có thể được viết có thể tạo ra một ngày trong một số năm nhưng không phải trong những năm khác, có tác dụng ngăn một số người dùng nhập đúng ngày sinh của họ. Điều này là do lớp Lịch chỉ cho phép tăng thời gian tiết kiệm ánh sáng ban ngày là một giờ vào mùa hè, trong khi theo lịch sử, nó cộng thêm 2 giờ vào khoảng thời gian diễn ra chiến tranh thế giới thứ hai. Mặc dù lỗi này hiện đã được sửa, nhưng nếu tại một thời điểm nào đó trong tương lai, một quốc gia nào đó chọn đưa ra mức tăng thời gian tiết kiệm ánh sáng ban ngày cộng với ba giờ vào mùa hè, thì lớp Lịch sẽ lại bị hỏng.

API Java SE hiện tại cũng gặp vấn đề trong môi trường đa luồng. Các lớp bất biến được biết là vốn dĩ an toàn theo luồng vì trạng thái của chúng không thể thay đổi. Tuy nhiên, cả Ngày và Lịch đều có thể thay đổi, điều này đòi hỏi các lập trình viên phải cân nhắc sao chép và phân luồng một cách rõ ràng. Ngoài ra, việc thiếu an toàn luồng trong DateTimeFormat không được biết đến rộng rãi và là nguyên nhân của nhiều vấn đề khó theo dõi luồng.

Cũng như các vấn đề với các lớp mà Java SE có cho datetime, nó không có các lớp để mô hình hóa các khái niệm khác. Ngày hoặc giờ không nằm trong múi giờ, thời lượng, khoảng thời gian và khoảng thời gian không có biểu diễn lớp trong Java SE. Do đó, các nhà phát triển thường sử dụng một int để biểu thị một khoảng thời gian, với javadoc chỉ định đơn vị.

Việc thiếu mô hình ngày và giờ toàn diện cũng dẫn đến nhiều hoạt động thông thường phức tạp hơn mức bình thường. Ví dụ, tính toán số ngày giữa hai ngày là một vấn đề đặc biệt khó hiện nay.

JSR này sẽ giải quyết vấn đề của một mô hình ngày và giờ hoàn chỉnh, bao gồm ngày và giờ (có và không có múi giờ), thời lượng và khoảng thời gian, khoảng thời gian, định dạng và phân tích cú pháp.


28
  • Các phiên bản ngày có thể thay đổi , điều này hầu như luôn luôn bất tiện.
  • Họ có một bản chất kép. Chúng đại diện cho cả dấu thời gian và ngày lịch. Hóa ra đây là vấn đề khi thực hiện tính toán vào ngày tháng.
  • Các biểu diễn số của dữ liệu lịch là phản trực quan trong nhiều trường hợp. Ví dụ: getMonth()dựa trên 0, getYear()là 1900 (tức là năm 2009 được biểu thị là 109).
  • Họ đang thiếu rất nhiều chức năng mà bạn mong đợi từ một Datelớp.

13

Tôi cảm thấy đối với bạn ... là một cựu lập trình viên .NET, tôi đã hỏi những câu hỏi tương tự, API thời gian trong .NET (timepans, nạp chồng toán tử) rất tiện lợi.

Trước tiên, để tạo một ngày cụ thể, bạn sử dụng API không dùng nữa hoặc:

Calendar c = Calendar.getInstance();
c.set(2000, 31, 12)

Để trừ một ngày bạn làm những điều xấu xa như

Date firstDate = ...
Calendar c = Calendar.getInstance();
c.setTime(fistDate);
c.add(Calendar.DATE,-1);
Date dayAgo = c.getTime();

hoặc tồi tệ hơn

Date d = new Date();
Date d2 = new Date(d.getTime() - 1000*60*60*24);

Để biết khoảng thời gian đã trôi qua giữa hai ngày (tính theo ngày / tuần / tháng) ... nó thậm chí còn tồi tệ hơn

Tuy nhiên DateUtils từ apache ( org.apache.commons.lang.time.DateUtils) cung cấp một số phương thức tiện lợi và gần đây tôi thấy mình chỉ sử dụng chúng

Như Brabster đã viết, Joda Time cũng là một thư viện bên ngoài tốt, nhưng apache có vẻ "phổ biến" hơn bất cứ thứ gì khác ...


Làm ơn-làm ơn, hãy chỉ cho chúng tôi cách làm như vậy ("tìm hiểu khoảng thời gian đã trôi qua giữa hai ngày")!
Anton Gogolev

Tôi ước gì tôi biết một cách dễ dàng ... bao gồm cả năm nhuận, tháng nhảy vọt và những ngày biến động ...
Eran Medan

1
Xem thêm, org.apache.commons.lang.time: commons.apache.org/lang//api/org/apache/commons/lang/time/…
dirtygod

@AntonGogolev Trong java.time , sử dụng PeriodDurationcác lớp để tính toán và biểu thị thời gian đã trôi qua theo thang năm-tháng-ngày và giờ-phút-giây tương ứng.
Basil Bourque

4

Thành thật mà nói, tôi thấy API ngày của Java có thể sử dụng được. Hầu hết các vấn đề mà tôi đã nhìn thấy và nghe nói về liên quan đến tính cách rườm rà, sự cần thiết phải bao gồm nhiều lớp để làm bất cứ điều gì hữu ích ( Calendar, Date, DateFormat/ SimpleDateFormat) và thiếu accessors đơn giản như getDayOfWeek().

Joda Time là một API thay thế được đánh giá cao trong Java và trong phần Tại sao Joda Time nó đưa ra thêm một số lý lẽ về lý do tại sao nó là một giải pháp thay thế khả thi có thể được quan tâm.

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.