Ngày Java so với Lịch


364

Ai đó có thể vui lòng tư vấn "thực tiễn tốt nhất" hiện tại xung quanh DateCalendarcác loại.

Khi viết mã mới, tốt nhất là luôn luôn ưu tiên Calendarhơn Date, hoặc có trường hợp nào Datelà kiểu dữ liệu phù hợp hơn không?



2
FYI, người già lớp ngày thời gian phiền hà như java.util.Date, java.util.Calendarjava.text.SimpleDateFormatbây giờ là di sản, thay thế bởi sự java.time lớp. Phần lớn chức năng java.time được chuyển ngược lại sang Java 6 & Java 7 trong dự án ThreeTen-Backport . Thích nghi hơn nữa cho Android trước đó trong dự án ThreeTenABP . Xem Cách sử dụng ThreeTenABP .
Basil Bourque

2
FYI, dự án Joda-Time (được đề cập trong một bình luận khác) hiện đang ở chế độ bảo trì , với nhóm tư vấn di chuyển đến các lớp java.time . Xem Hướng dẫn của Oracle .
Basil Bourque

Câu trả lời:


377

Ngày là một lớp đơn giản hơn và chủ yếu là ở đó vì lý do tương thích ngược. Nếu bạn cần đặt ngày cụ thể hoặc thực hiện số học ngày, hãy sử dụng Lịch. Lịch cũng xử lý nội địa hóa. Các hàm thao tác ngày trước của Date đã bị từ chối.

Cá nhân tôi có xu hướng sử dụng thời gian tính bằng mili giây dưới dạng dài (hoặc Dài, nếu phù hợp) hoặc Lịch khi có lựa chọn.

Cả Ngày và Lịch đều có thể thay đổi, có xu hướng trình bày các vấn đề khi sử dụng hoặc trong API.


5
FYI, người già lớp ngày thời gian khủng khiếp phiền hà như java.util.Date, java.util.Calendarjava.text.SimpleDateFormatbây giờ là di sản , thay thế bởi sự java.time class built vào Java 8 và sau đó. Xem Hướng dẫn của Oracle .
Basil Bourque

67

Cách tốt nhất cho mã mới (nếu chính sách của bạn cho phép mã của bên thứ ba) là sử dụng thư viện Joda Time .

Cả NgàyLịch đều có quá nhiều vấn đề về thiết kế mà không phải là giải pháp tốt cho mã mới.


7
Tôi thứ hai gợi ý để sử dụng thời gian joda. Nó dễ sử dụng và hiểu hơn và cung cấp nhiều chức năng hơn bạn có thể sử dụng.
Jeroen van Bergen

30
Để thảo luận về việc nên sử dụng Joda Time hay gắn bó với các lớp JDK tiêu chuẩn, hãy xem stackoverflow.com/questions/589870/
Kẻ

3
Tôi không biết nếu nó đáng bị downvote, nhưng nó không trả lời câu hỏi. Có thể chỉ là tốt hơn như một nhận xét.
IcedDante

7
Bởi vì câu hỏi là về việc sử dụng Ngày và Lịch không sử dụng thư viện của bên thứ ba có thêm rủi ro phụ thuộc của nhà cung cấp cho một dự án.
Archimedes Trajano

3
Đây là "cách tốt nhất" cho đến khi gói java.time được cung cấp với Java 8.
DaBlick 16/11/18

58
  • DateCalendarthực sự là cùng một khái niệm cơ bản (cả hai đều thể hiện tức thời và là các hàm bao quanh một longgiá trị cơ bản ).

  • Người ta có thể lập luận rằng Calendarthực sự thậm chí còn bị phá vỡ nhiều hơnDate là, vì nó dường như cung cấp sự thật cụ thể về những thứ như ngày trong tuần và thời gian trong ngày, trong khi nếu bạn thay đổi timeZonetài sản của mình , bê tông sẽ biến thành blancmange! Không có đối tượng nào thực sự hữu ích như một cửa hàng của năm tháng hoặc thời gian trong ngày vì lý do này.

  • CalendarChỉ sử dụng như một máy tính mà khi được đưa ra DateTimeZonecác đối tượng sẽ thực hiện các phép tính cho bạn. Tránh sử dụng nó để gõ thuộc tính trong một ứng dụng.

  • Sử dụng SimpleDateFormatcùng với TimeZoneDateđể tạo Chuỗi hiển thị.

  • Nếu bạn cảm thấy thích phiêu lưu, hãy sử dụng Joda-Time, mặc dù IMHO phức tạp không cần thiết và sẽ sớm được áp dụng bởi API ngày JSR-310 trong bất kỳ sự kiện nào.

  • Tôi đã trả lời trước đó rằng không khó để cuộn YearMonthDaylớp của riêng bạn , sử dụng Calendardưới mui xe để tính toán ngày. Tôi đã bị từ chối vì đề xuất này nhưng tôi vẫn tin rằng đó là một giá trị hợp lệ vì Joda-Time (và JSR-310 ) thực sự quá phức tạp đối với hầu hết các trường hợp sử dụng.


1
Có khung thời gian nào cho JSR 310 không? Nó sẽ có trong Java 7, nhưng tôi tin rằng bây giờ không phải vậy.
Brian Agnew

@Brian - chắc chắn nó rất im lặng trong danh sách gửi thư đó!
oxbow_lakes

Chỉ cần kiểm tra, nó đã không hoạt động, điều đó có nghĩa là họ đã không công bố bản dự thảo cột mốc trong 18 tháng :-(
Brian Agnew

Nhận xét gần đây nhất về danh sách gửi thư là từ tháng 7 và bởi Stephen, vì vậy dự án có lẽ vẫn còn tích tắc
oxbow_lakes

Đã đồng ý. Nếu bạn biết cách sử dụng Date một cách an toàn như một đối tượng bất biến và Lịch để thao túng Ngày, hầu hết mọi người nên an toàn. Hãy cẩn thận khi sử dụng SimpleDateFormat trong mã đa luồng.
cwash

25

Ngày là tốt nhất để lưu trữ một đối tượng ngày. Nó là cái bền bỉ, cái nối tiếp ...

Lịch là tốt nhất để thao túng Ngày.

Lưu ý: đôi khi chúng tôi cũng thích java.lang.Long hơn Date, vì Date có thể thay đổi và do đó không an toàn cho chuỗi. Trên một đối tượng Date, sử dụng setTime () và getTime () để chuyển đổi giữa hai đối tượng. Ví dụ: Ngày không đổi trong ứng dụng (ví dụ: zero 1970/01/01 hoặc END_OF_TIME áp dụng mà bạn đặt thành 2099/12/31; chúng rất hữu ích để thay thế các giá trị null là thời gian bắt đầu và thời gian kết thúc, đặc biệt là khi bạn duy trì chúng trong cơ sở dữ liệu, vì SQL rất đặc biệt với null).


Tôi lấy nó bạn đang lấy về bất biếnjava.lang.Long
pjp

17

Tôi thường sử dụng Ngày nếu có thể. Mặc dù nó có thể thay đổi, nhưng các trình biến đổi thực sự bị phản đối. Cuối cùng, về cơ bản nó kết thúc một khoảng thời gian dài biểu thị ngày / giờ. Ngược lại, tôi sẽ sử dụng Lịch nếu tôi phải thao tác với các giá trị.

Bạn có thể nghĩ về nó theo cách này: bạn chỉ sử dụng StringBuffer khi bạn cần có các Chuỗi mà bạn có thể dễ dàng thao tác và sau đó chuyển đổi chúng thành Chuỗi bằng phương thức toString (). Theo cùng một cách, tôi chỉ sử dụng Lịch nếu tôi cần thao tác dữ liệu tạm thời.

Để thực hành tốt nhất, tôi có xu hướng sử dụng các đối tượng bất biến càng nhiều càng tốt bên ngoài mô hình miền . Nó làm giảm đáng kể khả năng xảy ra bất kỳ tác dụng phụ nào và nó được trình biên dịch thực hiện cho bạn, thay vì kiểm tra JUnit. Bạn sử dụng kỹ thuật này bằng cách tạo các trường cuối cùng riêng tư trong lớp của bạn.

Và trở lại với sự tương tự StringBuffer. Đây là một số mã chỉ cho bạn cách chuyển đổi giữa Lịch và Ngày

String s = "someString"; // immutable string
StringBuffer buf = new StringBuffer(s); // mutable "string" via StringBuffer
buf.append("x");
assertEquals("someStringx", buf.toString()); // convert to immutable String

// immutable date with hard coded format.  If you are hard
// coding the format, best practice is to hard code the locale
// of the format string, otherwise people in some parts of Europe
// are going to be mad at you.
Date date =     new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse("2001-01-02");

// Convert Date to a Calendar
Calendar cal = Calendar.getInstance();
cal.setTime(date);

// mutate the value
cal.add(Calendar.YEAR, 1);

// convert back to Date
Date newDate = cal.getTime();

// 
assertEquals(new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse("2002-01-02"), newDate);

Vâng, các đối tượng bất biến có ý nghĩa cho công việc thời gian. Các lớp java.time thay thế Date/ Calendarsử dụng mẫu đối tượng bất biến.
Basil Bourque

Điều đó có thể đúng nhưng không phải trong năm 2010
Archimedes Trajano

Đúng. Nhưng có hàng ngàn người đọc trang này bây giờ , hơn 150.000 cho đến nay. Nhận xét của tôi là một lưu ý cho họ, không phải là một lời chỉ trích của bạn.
Basil Bourque

Tôi biết, đó là lý do tại sao tôi nêu lên câu trả lời khác. Tuy nhiên, vẫn còn một số người cần phải chịu đựng các JDK cũ hơn cũng cần câu trả lời ở trên.
Archimedes Trajano

1
Trên thực tế, đối với Java 6 & 7, chúng tôi có dự án ThreeTen-Backport . Điều này mang lại hầu hết java.time chức năng với hầu như cùng một API. Vì vậy, không cần phải sử dụng các lớp thời gian di sản khủng khiếp đó.
Basil Bourque

15

Dates nên được sử dụng như là điểm bất biến trong thời gian; Calendars có thể thay đổi và có thể được thông qua và sửa đổi nếu bạn cần cộng tác với các lớp khác để đưa ra ngày cuối cùng. Xem xét chúng tương tự StringStringBuildervà bạn sẽ hiểu cách tôi nghĩ chúng nên được sử dụng.

.


Vâng, các đối tượng bất biến có ý nghĩa cho công việc thời gian. Các lớp java.time thay thế Date/ Calendarsử dụng mẫu đối tượng bất biến. Cụ thể, Instantthay thế java.util.Date, và ZonedDateTimethay thế Calendar/ GregorianCalendar.
Basil Bourque

15

tl; dr

tư vấn "thực hành tốt nhất" hiện tại xung quanh DateCalendar

là nó tốt nhất để luôn luôn ủng hộ CalendarhơnDate

Tránh các lớp kế thừa hoàn toàn. Sử dụng các lớp java.time thay thế.

  • Trong một khoảnh khắc trong UTC , hãy sử dụngInstant
    (tương đương hiện đại Date)
  • Đối với một khoảnh khắc trong một múi giờ cụ thể , hãy sử dụng (tương đương hiện đại vớiZonedDateTime
    GregorianCalendar )
  • Trong một khoảnh khắc trong phần bù từ UTC cụ thể , hãy sử dụngOffsetDateTime
    (không tương đương trong các lớp kế thừa)
  • Đối với thời gian ngày (không phải là một khoảnh khắc) với múi giờ hoặc độ lệch không xác định, hãy sử dụng (không tương đương trong các lớp kế thừa)LocalDateTime

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

Chi tiết

Câu trả lời của Ortomala Lokni là đúng khi đề xuất sử dụng các lớp java.time hiện đại thay vì các lớp thời gian cũ kế thừa rắc rối (Date , Calendar, vv). Nhưng Câu trả lời đó cho thấy lớp sai là tương đương (xem nhận xét của tôi về Câu trả lời đó).

Sử dụng java.time

Các lớp java.time là một cải tiến lớn so với các lớp ngày giờ cũ, sự khác biệt giữa ngày và đêm. Các lớp học cũ được thiết kế kém, khó hiểu và rắc rối. Bạn nên tránh các lớp cũ bất cứ khi nào có thể. Nhưng khi bạn cần chuyển đổi sang / từ cái cũ / cái mới, bạn có thể làm như vậy bằng cách gọi các phương thức mới thêm vào cái lớp .

Để biết thêm thông tin về chuyển đổi, hãy xem sơ đồ Trả lời và tiện lợi của tôi sang Câu hỏi khác, Chuyển đổi java.util.Date sang loại java java.time nào? .

Tìm kiếm Stack Overflow cung cấp cho hàng trăm câu hỏi và câu trả lời ví dụ về việc sử dụng java.time. Nhưng đây là một bản tóm tắt nhanh chóng.

Instant

Nhận thời điểm hiện tại với một Instant. Các Instantlớp đại diện cho một thời điểm trên Timeline trong UTC với độ phân giải nano giây (lên đến chín (9) chữ số của một phân số thập phân).

Instant instant = Instant.now();

ZonedDateTime

Để xem cùng một khoảnh khắc đó qua ống kính của thời gian đồng hồ treo tường của một số khu vực cụ thể , hãy áp dụng múi giờ ( ZoneId) để có được a ZonedDateTime.

Múi giờ

Chỉ định một tên múi giờ thích hợp trong các định dạng của continent/region, chẳng hạn như America/Montreal, Africa/Casablancahoặc Pacific/Auckland. Không bao giờ sử dụng chữ viết tắt 3-4 chữ cái như ESThoặc ISTchúng không phải là múi giờ thực, không được chuẩn hóa và thậm chí không phải là duy nhất (!).

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone();

Bù lại

Múi giờ là lịch sử thay đổi của khu vực trong phần bù từ UTC . Nhưng đôi khi bạn chỉ được cung cấp một phần bù mà không có vùng đầy đủ. Trong trường hợp đó, sử dụng OffsetDateTimelớp.

ZoneOffset offset = ZoneOffset.parse( "+05:30" );
OffsetDateTime odt = instant.atOffset( offset );

Sử dụng múi giờ tốt hơn là sử dụng bù trừ đơn thuần.

LocalDateTime

Các địa phương trực tuyến trong các Local…lớp có nghĩa là bất kỳ địa phương, không phải là một địa phương cụ thể. Vì vậy, tên có thể phản trực giác.

LocalDateTime, LocalDateLocalTimecố tình thiếu bất kỳ thông tin nào về phần bù hoặc múi giờ. Vì vậy, họ không đại diện cho những khoảnh khắc thực tế, họ không phải là điểm trên dòng thời gian. Khi nghi ngờ hoặc nhầm lẫn, sử dụng ZonedDateTimechứ không phải LocalDateTime. Tìm kiếm Stack Overflow để thảo luận nhiều hơn.

Dây

Không kết hợp các đối tượng thời gian ngày với các chuỗi đại diện cho giá trị của chúng. Bạn có thể phân tích một chuỗi để lấy một đối tượng thời gian và bạn có thể tạo một chuỗi từ một đối tượng thời gian. Nhưng chuỗi không bao giờ là thời gian ngày.

Tìm hiểu về các định dạng ISO 8601 tiêu chuẩn , được sử dụng theo mặc định trong các lớp java.time.


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 .

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

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 đó, 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 .


10

Với Java 8, nên sử dụng gói java.time mới .

Đối tượng là bất biến, múi giờ và tiết kiệm ánh sáng ban ngày được tính đến.

Bạn có thể tạo một ZonedDateTimeđối tượng từ một java.util.Dateđối tượng cũ như thế này:

    Date date = new Date();
    ZonedDateTime zonedDateTime = date.toInstant().atZone(ZoneId.systemDefault());

Tốt để đề xuất các lớp java.time. Nhưng xấu để đề nghị LocalDateTime. Lớp đó cố tình mất bất kỳ thông tin nào về offset-từ-UTC và múi giờ. Vì vậy, lớp đó không tương đương như Datetrong UTC và Calendarcó múi giờ được chỉ định. Xem sơ đồ Trả lời và tiện lợi của tôi sang Câu hỏi khác, Chuyển đổi java.util.Date sang loại java java.time nào? .
Basil Bourque

9

Tôi luôn ủng hộ Joda-time . Đây là lý do tại sao.

  1. API phù hợp và trực quan. Không giống như các API java.util.Date/CalWiki
  2. nó không gặp phải vấn đề luồng, không giống như java.text.SimpleDateFormat, v.v. (Tôi đã thấy nhiều vấn đề của khách hàng liên quan đến việc không nhận ra rằng định dạng ngày / giờ tiêu chuẩn không an toàn cho chuỗi)
  3. nó là nền tảng của các API ngày / giờ Java mới ( JSR 310 , được lên lịch cho Java 8. Vì vậy, bạn sẽ sử dụng các API sẽ trở thành các API Java cốt lõi.

EDIT: Các lớp ngày / giờ Java được giới thiệu với Java 8 hiện là giải pháp ưa thích, nếu bạn có thể di chuyển sang Java 8


3
Lần cuối cùng tôi nhìn, JODA và JSR-310 trông rất khác nhau, ngay cả khi cả hai đều được viết bởi Stephen Colebourne. Điều đó nói rằng, JODA sẽ giới thiệu cho bạn về sự phức tạp của các vấn đề về thời gian mà JSR-310 cũng giải quyết
oxbow_lakes

2
Bởi vì câu hỏi là về việc sử dụng Ngày và Lịch không sử dụng thư viện của bên thứ ba có thêm rủi ro phụ thuộc của nhà cung cấp cho một dự án.
Archimedes Trajano

2
Tôi duy trì thực hành tốt nhất chỉ đơn giản là không sử dụng các lớp đó
Brian Agnew

3
Với các vấn đề đã biết với các lớp java.util.Date và Lịch, đề xuất Joda-Time (hoặc JSR 310) có vẻ phù hợp và có trách nhiệm với tôi. Chúng ta không nói về một vấn đề của hương vị hoặc phong cách thẩm mỹ. Nếu ai đó hỏi liệu họ nên đi xe màu đỏ hay xe màu bạc, và tôi biết chiếc xe màu đỏ có lốp bị xẹp và chiếc xe màu bạc có bộ tản nhiệt bị vỡ, tôi có nên chọn xe hay tôi nên đề nghị gọi taxi? Câu trả lời cho câu hỏi đó dường như rất rõ ràng hiện nay khi ngay cả Sun / Oracle đã quyết định bỏ lại những kẻ rác rưởi đó và mua một chiếc xe mới: JSR 310: API ngày và giờ.
Basil Bourque

1
FYI, dự án Joda-Time hiện đang ở chế độ bảo trì , khuyên nên chuyển sang các lớp java.time . Xem Hướng dẫn của Oracle .
Basil Bourque

8

Một chút muộn trong bữa tiệc, nhưng Java có API Ngày Thời gian mới trong JDK 8. Bạn có thể muốn nâng cấp phiên bản JDK của mình và nắm lấy tiêu chuẩn. Không có ngày / lịch lộn xộn, không có bình bên thứ 3.


1

Ngày nên được phát triển lại. Thay vì là một interger dài, nó nên giữ năm, tháng, ngày, giờ, phút, giây, như các trường riêng biệt. Nó có thể thậm chí tốt để lưu trữ lịch và múi giờ ngày này được liên kết với.

Trong cuộc trò chuyện tự nhiên của chúng tôi, nếu thiết lập một cuộc hẹn vào ngày 1 tháng 11 năm 2013 1 giờ chiều theo giờ NY, thì đây là DateTime. Nó KHÔNG phải là Lịch. Vì vậy, chúng ta cũng có thể trò chuyện như thế này trong Java.

Khi Ngày được lưu trữ dưới dạng một số nguyên dài (tính bằng mili giây kể từ ngày 1 tháng 1 năm 1970 hoặc một cái gì đó), việc tính ngày hiện tại của nó phụ thuộc vào lịch. Lịch khác nhau sẽ cho ngày khác nhau. Đây là từ triển vọng đưa ra một thời gian tuyệt đối (ví dụ 1 nghìn tỷ giây sau Big Bang). Nhưng thường thì chúng ta cũng cần một cách trò chuyện thuận tiện, như một đối tượng gói gọn năm, tháng, v.v.

Tôi tự hỏi nếu có những tiến bộ mới trong Java để dung hòa 2 mục tiêu này. Có lẽ kiến ​​thức java của tôi quá cũ.


Thực tế là bạn có thể lưu trữ cùng một thời điểm tức thời, nhưng báo cáo giờ / phút / ngày / tuần / năm / foo khác nhau dựa trên các hệ thống lịch khác nhau, là một điểm mạnh, không phải là điểm yếu. Nó phản ánh thực tế (phức tạp).
ThrawnCA

Thật vậy, Dateđã được phát triển lại; được thay thế bởi java.time.Instantlớp học. Và Calendar/ GregorianCalendarđã được thay thế bởi java.time.ZonedDateTimelớp học.
Basil Bourque

0

Btw "date" thường được gắn thẻ là "lỗi thời / không dùng nữa" (tôi không biết chính xác tại sao) - một cái gì đó về nó được viết ở đó Java: Tại sao hàm tạo ngày không được dùng nữa và thay vào đó tôi sử dụng cái gì?

Có vẻ như đó chỉ là vấn đề của nhà xây dựng thông qua Ngày mới (int year, int tháng, int day) , cách được đề xuất là thông qua Lịch và đặt các thông số riêng biệt .. ( Lịch cal = Lịch.getInstance (); )


0

Tôi sử dụng Lịch khi tôi cần một số thao tác cụ thể theo ngày như di chuyển kịp thời, nhưng Ngày tôi thấy hữu ích khi bạn cần định dạng ngày để điều chỉnh nhu cầu của mình, gần đây tôi phát hiện ra rằng Locale có rất nhiều thao tác và phương pháp hữu ích. Tôi đang sử dụng Locale ngay bây giờ!


FYI, các lớp Calendar& rắc rối Dateđã được thay thế bởi các lớp java.time . Không cần phải sử dụng Datehay Calendar. Và Localekhông có gì để làm với ý nghĩa của các đối tượng thời gian. A Localechỉ được sử dụng để xác định các chuẩn mực ngôn ngữ và văn hóa của con người sẽ được sử dụng trong việc bản địa hóa trong khi tạo văn bản để thể hiện giá trị của một đối tượng thời gian.
Basil Bourque
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.