Làm cách nào để có được Ngày không có thời gian trong Java?


235

Tiếp tục từ chương trình Java câu hỏi Stack Overflow để có được ngày hiện tại mà không có dấu thời gian :

Cách hiệu quả nhất để có được một đối tượng Date mà không có thời gian là gì? Có cách nào khác ngoài hai?

// Method 1
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date dateWithoutTime = sdf.parse(sdf.format(new Date()));

// Method 2
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
dateWithoutTime = cal.getTime();

Cập nhật :

  1. Tôi biết về Joda-Time ; Tôi chỉ cố gắng tránh thư viện bổ sung cho một nhiệm vụ đơn giản (tôi nghĩ) như vậy. Nhưng dựa trên các câu trả lời cho đến nay Joda-Time có vẻ cực kỳ phổ biến, vì vậy tôi có thể xem xét nó.

  2. Bằng cách hiệu quả , ý tôi là tôi muốn tránh Stringviệc tạo đối tượng tạm thời như được sử dụng method 1, trong khi đó method 2có vẻ như là một hack thay vì một giải pháp.


1
Có hiệu quả? Bạn có cần hiệu quả cao hơn những gì được cung cấp, ví dụ, theo phương thức1?
Johan Sjöberg

2
Bạn có ý nghĩa gì bởi "hiệu quả"? Một ngày về cơ bản là một thời gian dài, bạn thực sự không thể làm điều này trong bộ nhớ ít hơn thế. Nếu bạn có nghĩa là "thuận tiện", thời gian JODA là cách để đi.
millimoose

3
+1 Chính xác là câu hỏi tôi có.
Alba Mendez

1
Tôi thích phương thức 2. Tạo một phương thức tĩnh trong một lớp tiện ích và chỉ cần sử dụng nó. Tôi đã tiếp cận phương pháp này trong nhiều năm.
Wilson Freitas

1
Nitpicking trên "bản cập nhật 1" của bạn: nếu đó là "một nhiệm vụ đơn giản" như vậy, tôi đoán Sun sẽ không đến với API khủng khiếp và kém hiệu quả như vậy, và bạn (và rất nhiều người khác) sẽ không hỏi câu hỏi đó ở tất cả ;-)
Flávio Etrusco

Câu trả lời:


108

Bạn hoàn toàn phải sử dụng java.util.Date? Tôi hoàn toàn khuyên bạn nên sử dụng Joda Time hoặc java.timegói từ Java 8 thay thế. Cụ thể, trong khi Ngày và Lịch luôn đại diện cho một thời điểm cụ thể, không có khái niệm "chỉ là một ngày", Joda Time không có loại đại diện cho điều này ( LocalDate). Mã của bạn sẽ rõ ràng hơn nhiều nếu bạn có thể sử dụng các loại đại diện cho những gì bạn thực sự đang cố gắng làm.

Có rất nhiều, rất nhiều lý do khác để sử dụng Joda Time hoặc java.timethay vì các java.utilloại tích hợp - chúng thường là các API tốt hơn nhiều. Bạn luôn có thể chuyển đổi sang / từ a java.util.Datetại các ranh giới của mã của riêng bạn nếu bạn cần, ví dụ như cho tương tác cơ sở dữ liệu.


75
Trong các ứng dụng doanh nghiệp, chúng tôi không phải lúc nào cũng có tùy chọn để thêm / sử dụng các thư viện khác. Tôi đánh giá cao con trỏ tới Joda Time, nhưng nó thực sự không phải là câu trả lời cho vấn đề ban đầu về việc lấy phần ngày bằng cách sử dụng Java tiêu chuẩn. Cảm ơn. Nâng cao câu trả lời của Chathuranga.
noogrub

3
@noogrub: Câu trả lời của Chathugranga không trả lời câu hỏi ban đầu, đó là hỏi về cách lấy một Dateđối tượng - câu trả lời đó định dạng một ngày thành một chuỗi, không giống nhau. Về cơ bản, hỏi ngày a Datelà ngày nào là một câu hỏi vô nghĩa mà không có thêm thông tin: múi giờ và hệ thống lịch bạn đang sử dụng.
Jon Skeet

7
"Trong các ứng dụng doanh nghiệp, chúng tôi không phải lúc nào cũng có tùy chọn để thêm / sử dụng các thư viện khác". Có thật không ? Bạn có gợi ý rằng bạn không thể sử dụng libs của bên thứ 3 không?
Brian Agnew

3
Nhận xét của @BrianAgnew noogrub dường như có liên quan đến các ràng buộc phi công nghệ, tôi nghĩ
Jose_GD

3
Từ quan điểm ngày nay: " Joda-Time là thư viện ngày và giờ tiêu chuẩn thực tế cho Java trước Java SE 8. Người dùng hiện được yêu cầu chuyển sang java.time (JSR-310)."
Drux

66

Đây là những gì tôi đã sử dụng để có được ngày hôm nay với thời gian được đặt thành 00:00:00:

DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");

Date today = new Date();

Date todayWithZeroTime = formatter.parse(formatter.format(today));

28
Điều này khác với "phương pháp 1" đã được đề xuất trong câu hỏi ban đầu như thế nào?
Chris

Không giải quyết múi giờ này xảy ra trong (trái sang múi giờ mặc định tùy ý của JVM).
Darrell Teague

58

Bạn có thể sử dụng DateUtils.truncate từ thư viện Apache Commons.

Thí dụ:

DateUtils.truncate(new Date(), java.util.Calendar.DAY_OF_MONTH)

3
Nếu bạn định thêm một thư viện mới, hãy để nó là Joda thay vì Apache Commons.
Dibbeke

6
+1 @Dibbeke Sự khác biệt không chỉ là bạn sẽ thêm một thư viện thay vì thư viện khác. Đôi khi người ta không muốn học một thư viện hoàn toàn mới chỉ với nhiệm vụ tầm thường là cắt ngắn thời gian kể từ ngày. Hoặc không muốn có mã kết hợp Ngày / Lịch với ngày Joda bằng cách sử dụng đôi khi trước đây và đôi khi sau đó. Hoặc không thể đủ khả năng / biện minh cho việc thay thế tất cả mã dựa trên Ngày / Lịch hoạt động tốt với một thư viện khác cho mục đích tầm thường như vậy. Trong khi đề xuất Joda chắc chắn là tốt vì nó rõ ràng là vượt trội và đơn giản là Điều đúng, đôi khi cuộc sống thực sự bắt đầu.
SantiBailors

Thiên tài! Giúp tôi rất nhiều
Thiesen

37

Có cách nào khác ngoài hai?

Có, có.

LocalDate.now( 
    ZoneId.of( "Pacific/Auckland" ) 
)

java.time

Java 8 trở lên đi kèm với gói java.time mới được tích hợp sẵn. Xem hướng dẫn . Phần lớn chức năng java.time được chuyển ngược lại sang Java 6 & 7 trong ThreeTen-Backport và được điều chỉnh thêm cho Android trong ThreeTenABP .

Tương tự như Joda-Time , java.time cung cấp một LocalDatelớp để biểu thị giá trị chỉ theo ngày mà không có thời gian trong ngày và không có múi giờ.

Lưu ý rằng múi giờ là rất quan trọng để xác định một ngày cụ thể. Chẳng hạn, vào lúc nửa đêm ở Paris, chẳng hạn, ngày vẫn là ngày hôm qua, tại Montréal.

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) ) ;

Theo mặc định, java.time sử dụng tiêu chuẩn ISO 8601 trong việc tạo biểu diễn chuỗi của giá trị ngày hoặc thời gian. (Một điểm tương đồng khác với Joda-Time.) Vì vậy, chỉ cần gọi toString()để tạo văn bản như thế nào 2015-05-21.

String output = today.toString() ; 

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 .

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 .

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
Cảm ơn đã đề cập đến múi giờ . Nó đã không xảy ra với tôi rằng "ngày nào" không phải là một khái niệm tuyệt đối.
ToolmakerSteve

@ToolmakerSteve Lấy ngày luôn liên quan đến múi giờ. Nhưng mẹo ở đây là nếu bạn không chỉ định múi giờ, múi giờ mặc định hiện tại của JVM của bạn sẽ tự động được áp dụng để xác định ngày. Trên hết, múi giờ mặc định hiện tại của JVM có thể thay đổi bất cứ lúc nào ! Bất kỳ mã nào trong bất kỳ luồng nào của bất kỳ ứng dụng nào trong JVM đều có thể gọi TimeZone.setDefaulttrong thời gian chạy và ngay lập tức ảnh hưởng đến tất cả các mã khác đang chạy trong JVM đó. Đạo đức của câu chuyện: Luôn chỉ định múi giờ.
Basil Bourque

22

Cách đơn giản nhất:

long millisInDay = 60 * 60 * 24 * 1000;
long currentTime = new Date().getTime();
long dateOnly = (currentTime / millisInDay) * millisInDay;
Date clearDate = new Date(dateOnly);

5
Đối với tôi, đây là: "Thứ bảy ngày 19 tháng 2 01 : 00: 00 CET 2011". Nếu bạn muốn sử dụng nó, thì chỉ với UTC.
Chris Lercher

4
Không phải là dòng 3 long dateOnly = (currentTime / millisInDay) * millisInDay;giống như viết long dateOnly = currentTime;?
Chris

5
Không phải vì toán học số nguyên. Hãy nghĩ về nó giống như Math.floor (currentTime / millisInDay) * millisInDay. Anh ta thiết lập thời gian hiệu quả đến 00:00:00 theo cách đó.
Nicholas Flynt

2
Giải pháp này có thể tốt cho hầu hết các ứng dụng, nhưng được cảnh báo rằng giả định rằng một ngày có 60 * 60 * 24 * 1000 không phải lúc nào cũng đúng. Ví dụ, bạn có thể có giây nhuận trong một số ngày.
Pierre-Antoine

1
Đây được cho là cách khó hiểu nhất và không được khuyến nghị so với các phương pháp rõ ràng và ngắn gọn 1 và 2 dòng khác.
Darrell Teague

22

Câu trả lời chuẩn cho những câu hỏi này là sử dụng Joda Time . API tốt hơn và nếu bạn đang sử dụng trình định dạng và trình phân tích cú pháp, bạn có thể tránh được sự thiếu an toàn của luồng không trực quan SimpleDateFormat.

Sử dụng Joda có nghĩa là bạn có thể chỉ cần làm:

LocalDate d = new LocalDate();

Cập nhật :: Sử dụng java 8 có thể đạt được điều này bằng cách sử dụng

LocalDate date = LocalDate.now();

Gói java.time mới trong Java 8 cũng cung cấp một LocalDatelớp tương tự như Joda-Time.
Basil Bourque

1
Lý tưởng nhất là bạn sẽ chuyển một DateTimeZonehàm tạo LocalDate đó. Xác định ngày hiện tại phụ thuộc vào múi giờ, vì Paris bắt đầu một ngày mới sớm hơn Montréal. Nếu bạn bỏ qua múi giờ, múi giờ mặc định của JVM của bạn sẽ được áp dụng. Thông thường tốt hơn để chỉ định hơn dựa vào mặc định.
Basil Bourque

8

Đây là một cách đơn giản để làm điều đó:

Calendar cal = Calendar.getInstance();
SimpleDateFormat dateOnly = new SimpleDateFormat("MM/dd/yyyy");
System.out.println(dateOnly.format(cal.getTime()));

4
Câu trả lời này không giải quyết được câu hỏi. Có được một chuỗi định dạng không phải là mục tiêu.
Basil Bourque

7

Sẽ không có ý nghĩa gì khi nói về một ngày không có dấu thời gian liên quan đến các thói quen Ngày trong thời gian chạy java tiêu chuẩn, vì về cơ bản, nó ánh xạ xuống một mili giây cụ thể chứ không phải là một ngày. Về cơ bản, một phần nghìn giây có thời gian gắn liền với nó khiến nó dễ bị ảnh hưởng bởi các vấn đề múi giờ như Giờ tiết kiệm ánh sáng ban ngày và các điều chỉnh lịch khác. Xem tại sao trừ hai lần này (năm 1927) lại cho kết quả lạ? cho một ví dụ thú vị.

Nếu bạn muốn làm việc với ngày thay vì mili giây, bạn cần sử dụng một cái gì đó khác. Đối với Java 8, có một bộ phương thức mới cung cấp chính xác những gì bạn yêu cầu. Đối với Java 7 trở về trước, hãy sử dụng http://www.joda.org/joda-time/


3
Lưu ý: Những cách tiếp cận nói rằng "ngày vào lúc nửa đêm" không xử lý tốt thời gian tiết kiệm ánh sáng ban ngày và nhiều múi giờ.
Thorbjørn Ravn Andersen

Tôi xác nhận điều đó, tôi đã tăng ngày một ngày trong ứng dụng của mình và đã tìm hiểu về điều này với báo cáo lỗi từ người dùng trong múi giờ với DST (quốc gia của tôi không sử dụng DST). Vào ngày DST bắt đầu, ứng dụng của tôi thêm 11 giờ thay vì 12. Có lẽ sử dụng buổi trưa là giờ cho ngày "không có thời gian" thì tốt hơn?
Jose_GD

1
@Jose_GD Tốt nhất vẫn là hack. Bạn có thể tìm thấy youtube.com/watch?v=-5wpm-gesOY giải trí.
Thorbjørn Ravn Andersen

@ Thorbjorn, bạn nói đúng, chỉ muốn tránh JodaTime vì thêm thư viện cho chỉ một tính năng âm thanh quá mức cần thiết đối với tôi. Tôi biết về video đó, thực sự buồn cười.
Jose_GD

1
@Jose_GD Vấn đề là có một trường hợp sử dụng thực tế trong cuộc sống mà cách tiếp cận của bạn sẽ bị coi là một lỗi. Nếu bạn tìm thấy một cái, có thể có nhiều hơn ... Tôi không biết Joda xử lý việc này như thế nào, nhưng bạn có thể có một cái nhìn. Cũng lưu ý rằng Java 8 mang đến một thư viện thời gian mới (dựa trên các trải nghiệm với Joda) mà bạn có thể muốn xem xét.
Thorbjørn Ravn Andersen

4

Chắc chắn không phải là cách chính xác nhất, nhưng nếu bạn chỉ cần một giải pháp nhanh chóng để có được ngày mà không có thời gian và bạn không muốn sử dụng thư viện của bên thứ ba thì điều này nên làm

    Date db = db.substring(0, 10) + db.substring(23,28);

Tôi chỉ cần ngày cho mục đích trực quan và không thể Joda vì vậy tôi đã xâu chuỗi.


4
// 09/28/2015
System.out.println(new SimpleDateFormat("MM/dd/yyyy").format(Calendar.getInstance().getTime()));

// Mon Sep 28
System.out.println( new Date().toString().substring(0, 10) );

// 2015-09-28
System.out.println(new java.sql.Date(System.currentTimeMillis()));

// 2015-09-28
// java 8
System.out.println( LocalDate.now(ZoneId.of("Europe/Paris")) ); // rest zones id in ZoneId class

3

Theo tôi biết, không có cách nào dễ dàng hơn để đạt được điều này nếu bạn chỉ sử dụng JDK tiêu chuẩn.

Tất nhiên, bạn có thể đặt logic đó trong phương thức 2 vào một hàm tĩnh trong lớp trình trợ giúp, giống như được thực hiện ở đây trong phương thức toBeginningOfTheDay

Sau đó, bạn có thể rút ngắn phương thức thứ hai thành:

Calendar cal = Calendar.getInstance();
Calendars.toBeginningOfTheDay(cal);
dateWithoutTime = cal.getTime();

Hoặc, nếu bạn thực sự cần ngày hiện tại ở định dạng này thường xuyên, thì bạn có thể chỉ cần gói nó trong một phương thức trợ giúp tĩnh khác, từ đó biến nó thành một lớp lót.


3

Nếu tất cả những gì bạn muốn là xem ngày như vậy "YYYY-MM-DD" mà không có sự lộn xộn khác, ví dụ: "Ngày 21 tháng 5 12:08:18 EDT 2015" thì chỉ cần sử dụng java.sql.Date. Ví dụ này nhận được ngày hiện tại:

new java.sql.Date(System.currentTimeMillis());

Cũng java.sql.Datelà một lớp con của java.util.Date.


3

Nếu bạn chỉ cần ngày hiện tại, không có thời gian, một tùy chọn khác là:

DateTime.now().withTimeAtStartOfDay()

1
Câu hỏi yêu cầu không có thời gian trong ngày. Kết quả của bạn là một đối tượng thời gian thực sự có thời gian trong ngày, thời gian đó 00:00:00thường là (có thể thay đổi theo múi giờ cho các dị thường như DST ). Vì vậy, như các câu trả lời khác lưu ý, LocalDatelớp phù hợp hơn, từ Joda-Time (giả sử bạn đang tham khảo Joda-Time - mà bạn nên lưu ý rõ ràng khi trích dẫn các lớp không được gói bằng Java).
Basil Bourque

3

Sử dụng LocalDate.now()và chuyển đổi thành Datenhư dưới đây:

Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());

1
Múi giờ mặc định hiện tại của JVM có thể thay đổi bất cứ lúc nào trong thời gian chạy. Bất kỳ mã nào trong bất kỳ luồng nào của bất kỳ ứng dụng nào trong JVM đều có thể gọi TimeZone.setDefault. Mã của bạn đang gọi ZoneId.systemDefaulthai lần, lần đầu tiên là ẩn LocalDate.now()và lần thứ hai là rõ ràng với atStartOfDay. Ở giữa hai thời điểm đó trong thời gian chạy, vùng mặc định hiện tại có thể thay đổi. Tôi đề nghị chụp vùng này vào một biến và truyền cho cả hai phương thức.
Basil Bourque

2

Nếu bạn cần phần ngày chỉ nhằm mục đích lặp lại, thì

Date d = new Date(); 
String dateWithoutTime = d.toString().substring(0, 10);

1
Tôi không nghĩ đây là i18n thân thiện
Mark Lapasa

2

Cái này thì sao?

public static Date formatStrictDate(int year, int month, int dayOfMonth) {
    Calendar calendar = Calendar.getInstance();
    calendar.set(year, month, dayOfMonth, 0, 0, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    return calendar.getTime();
}

1

Kiểm tra thời gian Veyder . Nó là một sự thay thế đơn giản và hiệu quả cho cả java.util và Joda-time. Nó có API trực quan và các lớp biểu thị ngày một mình, không có dấu thời gian.


1

Cách khó nhất để sử dụng toàn bộ Cơ sở dữ liệu TimeZone khổng lồ của Java và là chính xác:

long currentTime = new Date().getTime();
long dateOnly = currentTime + TimeZone.getDefault().getOffset(currentTime);

1

Đây là một giải pháp sạch không có chuyển đổi thành chuỗi và ngược lại, và nó cũng không tính lại thời gian nhiều lần khi bạn đặt lại từng thành phần của thời gian về không. Nó cũng sử dụng% (mô-đun) thay vì chia theo sau để nhân hoạt động kép.

Nó không yêu cầu sự phụ thuộc của bên thứ ba và nó TRẢ LỜI THỜI GIAN CỦA đối tượng Calender được truyền vào. Hàm này trả về thời điểm đúng lúc 12 giờ sáng theo múi giờ của ngày (Lịch) mà bạn vượt qua.

public static Calendar date_only(Calendar datetime) {
    final long LENGTH_OF_DAY = 24*60*60*1000;
    long millis = datetime.getTimeInMillis();
    long offset = datetime.getTimeZone().getOffset(millis);
    millis = millis - ((millis + offset) % LENGTH_OF_DAY);
    datetime.setTimeInMillis(millis);
    return datetime;
}

Tôi không tin lắm nó sẽ tôn trọng mùa hè tme (DST)?
Ole VV

0

Không thích sử dụng thư viện của bên thứ ba càng nhiều càng tốt. Tôi biết rằng cách này đã được đề cập trước đây, nhưng đây là một cách sạch đẹp:

  /*
    Return values:
    -1:    Date1 < Date2
     0:    Date1 == Date2
     1:    Date1 > Date2

    -2:    Error
*/
public int compareDates(Date date1, Date date2)
{
    SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyyy");

    try
    {
        date1 = sdf.parse(sdf.format(date1));
        date2 = sdf.parse(sdf.format(date2));
    }
    catch (ParseException e) {
        e.printStackTrace();
        return -2;
    }

    Calendar cal1 = new GregorianCalendar();
    Calendar cal2 = new GregorianCalendar();

    cal1.setTime(date1);
    cal2.setTime(date2);

    if(cal1.equals(cal2))
    {
        return 0;
    }
    else if(cal1.after(cal2))
    {
        return 1;
    }
    else if(cal1.before(cal2))
    {
        return -1;
    }

    return -2;
}

Chà, không sử dụng GregorianCalWiki có lẽ là một lựa chọn!


0

Tôi chỉ làm điều này cho ứng dụng của tôi:

public static Date getDatePart(Date dateTime) {
    TimeZone tz = TimeZone.getDefault();
    long rawOffset=tz.getRawOffset();
    long dst=(tz.inDaylightTime(dateTime)?tz.getDSTSavings():0);
    long dt=dateTime.getTime()+rawOffset+dst; // add offseet and dst to dateTime
    long modDt=dt % (60*60*24*1000) ;

    return new Date( dt
                    - modDt // substract the rest of the division by a day in milliseconds
                    - rawOffset // substract the time offset (Paris = GMT +1h for example)
                    - dst // If dayLight, substract hours (Paris = +1h in dayLight)
    );
}

API Android cấp 1, không có thư viện bên ngoài. Nó tôn trọng ánh sáng ban ngày và thời gian mặc định. Không có thao tác chuỗi nào nên tôi nghĩ cách này hiệu quả hơn CPU nhưng tôi chưa thực hiện bất kỳ thử nghiệm nào.


0

Yo có thể sử dụng thời gian joda.

private Date dateWitoutTime(Date date){
 return new LocalDate(date).toDate()
}

và bạn gọi với:

Date date = new Date();
System.out.println("Without Time = " + dateWitoutTime(date) + "/n  With time = " + date);
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.