1

i have this code in my program:

if (expire.after(start) && expire.before(end))
    //do somethig

But this code does not include the bound day. I mean for example if start is 2014/01/15, end is 2014/01/20 and expire is 2014/01/20, expire is not considered. How to solve?

I tried in this way:

if (expire.after(start) && (expire.equals(end) || expire.before(end)))

But it doesn't work, i get the same result.

smartmouse
  • 13,912
  • 34
  • 100
  • 166

5 Answers5

1

When I have done time checks in the past I always used only after and before, even when I has a range check that was start <= current <= end.

You achieve this by adjusting the start and end dates such that they are outside the bounds of the desired range.

For example, if the start date is 2014/01/15, then use the start date value of 2014/01/14 23:59:59 as the actual start date that is used for the comparison. For the end date, instead of 2014/01/20, use 2014/01/21 00:00:00. With these values you can use .after(startDate) and .before(endDate).

Here is some code to calculate the start date:

    private static Date calculateStartDate(
        final int year,
        final int month,
        final int dayOfMonth)
    {
        Calendar calendar = Calendar.getInstance();
        Date returnValue;

        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month);
        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        calendar.set(Calendar.HOUR, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);

        returnValue = calendar.getTime();

        return returnValue;
    }

Edit added millisecond above

DwB
  • 37,124
  • 11
  • 56
  • 82
  • You have forgotten to code `calendar.set(Calendar.MILLISECOND, 999);`. This is relevant if you for example retrieve a calendar instance based on current time. – Meno Hochschild Feb 05 '14 at 15:48
  • This answer is close but not quite right. More common is the "half-open" approach, to make the start of the time span *inclusive* but the end of the time span *exclusive*. See the Interval, Period, and Duration classes in [Joda-Time](http://www.joda.org/joda-time/). – Basil Bourque Feb 06 '14 at 09:42
1

As @assylias stated you might try !expire.after(end) although this is probably not enough. If you instead use expire.equals(end) then you have to keep in mind that this is not just a temporal comparison! Here you also compare time zone informations, even the locale and so on. In general it is not good to use GregorianCalendar for only date comparisons because this type also knows milliseconds as time part. So you have probably either to manually set the whole time part of all three calendar instances to zero (midnight, leaving out time zone anomalies like in Brazil at certain days) or much better, you should instead extract the date informations and compare these details by using following tool:

public class DateComparator implements Comparator<GregorianCalendar> {
  public int compare(GregorianCalendar gcal1, GregorianCalendar gcal2) {
    int y1 = gcal1.get(Calendar.YEAR);
    int y2 = gcal2.get(Calendar.YEAR);
    if (y1 != y2) {
       return y1 - y2;
    }

    int m1 = gcal1.get(Calendar.MONTH);
    int m2 = gcal2.get(Calendar.MONTH);
    if (m1 != m2) {
       return m1 - m2;
    }
    return gcal1.get(Calendar.DATE) - gcal2.get(Calendar.DATE);
  }
}

But make sure that you always have the same time zone when comparing the date parts. Otherwise you can try JodaTime which offers the type LocalDate. And a similar date-only type is also contained in new Java 8.

Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
0

Or

if ((expire.compareTo(start) >= 0) && (expire.compareTo(end) <= 0))

since

http://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html#after(java.lang.Object)

says .before is equivalent to compareTo(when) < 0 and .after is equivalent to compareTo(when) > 0

Oliver Kohll
  • 772
  • 2
  • 7
  • 19
  • or of course (expire.after(start) && (expire.compareTo(end) <=0) if you want to be inclusive of end but not start – Oliver Kohll Feb 05 '14 at 15:18
  • It seems the right solution, but it doesn't work for the reason explained above in the post by Meno Hochschild – smartmouse Feb 05 '14 at 15:24
0

I finally solved adjusting the start and end dates such that they are outside the bounds of the desired range.

For example, if the start date is 2014/01/15, i set it to 2014/01/14 and if the end date is 2014/01/20, i set it to 2014/01/21. With these values i can use .after and .before methods with no problems.

Thank you to DwB for the suggestion and than you to all for your great support!

smartmouse
  • 13,912
  • 34
  • 100
  • 166
0

tl;dr

Use java.time.LocalDate for a date-only value.

( ! ( myLocalDate.isBefore( start ) )   // Is target date NOT before the start (meaning, is the target equal to or later than
&& 
myLocalDate.isBefore( stop )

…or, better:

org.threeten.extra.LocalDateRange.of(            // Half-Open range.
    LocalDate.of( 2014 , Month.JANUARY , 15 ) ,  // Inclusive.
    LocalDate.of( 2014 , Month.JANUARY , 20 )    // Exclusive.
).contains(
    LocalDate.of( 2014 , Month.JANUARY , 11 )
)

false

GregorianCalendar is a troublesome obsolete class. Avoid it.

Details

I do not understand the logic of your business problem, but perhaps this example code will help.

I believe you'll find that working with time spans in the "half-open" approach works best. The start of the span is inclusive, and the ending is exclusive. For more discussion see another answer of mine with this chart…

enter image description here

java.time

Here is some code using the modern java.time classes built into Java 8 and later.

LocalDate

The LocalDate class represents a date-only value without time-of-day and without time zone.

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment, so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

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

If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the JVM’s current default is applied implicitly. Better to be explicit, as the default may be changed at any moment during runtime by any code in any thread of any app within the JVM.

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

Or specify a date. You may set the month by a number, with sane numbering 1-12 for January-December.

LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.

Or, better, use the Month enum objects pre-defined, one for each month of the year. Tip: Use these Month objects throughout your codebase rather than a mere integer number to make your code more self-documenting, ensure valid values, and provide type-safety.

LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;

Comparing dates

The LocalDate class offers comparison methods: isBefore, isAfter, isEqual, equals, and compareTo.

Not exactly sure from your Question what your comparison rules are. But I guess you are missing the concept of using the ! operator for "NOT" condition.

Using the Half-Open approach we want to ask if the target date "is equal to or later than the beginning AND before than ending".

LocalDate start = LocalDate.of( 2014 , Month.JANUARY , 15 ) ;
LocalDate stop = LocalDate.of( 2014 , Month.JANUARY , 20 ) ;

boolean rangeContainsTarget = ( ld.isEqual( start ) || (ld.isAfter( start ) ) && ld.isBefore( stop ) ;  // Half-Open test, the long-way. "is equal to or later than the beginning AND before than ending"

A shorter way to ask "is equal to or later than" is "is not before".

boolean rangeContainsTarget = ( ! ( ld.isBefore( start ) ) && ld.isBefore( stop ) ;  // Half-Open test, the long-way. "is not before the beginning AND before than ending"

Range object

Add the ThreeTen-Extra library to your project to access the LocalDateRange class. It defines a range of time as a pair of LocalDate objects. The class offers handy methods such as abuts, contains, intersection, and more. It uses the Half-Open approach.

LocalDateRange range = LocalDateRange.of( start , stop ) ;
boolean rangeContainsTarget = range.contains( target ) ;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.


Joda-Time

UPDATE: The Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. This section left intact for history.

Look at this code using the Joda-Time 2.3 library.

// Specify a time zone rather than rely on default.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Oslo" );

DateTime event = new DateTime( 2014, 1, 20, 23, 59, 59, timeZone );

DateTime begin = new DateTime( 2014, 01, 15, 0, 0, 0, timeZone ); // Inclusive
// After the stroke of midnight of the 20th, on the first moment of the new day (the 21st), event is late.
DateTime end = new DateTime( 2014, 01, 21, 0, 0, 0, timeZone ); // Exclusive. All split-second moments before this are acceptable, but this moment and onwards is not acceptable.

Interval interval = new Interval( begin, end );
boolean isEventOnSchedule = interval.contains( event );

Dump to console…

System.out.println( "event: " + event );
System.out.println( "begin: " + begin );
System.out.println( "end: " + end );
System.out.println( "interval: " + interval );
System.out.println( "isEventOnSchedule: " + isEventOnSchedule );

When run…

event: 2014-01-20T23:59:59.000+01:00
begin: 2014-01-15T00:00:00.000+01:00
end: 2014-01-21T00:00:00.000+01:00
interval: 2014-01-15T00:00:00.000+01:00/2014-01-21T00:00:00.000+01:00
isEventOnSchedule: true
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154