Làm cách nào để trừ X ngày kể từ ngày sử dụng lịch Java?


180

Bất cứ ai cũng biết một cách đơn giản sử dụng lịch Java để trừ X ngày kể từ ngày?

Tôi không thể tìm thấy bất kỳ hàm nào cho phép tôi trừ trực tiếp X ngày kể từ ngày trong Java. Ai đó có thể chỉ cho tôi đi đúng hướng?


FYI, các lớp thời gian cũ rắc rối như java.util.Calendarbây giờ là di sản , được thay thế bởi các lớp java.time . Xem Hướng dẫn của Oracle .
Basil Bourque

Câu trả lời:


309

Lấy từ các tài liệu ở đây :

Thêm hoặc trừ lượng thời gian đã chỉ định vào trường lịch đã cho, dựa trên quy tắc của lịch. Ví dụ: để trừ 5 ngày kể từ thời điểm hiện tại của lịch, bạn có thể đạt được nó bằng cách gọi:

Calendar calendar = Calendar.getInstance(); // this would default to now
calendar.add(Calendar.DAY_OF_MONTH, -5).

1
Chỉ cần cẩn thận làm điều này vì nó không phải lúc nào cũng cuộn như bạn mong đợi.
caron

1
Câu trả lời này có nhiều upvote, nhưng nó có an toàn để sử dụng không? hoặc điều này là tốt hơn: stackoverflow.com/a/10796111/948268
Kuldeep Jain

44
Bạn sẽ không sử dụng Calendar.DAY_OF_MONTHtrong trường hợp này vì nó sẽ không xử lý cuộn giữa các tháng như bạn muốn. Sử dụng Calendar.DAY_OF_YEARthay thế
thuật toán

4
Điều này sẽ an toàn, rõ ràng khi bạn thêm nó không thành vấn đề nếu đó là DAY_OF_MONTH hoặc DAY_OF_YEAR stackoverflow.com/q/14065198/32453
rogerdpack

@carson một số vấn đề với phương pháp này là gì?
tatmanblue

38

Bạn có thể sử dụng addphương thức và truyền cho nó một số âm. Tuy nhiên, bạn cũng có thể viết một phương thức đơn giản hơn mà không sử dụng Calendarlớp như sau

public static void addDays(Date d, int days)
{
    d.setTime( d.getTime() + (long)days*1000*60*60*24 );
}

Điều này nhận giá trị dấu thời gian của ngày (mili giây kể từ kỷ nguyên) và thêm số mili giây thích hợp. Bạn có thể truyền một số nguyên âm cho tham số ngày để thực hiện phép trừ. Điều này sẽ đơn giản hơn giải pháp lịch "thích hợp":

public static void addDays(Date d, int days)
{
    Calendar c = Calendar.getInstance();
    c.setTime(d);
    c.add(Calendar.DATE, days);
    d.setTime( c.getTime().getTime() );
}

Lưu ý rằng cả hai giải pháp này đều thay đổi Dateđối tượng được truyền dưới dạng tham số thay vì trả về hoàn toàn mới Date. Một trong hai chức năng có thể dễ dàng thay đổi để làm điều đó theo cách khác nếu muốn.


3
Đừng tin .setTime và .add là các phương thức tĩnh của Lịch. Bạn nên sử dụng biến thể c.
Edward

@Edward: bạn đã đúng, cảm ơn vì đã chỉ ra lỗi của tôi, tôi đã sửa mã để nó hoạt động bình thường.
Eli Courtwright

3
Hãy cẩn thận với cách đầu tiên, ví dụ như khi giao dịch với những ngày nhuận, nó có thể thất bại: stackoverflow.com/a/1006388/32453
rogerdpack

FYI, các lớp thời gian cũ rắc rối như java.util.Calendarbây giờ là di sản , được thay thế bởi các lớp java.time . Xem Hướng dẫn của Oracle .
Basil Bourque

36

Câu trả lời của Anson sẽ hoạt động tốt trong trường hợp đơn giản, nhưng nếu bạn sẽ thực hiện bất kỳ phép tính ngày phức tạp nào hơn, tôi khuyên bạn nên kiểm tra Joda Time . Nó sẽ làm cho cuộc sống của bạn dễ dàng hơn nhiều.

FYI trong Joda Thời gian bạn có thể làm

DateTime dt = new DateTime();
DateTime fiveDaysEarlier = dt.minusDays(5);

1
@ShahzadImam, hãy xem DateTimeFormat . Nó sẽ cho phép bạn chuyển đổi các thể hiện DateTime thành các chuỗi bằng các định dạng tùy ý. Nó rất giống với lớp java.text.DateFormat.
Mike Deck

1
Kể từ Java 8, hãy xem xét sử dụng java.time docs.oracle.com/javase/8/docs/api/java/time/ Kẻ
toidiu

FYI, dự án Joda-Time 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

19

tl; dr

LocalDate.now().minusDays( 10 )

Tốt hơn để xác định múi giờ.

LocalDate.now( ZoneId.of( "America/Montreal" ) ).minusDays( 10 )

Chi tiết

Các lớp thời gian cũ được gói cùng với các phiên bản đầu tiên của Java, như java.util.Date/ .Calendar, đã được chứng minh là rắc rối, khó hiểu và thiếu sót. Tránh chúng.

java.time

Java 8 và sau đó thay thế các lớp cũ đó bằng khung java.time mới. Xem hướng dẫn . Được xác định bởi JSR 310 , lấy cảm hứng từ Joda-Time và được mở rộng bởi dự án ThreeTen-Extra . Dự án ThreeTen-Backport chuyển ngược các lớp sang Java 6 & 7; dự án ThreeTenABP cho Android.

Câu hỏi mơ hồ, không rõ ràng nếu nó yêu cầu chỉ ngày hoặc thời gian.

LocalDate

Đối với một ngày duy nhất, không có thời gian trong ngày, sử dụng LocalDatelớp. Lưu ý rằng múi giờ rất quan trọng trong việc xác định ngày, chẳng hạn như "hôm nay".

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

ZonedDateTime

Nếu bạn có nghĩa là một thời gian ngày, sau đó sử dụng Instantlớp để có được một khoảnh khắc trên dòng thời gian trong UTC . Từ đó, điều chỉnh theo múi giờ để lấy ZonedDateTimeđối tượng.

Instant now = Instant.now();  // UTC.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
ZonedDateTime tenDaysAgo = zdt.minusDays( 10 );

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 , Java SE 10 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 .

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 .


Đây là một cải tiến tốt hơn nhiều so với các tùy chọn khác (nó cũng mới hơn nhiều và sử dụng các phương thức mới). Nếu bạn đang truy cập vấn đề này bây giờ, Đây là cách để đi.
Shourya Bansal


5

Thay vì tự viết addDaystheo đề xuất của Eli, tôi thích sử dụng DateUtilstừ Apache . Nó rất tiện dụng đặc biệt là khi bạn phải sử dụng nó nhiều nơi trong dự án của bạn.

API nói:

addDays(Date date, int amount)

Thêm một số ngày vào một ngày trả lại một đối tượng mới.

Lưu ý rằng nó trả về một Dateđối tượng mới và không thay đổi đối tượng trước đó.


2

Nó có thể được thực hiện dễ dàng bằng cách sau đây

Calendar calendar = Calendar.getInstance();
        // from current time
        long curTimeInMills = new Date().getTime();
        long timeInMills = curTimeInMills - 5 * (24*60*60*1000);    // `enter code here`subtract like 5 days
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());

        // from specific time like (08 05 2015)
        calendar.set(Calendar.DAY_OF_MONTH, 8);
        calendar.set(Calendar.MONTH, (5-1));
        calendar.set(Calendar.YEAR, 2015);
        timeInMills = calendar.getTimeInMillis() - 5 * (24*60*60*1000);
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());

Câu trả lời này không được khuyến khích, sử dụng các lớp thời gian cũ khủng khiếp mà bây giờ là di sản, được thay thế bởi các lớp java.time. Ngoài ra, mã này bỏ qua vấn đề quan trọng của múi giờ, giả sử ngây thơ 24 ngày. Một vấn đề khác là sử dụng lớp thời gian để giải quyết vấn đề chỉ có ngày. Sử dụng LocalDateđơn giản hơn nhiều và phù hợp hơn.
Basil Bourque

1

Có người đề nghị Joda Thời gian như vậy - Tôi đã được sử dụng này CalendarDate lớp http://calendardate.sourceforge.net

Đây là một dự án hơi cạnh tranh với Joda Time, nhưng cơ bản hơn nhiều chỉ có 2 lớp. Nó rất tiện dụng và hoạt động tốt cho những gì tôi cần vì tôi không muốn sử dụng gói lớn hơn dự án của mình. Không giống như các đối tác Java, đơn vị nhỏ nhất của nó là ngày nên nó thực sự là một ngày (không giảm xuống còn mili giây hoặc một cái gì đó). Khi bạn tạo ngày, tất cả những gì bạn làm để trừ là một cái gì đó như myDay.addDays (-5) để quay lại 5 ngày. Bạn có thể sử dụng nó để tìm ngày trong tuần và những thứ tương tự. Một vi dụ khac:

CalendarDate someDay = new CalendarDate(2011, 10, 27);
CalendarDate someLaterDay = today.addDays(77);

Và:

//print 4 previous days of the week and today
String dayLabel = "";
CalendarDate today = new CalendarDate(TimeZone.getDefault());
CalendarDateFormat cdf = new CalendarDateFormat("EEE");//day of the week like "Mon"
CalendarDate currDay = today.addDays(-4);
while(!currDay.isAfter(today)) {
    dayLabel = cdf.format(currDay);
    if (currDay.equals(today))
        dayLabel = "Today";//print "Today" instead of the weekday name
    System.out.println(dayLabel);
    currDay = currDay.addDays(1);//go to next day
}

1

Tôi tin rằng một cách sạch sẽ và tốt đẹp để thực hiện phép trừ hoặc thêm bất kỳ đơn vị thời gian nào (tháng, ngày, giờ, phút, giây, ...) có thể đạt được bằng cách sử dụng java.time.Instant lớp .

Ví dụ để trừ 5 ngày kể từ thời điểm hiện tại và nhận kết quả là Ngày:

new Date(Instant.now().minus(5, ChronoUnit.DAYS).toEpochMilli());

Một ví dụ khác để trừ 1 giờ và thêm 15 phút:

Date.from(Instant.now().minus(Duration.ofHours(1)).plus(Duration.ofMinutes(15)));

Nếu bạn cần độ chính xác cao hơn, Instance đo đến tối đa nano giây. Phương pháp thao tác phần nano giây:

minusNano()
plusNano()
getNano()

Ngoài ra, hãy nhớ rằng Ngày đó không chính xác như Tức thì.


1
Hoặc tùy theo khẩu vị, ngắn hơn một chút:Date.from(Instant.now().minus(5, ChronoUnit.DAYS))
Ole VV

1
Đẹp! Bạn đúng rồi. Tôi thực sự đã cập nhật câu trả lời để sử dụng cái này và cũng hiển thị cách sử dụng lớp java.time.Duration, trong trường hợp này cũng rất mạnh.
Yordan Boev

0

Giải pháp thứ hai của Eli Courtwright là sai, cần phải là:

Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(Calendar.DATE, -days);
date.setTime(c.getTime().getTime());

2
Tên của hàm trong giải pháp của Eli là addDays; Giải pháp của anh ấy là chính xác. Nếu bạn muốn trừ ngày kể từ ngày, bạn chuyển vào một giá trị âm cho days.
Thomas Upton

Vâng, đó là điều mà lập trình viên phải chỉ định khi tạo hàm, phải không? : P
dùng178973
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.