Java: Làm cách nào để kiểm tra xem Ngày có nằm trong một phạm vi nhất định không?


79

Tôi có một loạt phạm vi với ngày bắt đầu và ngày kết thúc. Tôi muốn kiểm tra xem một ngày có nằm trong phạm vi đó không.

Date.before () và Date. after () có vẻ hơi khó sử dụng. Những gì tôi thực sự cần là một cái gì đó giống như mã giả này:

boolean isWithinRange(Date testDate) {
    return testDate >= startDate && testDate <= endDate;
}

Không chắc liệu nó có liên quan hay không, nhưng những ngày tôi lấy từ cơ sở dữ liệu có dấu thời gian.



1
FYI, các lớp date-time cũ rắc rối như java.util.Datejava.util.Calendarbây giờ là kế thừa , đượ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:


145
boolean isWithinRange(Date testDate) {
   return !(testDate.before(startDate) || testDate.after(endDate));
}

Tôi không thấy khó xử. Lưu ý rằng tôi đã viết nó theo cách đó thay vì

return testDate.after(startDate) && testDate.before(endDate);

vì vậy nó sẽ hoạt động ngay cả khi testDate chính xác bằng một trong các trường hợp kết thúc.


4
với mã này, mã sau không hợp lệ: date is 01-01-2014 and the range is from 01-01-2014 to 31-01-2014làm thế nào bạn có thể làm cho nó hợp lệ?
Shahe

4
@Shahe, điều đầu tiên tôi sẽ làm là kiểm tra phần thời gian trong ngày của bạn. Rất có thể bạn dateđến vào sáng sớm và startDatesau đó.
Paul Tomblin

thủ thuật này có phủ định hoặc vẫn tốt sau bảy năm. :)
Tosz

Nó không hoạt động khi testDate là ngày hiện tại của hôm nay .. Cách xử lý tình huống này trong giải pháp được đề xuất ở trên.
Swift

@Swift Datechứa thời gian (giờ, phút, giây và mili giây). Vì vậy, nếu bạn muốn kiểm tra xem thứ gì đó có phải là "hôm nay" hay không, thì bạn phải tạo một đối tượng Ngày là "hôm nay" tại thời điểm 0: 00: 00.000 và một đối tượng là "ngày mai" tại thời điểm 0: 00: 00.000. Tôi sử dụng Calendarlớp cho điều đó, nhưng tôi chắc chắn có một cách tốt hơn nếu bạn sử dụng JodaTime hoặc các lớp Java cơ sở mới hơn.
Paul Tomblin

26

tl; dr

ZoneId z = ZoneId.of( "America/Montreal" );  // A date only has meaning within a specific time zone. At any given moment, the date varies around the globe by zone.
LocalDate ld = 
    givenJavaUtilDate.toInstant()  // Convert from legacy class `Date` to modern class `Instant` using new methods added to old classes.
                     .atZone( z )  // Adjust into the time zone in order to determine date.
                     .toLocalDate();  // Extract date-only value.

LocalDate today = LocalDate.now( z );  // Get today’s date for specific time zone.
LocalDate kwanzaaStart = today.withMonth( Month.DECEMBER ).withDayOfMonth( 26 );  // Kwanzaa starts on Boxing Day, day after Christmas.
LocalDate kwanzaaStop = kwanzaaStart.plusWeeks( 1 );  // Kwanzaa lasts one week.
Boolean isDateInKwanzaaThisYear = (
    ( ! today.isBefore( kwanzaaStart ) ) // Short way to say "is equal to or is after".
    &&
    today.isBefore( kwanzaaStop )  // Half-Open span of time, beginning inclusive, ending is *exclusive*.
)

Mở một nửa

Công việc theo thời gian thường sử dụng phương pháp "Nửa công khai" để xác định khoảng thời gian. Mở đầu là bao gồm trong khi kết thúc là độc quyền . Vì vậy, một tuần bắt đầu từ Thứ Hai sẽ kéo dài đến, nhưng không bao gồm, Thứ Hai tiếp theo.

java.time

Java 8 trở lên đi kèm với java.timekhuôn khổ được tích hợp sẵn. Bổ sung các lớp rắc rối cũ bao gồm java.util.Date/.CalendarSimpleDateFormat. Lấy cảm hứng từ thư viện Joda-Time thành công. Được xác định bởi JSR 310. Được mở rộng bởi dự án ThreeTen-Extra.

An Instantlà một thời điểm trên dòng thời gian ở UTC với độ phân giải nano giây.

Instant

Chuyển đổi các java.util.Dateđối tượng của bạn thành Đối tượng tức thì.

Instant start = myJUDateStart.toInstant();
Instant stop = …

Nếu nhận java.sql.Timestampcác đối tượng thông qua JDBC từ cơ sở dữ liệu, hãy chuyển đổi sang java.time.Instant theo cách tương tự. A java.sql.Timestampđã ở UTC nên không cần lo lắng về múi giờ.

Instant start = mySqlTimestamp.toInstant() ;
Instant stop = …

Lấy thời điểm hiện tại để so sánh.

Instant now = Instant.now();

So sánh bằng cách sử dụng các phương thức isBefore, isAfter và bằng.

Boolean containsNow = ( ! now.isBefore( start ) ) && ( now.isBefore( stop ) ) ;

LocalDate

Có lẽ bạn chỉ muốn làm việc với ngày chứ không phải thời gian trong ngày.

Các LocalDatelớp đại diện cho một giá trị ngày-chỉ, không có nhiều thời gian trong ngày và không có múi giờ.

LocalDate start = LocalDate.of( 2016 , 1 , 1 ) ;
LocalDate stop = LocalDate.of( 2016 , 1 , 23 ) ;

Để lấy ngày hiện tại, hãy chỉ định múi giờ. Tại bất kỳ thời điểm nào, ngày hôm nay thay đổi theo múi giờ. Ví dụ, một ngày mới ở Paris đến sớm hơn ở Montréal.

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

Chúng tôi có thể sử dụng isEqual, isBeforeisAfterphương pháp để so sánh. Trong công việc theo thời gian, chúng tôi thường sử dụng phương pháp Half-Open trong đó phần đầu của khoảng thời gian là bao gồm trong khi phần cuối là độc quyền .

Boolean containsToday = ( ! today.isBefore( start ) ) && ( today.isBefore( stop ) ) ;

Interval

Nếu bạn chọn thêm thư viện ThreeTen-Extra vào dự án của mình, bạn có thể sử dụng Intervallớp để xác định khoảng thời gian. Lớp đó cung cấp các phương thức để kiểm tra xem khoảng thời gian có chứa , tiếp theo , bao quanh hoặc chồng chéo ngày-giờ / khoảng thời gian khác hay không.

Các Intervallớp hoạt động trên Instantcác đối tượng. 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).

Chúng ta có thể điều chỉnh LocalDatethời điểm cụ thể, thời điểm đầu tiên trong ngày, bằng cách chỉ định múi giờ để lấy a ZonedDateTime. Từ đó, chúng ta có thể quay lại UTC bằng cách giải nén a Instant.

ZoneId z = ZoneId.of( "America/Montreal" );
Interval interval = 
    Interval.of( 
        start.atStartOfDay( z ).toInstant() , 
        stop.atStartOfDay( z ).toInstant() );
Instant now = Instant.now();
Boolean containsNow = interval.contains( now );

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 di cư đến java.timecác lớp học.

Để tìm hiểu thêm, hãy xem Hướng dẫn Oracle . Và tìm kiếm Stack Overflow để có nhiều ví dụ và giải thích. Đặc điểm kỹ thuật là JSR 310 .

Lấy các java.timelớp học ở đâu?

  • Java SE 8 SE 9 trở lên
  • Được xây dựng trong.
  • Một phần của API Java tiêu chuẩn với một triển khai đóng gói.
  • Java 9 bổ sung một số tính năng nhỏ và các bản sửa lỗi.
  • Java SE 6 SE 7
  • Phần lớn java.timechức năng được chuyển ngược sang Java 6 & 7 trong ThreeTen-Backport .
  • Android
  • Các ThreeTenABP dự án thích nghi ThreeTen-backport (nêu trên) cho Android cụ thể.
  • Xem Cách sử dụng… .

Các ThreeTen-Extra dự án mở rộng java.timevới các lớp bổ sung. Dự án này là cơ sở chứng minh cho khả năng bổ sung trong tương lai 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 .


16

Đó là cách chính xác. Lịch hoạt động theo cùng một cách. Điều tốt nhất tôi có thể cung cấp cho bạn (dựa trên ví dụ của bạn) là:

boolean isWithinRange(Date testDate) {
    return testDate.getTime() >= startDate.getTime() &&
             testDate.getTime() <= endDate.getTime();
}

Date.getTime () trả về số mili giây kể từ ngày 1/1/1970 00:00:00 GMT và là một khoảng thời gian dài nên có thể dễ dàng so sánh.


9

Cân nhắc sử dụng Joda Time . Tôi yêu thư viện này và ước gì nó sẽ thay thế mớ hỗn độn khủng khiếp hiện tại là các lớp Ngày và Lịch Java hiện có. Đó là xử lý ngày được thực hiện đúng.

CHỈNH SỬA: Nó không phải là năm 2009 nữa, và Java 8 đã ra đời từ lâu. Sử dụng các lớp java.time được xây dựng trong Java 8 dựa trên Joda Time, như Basil Bourque đã đề cập ở trên. Trong trường hợp này, bạn sẽ muốn lớp Giai đoạn và đây là hướng dẫn của Oracle về cách sử dụng nó.


3
không bạn tin rằng giới thiệu Joda Thời gian cho một dự án là theo thời gian nếu tất cả ai muốn làm là làm một so sánh đơn giản như người hỏi chỉ -?
hhafez

3
Tôi cho rằng bạn có nghĩa là quá mức cần thiết. Trong trường hợp của hầu hết các bổ sung thư viện, tôi sẽ đồng ý với bạn. Tuy nhiên, Joda Time khá nhẹ và giúp bạn viết mã xử lý ngày tháng chính xác hơn nhờ tính bất biến của các biểu diễn của nó, vì vậy theo tôi không phải là một điều tồi tệ để giới thiệu.
Ben Hardy

25
Và đừng quên quy tắc StackOverflow # 46: Nếu ai đó đề cập đến ngày hoặc giờ trong Java, thì ai đó bắt buộc phải đề xuất chuyển sang Joda Time. Không tin tôi? Cố gắng tìm một câu hỏi không.
Paul Tomblin

2
Thậm chí Sun / Oracle đã từ bỏ các lớp date-time cũ đi kèm với các phiên bản Java sớm nhất, đồng ý thay thế các lớp cũ bằng khung java.time (lấy cảm hứng từ Joda-Time). Những lớp cũ đó được thiết kế kém, và đã được chứng minh là rắc rối và khó hiểu.
Basil Bourque

4

Một cách dễ dàng là chuyển đổi ngày tháng thành mili giây sau ngày 1 tháng 1 năm 1970 (sử dụng Date.getTime ()) và sau đó so sánh các giá trị này.


1

Đối với trường hợp của tôi -> Tôi có ngày bắt đầu & ngày kết thúc của phạm vi và danh sách ngày có thể nằm một phần trong phạm vi được cung cấp, như đầy đủ (chồng chéo).

Giải pháp bao gồm các thử nghiệm:

/**
 * Check has any of quote work days in provided range.
 *
 * @param startDate inclusively
 * @param endDate   inclusively
 *
 * @return true if any in provided range inclusively
 */
public boolean hasAnyWorkdaysInRange(LocalDate startDate, LocalDate endDate) {
    if (CollectionUtils.isEmpty(workdays)) {
        return false;
    }

    LocalDate firstWorkDay = getFirstWorkDay().getDate();
    LocalDate lastWorkDay = getLastWorkDay().getDate();

    return (firstWorkDay.isBefore(endDate) || firstWorkDay.equals(endDate))
            && (lastWorkDay.isAfter(startDate) || lastWorkDay.equals(startDate));
}

1

Từ Java 8

LƯU Ý: Tất cả, trừ một, Datecác hàm tạo của không được dùng nữa. Hầu hết các Datephương pháp của đều không được dùng nữa. REF: Ngày: Phương thức không được dùng nữa . Tất cả, nhưng Date::from(Instant), các phương thức tĩnh không được dùng nữa.

Vì vậy, vì java 8 hãy cân nhắc sử dụng kiểu Instant (không thay đổi, an toàn cho luồng, nhận biết giây nhảy vọt) hơn là Date. REF: Tức thì

  static final LocalTime MARKETS_OPEN = LocalTime.of(07, 00);
  static final LocalTime MARKETS_CLOSE = LocalTime.of(20, 00);

    // Instant utcTime = testDate.toInstant();
    var bigAppleTime = ZonedDateTime.ofInstant(utcTime, ZoneId.of("America/New_York"));

trong phạm vi BAO GỒM:

    return !bigAppleTime.toLocalTime().isBefore(MARKETS_OPEN)
        && !bigAppleTime.toLocalTime().isAfter(MARKETS_CLOSE);

trong phạm vi ĐỘC QUYỀN:

    return bigAppleTime.toLocalTime().isAfter(MARKETS_OPEN)
        && bigAppleTime.toLocalTime().isBefore(MARKETS_CLOSE);

0

Không quan tâm giới hạn ngày tháng nào.

Math.abs(date1.getTime() - date2.getTime()) == 
    Math.abs(date1.getTime() - dateBetween.getTime()) + Math.abs(dateBetween.getTime() - date2.getTime());

0

bạn có thể sử dụng như thế này

Interval interval = new Interval(date1.getTime(),date2.getTime());
Interval interval2 = new Interval(date3.getTime(), date4.getTime());
Interval overlap = interval.overlap(interval2);
boolean isOverlap = overlap == null ? false : true

0

Điều này rõ ràng hơn với tôi,

// declare calendar outside the scope of isWithinRange() so that we initialize it only once
private Calendar calendar = Calendar.getInstance();

public boolean isWithinRange(Date date, Date startDate, Date endDate) {

    calendar.setTime(startDate);
    int startDayOfYear = calendar.get(Calendar.DAY_OF_YEAR); // first day is 1, last day is 365
    int startYear = calendar.get(Calendar.YEAR);

    calendar.setTime(endDate);
    int endDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
    int endYear = calendar.get(Calendar.YEAR);

    calendar.setTime(date);
    int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
    int year = calendar.get(Calendar.YEAR);

    return (year > startYear && year < endYear) // year is within the range
            || (year == startYear && dayOfYear >= startDayOfYear) // year is same as start year, check day as well
            || (year == endYear && dayOfYear < endDayOfYear); // year is same as end year, check day as well

}

2
(A) Calendarlớp không an toàn theo luồng. Vì vậy, việc duy trì một phiên bản có thể được nhiều hơn một luồng truy cập là rất rủi ro. (B) Các lớp cũ rắc rối như java.util.Datejava.util.Calendarbây giờ là kế thừa , được thay thế bởi các lớp java.time . Xem Hướng dẫn Oracle .
Basil Bourque

@BasilBourque, cảm ơn bạn đã chỉ ra vấn đề. Có vẻ như Android chưa hỗ trợ lớp DateTime mới. : /
Dhunju_likes_to_Learn

1
Phần lớn chức năng java.time được chuyển ngược sang Java 6 & 7 trong ThreeTen-Backport và được điều chỉnh thêm cho Android trong ThreeTenABP (xem Cách sử dụng… ). Và, Câu hỏi này không phải về Android.
Basil Bourque

0
  public class TestDate {

    public static void main(String[] args) {
    // TODO Auto-generated method stub

    String fromDate = "18-FEB-2018";
    String toDate = "20-FEB-2018";

    String requestDate = "19/02/2018";  
    System.out.println(checkBetween(requestDate,fromDate, toDate));
}

public static boolean checkBetween(String dateToCheck, String startDate, String endDate) {
    boolean res = false;
    SimpleDateFormat fmt1 = new SimpleDateFormat("dd-MMM-yyyy"); //22-05-2013
    SimpleDateFormat fmt2 = new SimpleDateFormat("dd/MM/yyyy"); //22-05-2013
    try {
     Date requestDate = fmt2.parse(dateToCheck);
     Date fromDate = fmt1.parse(startDate);
     Date toDate = fmt1.parse(endDate);
     res = requestDate.compareTo(fromDate) >= 0 && requestDate.compareTo(toDate) <=0;
    }catch(ParseException pex){
        pex.printStackTrace();
    }
    return res;
   }
 }

2
Vui lòng thêm một số từ để mô tả ngắn gọn / giải thích lý do và cách mã này trả lời câu hỏi.
Yannis

1
Rất vui khi bạn muốn đóng góp, cảm ơn. (1) Xin đừng dạy những người trẻ tuổi sử dụng SimpleDateFormatlớp học đã lỗi thời và khét tiếng là rắc rối . Ít nhất không phải là tùy chọn đầu tiên. Và không phải không có bất kỳ đặt trước. Hôm nay chúng ta có rất nhiều thứ tốt hơn java.time, API ngày giờ hiện đại của Java và nó DateTimeFormatter. (2) Các câu trả lời chỉ có mã là rất hiếm khi hữu ích. Đó là từ lời giải thích kèm theo mà chúng ta cùng tìm hiểu.
Ole VV

1
Đề xuất vào năm 2018 việc sử dụng các lớp cực kỳ rắc rối này đã được thay thế các năm trước là một lời khuyên tồi. Hơn nữa, mã của bạn bỏ qua vấn đề quan trọng về múi giờ.
Basil Bourque

-1

logic của bạn sẽ hoạt động tốt. Như bạn đã đề cập, ngày bạn nhận được từ cơ sở dữ liệu nằm trong dấu thời gian, Bạn chỉ cần chuyển đổi dấu thời gian thành ngày tháng đầu tiên và sau đó sử dụng logic này.

Cũng đừng quên kiểm tra các ngày trống.

đây m chia sẻ một chút để chuyển đổi từ Timestamp đến nay.

public static Date convertTimeStamptoDate (String val) ném Exception {

    DateFormat df = null;
    Date date = null;

    try {
        df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        date = df.parse(val);
        // System.out.println("Date Converted..");
        return date;
    } catch (Exception ex) {
        System.out.println(ex);
        return convertDate2(val);
    } finally {
        df = null;
        date = null;
    }
}
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.