Calendar comparison - java

I am storing current date in SQLite DB as
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
When I try to compare Calendar objects, its always shows objects as not equal.
Here is my code.
Creating Calendar from Database string
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendarDB = Calendar.getInstance();
String date = "extracted only date (2014-03-03 for ex) from DB value ignoring time";
calendarDB.setTime(sdf.parse(date));
Current Calendar instance
Calendar calendarCurrent = Calendar.getInstance();
Comparison... I see not equal for all the instances.
if(calendarDB.equals(calendarCurrent))
Log.i(TAG, "equal!!!");
else
Log.i(TAG, "Not equal!!!");
I can see both Calendar instance values like day, month, year equal in Log.
What's wrong with it?

Calendar.getInstance() is not singleton. It every time creates a new instance of different implementations of java.util.Calendar based on Locale
Secondly, if you check equals method of Calendar, it checks lot more, than just the date
public boolean equals(Object obj) {
if (this == obj)
return true;
try {
Calendar that = (Calendar)obj;
return compareTo(getMillisOf(that)) == 0 &&
lenient == that.lenient &&
firstDayOfWeek == that.firstDayOfWeek &&
minimalDaysInFirstWeek == that.minimalDaysInFirstWeek &&
zone.equals(that.zone);
} catch (Exception e) {
// Note: GregorianCalendar.computeTime throws
// IllegalArgumentException if the ERA value is invalid
// even it's in lenient mode.
}
return false;
}
If you want to compare 2 dates then, you can do
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date one = new Date(); // one = calOne.getTime();
Date two = new Date(); //two = calTwo.getTime();
sdf.format(one).equals(sdf.format(two));

That's happens because a Calendar equals method compare to next fields :
#Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof Calendar)) {
return false;
}
Calendar cal = (Calendar) object;
return getTimeInMillis() == cal.getTimeInMillis()
&& isLenient() == cal.isLenient()
&& getFirstDayOfWeek() == cal.getFirstDayOfWeek()
&& getMinimalDaysInFirstWeek() == cal.getMinimalDaysInFirstWeek()
&& getTimeZone().equals(cal.getTimeZone());
}
So you need to check :
millis
isLenient
first day of week
minimal days in first week
and time zone
As you can see equals method need a same TimeZone in comaprable object.

tl;dr
LocalDate.now( ZoneId( "Pacific/Auckland" ) ) // Get today’s date in a particular time zone.
.isEqual( // Test for equality against a `LocalDate` object to be retrieved from database.
myResultSet.getObject( … , Instant.class ) // Retrieve a moment in UTC from the database, an `Instant` object.
.atZone( ZoneId( "Pacific/Auckland" ) ) // Produce a `ZonedDateTime` to represent the same moment in time but with the wall-clock time of a particular region’s time zone.
.toLocalDate() // Extract a date-only value, without time-of-day and without time zone.
)
java.time
The modern approach uses the java.time classes.
With JDBC 4.2 and later, you may directly exchange java.time objects with your database. No need for the troublesome Calendar class, no need for mere strings.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = myResultSet.getObject( … , Instant.class ) ;
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.
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" ) ;
Apply the ZoneId to your Instant to get a ZonedDateTime.
ZonedDateTime zdt = instant.atZone( z ) ;
If you care about only the date portion, and not the time-of-day, extract a LocalDate.
LocalDate ld = zdt.toLocalDate() ;
Compare to the current date.
LocalDate today = LocalDate.now( z ) ;
Boolean isSameDateAsToday = ld.isEqual( today ) ;
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?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

Related

Compare current Date with given Date Java [duplicate]

This question already has answers here:
How to check if a date is greater than another in Java? [duplicate]
(4 answers)
Closed 2 years ago.
My date time formatter is , "yyyy-MM-DD"
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-DD")
I want to check the date is later than today's date or not. I checked it using this validation.
if(dateFormatter.format(given_Date).compareTo(dateFormatter.format(new Date())) > 0){ ...}
But every time this returns false when the given date is later or not. Can anyone help with this me, please?
tl;dr
You asked:
check the date is later than today's date
LocalDate // Represent a date-only value, without time-of-day and without time zone or offset-from-UTC.
.parse( "2020-01-23" ) // No need to specify formatting pattern when parsing a string in standard ISO 8601 format. Returns a `LocalDate` object.
.isAfter // Compare one `LocalDate` object to another.
(
LocalDate.now // Capture the current date as seen in a particular time zone.
(
ZoneId.of( "Africa/Tunis" ) // or: ZoneId.systemDefault()
) // Returns a `LocalDate` object.
) // Returns `boolean`.
Details
Modern solution uses java.time classes, specifically java.time.LocalDate. Compare with isAfter method. You are using terrible date-time classes that were years ago supplanted by java.time.
No need to specify a formatting pattern. Your input strings comply with the ISO 8601 standard used by default in java.time.
By the way, formatting codes are case-sensitive, and day-of-month is dd rather than the DD you used. So the formatting pattern used here by default is akin to uuuu-MM-dd.
boolean isFuture = LocalDate.parse( "2020-01-23" ).isAfter( LocalDate.now() ) ;
Better to explicitly specify desired/expected time by which to determine today’s date. If omitted, the JVM’s current default time zone is implicitly applied.
boolean isFuture = LocalDate.parse( "2020-01-23" ).isAfter( LocalDate.now( ZoneId.of( "America/Edmonton" ) ) ) ;
If you can work with the variable givenDate as a String there is another way. Check out my code:
import java.util.Calendar;
public class compareDates {
public static void main(String[] args){
Calendar c = Calendar.getInstance();
String givenDate = "2021-01-10";
boolean later = false;
int yr_now = c.get(Calendar.YEAR);
int m_now = c.get(Calendar.MONTH) + 1;
int day_now = c.get(Calendar.DAY_OF_MONTH);
int given_yr = Integer.parseInt(givenDate.substring(0,4));
int given_m = Integer.parseInt(givenDate.substring(5,7));
int given_day = Integer.parseInt(givenDate.substring(8,10));
//is "given date" later than today's date?
if(yr_now > given_yr) {
System.out.print(later);
}
else if (yr_now == given_yr && m_now > given_m){
System.out.print(later);
}
else if (m_now == given_m && day_now >= given_day){
System.out.print(later);
}
else {later = true; System.out.print(later);}
}}

Same dates comparing in Java returning -1

try {
String hql = "SELECT taskDate FROM TaskFutureDates t WHERE t.taskId= : taskId";
List<java.sql.Date> result = em.createQuery(hql).setParameter("taskId", taskId).getResultList();
java.sql.Date currentDate =new Date(new java.util.Date().getTime());
if (result.size() != 0) {
for(java.sql.Date date: result) {
if(date.compareTo(currentDate)>=0) {
System.err.println("CAST= "+(Date) date);
return (java.sql.Date) date;
}
}
}
} catch (Exception e) {
// TODO: handle exception
//System.err.println(e);
}
return null;
}
when I am comparing the two dates I am getting error scenarios
1.when I am comparing alternative days I am getting -1
eg
09/04/2020
10/04/2020
when the date is same I am getting -1
eg
10/04/2020
10/04/2020
in the 2nd scenario, I should get 0 why the result is -1?
from the compareTo doc:
a value less than 0 if this Date is before the Date argument
today() (= last midnight) or probably any date from your table is less than now() (which includes the current time)
tl;dr
Use modern java.time classes to retrieve and compare stored date values against today’s date.
myResultSet.getObject( … , LocalDate.class ).isAfter( LocalDate.now( "Africa/Tunis" ) )
Details
As the correct Answer by Rotteveel explained, you should not be using the terrible class java.sql.Date. That class was years ago supplanted by the modern java.time classes. Specifically, java.time.LocalDate.
As of JDBC 4.2, we can exchange java.time objects with the database. For columns of a type akin to the standard-SQL type DATE, use setObject, updateObject, and getObject to exchange LocalDate objects.
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
To get today’s date requires a time zone. For any given moment, the date varies around the globe by time zone. So while it may be “tomorrow” in Tokyo Japan, it may simultaneously be “yesterday” in Toledo Ohio US.
If you do not specify a time zone, the JVM’s current default time zone is applied implicitly.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
LocalDate today = LocalDate.now( z ) ;
Compare using isEqual, isBefore, and isAfter methods.
Boolean isFuture = ld.isAfter( today ) ;
Although java.sql.Date is supposed to be a date, it actually is still a thin wrapper around epoch milliseconds. It doesn't truncate them. So two java.sql.Date values that are the same date, can still have two different epoch milliseconds values (ie check the getTime() result), and as a result they won't compare identical.
The implementation of the java.sql.Date constructor (Date(long)) is:
public Date(long date) {
// If the millisecond date value contains time info, mask it out.
super(date);
}
Contrary to the comment, nothing is masked out. This is probably because java.sql.Date is sensitive to the default JVM time zone, and trying to mask it out would only complicate things.
In any case, since Java 8 / JDBC 4.2, it would be better to use java.time.LocalDate instead of java.sql.Date.
If you can't fully switch to using LocalDate, I would recommend at least using:
LocalDate currentDate = LocalDate.now();
//...
if (date.toLocalDate().compareTo(currentDate) >= 0) {
// ...
}
Or - as shown in the answer by Basil Bourque - use the specialized comparison methods like isAfter, isBefore and isEqual.

Java - auto generate current day + user input time

At the moment I got a class 'Flight' with Date datatypes; departure and arrival datetime.
The adding of flights happens by user input. The day should be the current date automatically and the time is the user's choice. Which means a person only has to input HH:mm.
At the moment it is a bit confusing with all the choices; Timestamp, Date, Localtime etc.
How can I make a simple user input with a scanner for this problem?
It should take todays date, add the user input containing the time and add it together to fit into my Date datatype.
Anyone has a clue how to do this or could provide some tips / best practices?
Since you said that you are developing a desktop app and you need the current date (on the pc) you can use a combination of LocalDate and LocalTime to achieve your goal.
Here is the code:
public static void main(String[] args) {
LocalDate currentDate = LocalDate.now();
LocalTime userInputTime = null;
Scanner sc = new Scanner(System.in);
String dateTimeLine = sc.nextLine();
sc.close();
DateTimeFormatter dt = DateTimeFormatter.ofPattern("HH:mm");
userInputTime = LocalTime.parse(dateTimeLine,dtf);
System.err.println(LocalDateTime.of(currentDate, userInputTime));
}
First, you use LocalDate.now() in order to generate the current date (only the date, without hours, minutes and seconds).
Next we use Scanner in order to read a string entered by the user.
In order to convert the string to a LocalTime (this class contains info only about time in a day, so it has values for hours,minutes,seconds and nanoseconds), we have to define a DateTimeFormatter. The DateTimeFormatter defines how the string will be converted into LocalTime instance.
In the code I just wrote, I said that the string input will be of type "hours:minutes". For example, possible values are:
"10:25" - 10 hours and 25 minutes,
"23:00" - 23 hours and 0 minutes,
"02:13" - 2 hours and 13 minutes.
After we create the LocalTime object, all we have to do is to join the date and time objects in order to create a LocalDateTime object which is done in this line:
LocalDateTime.of(currentDate, userInputTime)
So lets say that the date on your current PC is 2018-05-06. If you run the program and enter 10:50 in the console, the output should be a LocalDateTime object that has 2018-05-06 as a date and 10 hours and 50 minutes as time of the day.
It is important to note that this line:
userInputTime = LocalTime.parse(dateTimeLine,dtf);
will throw an java.time.format.DateTimeParseException if the entered string by the user does not satisfy the required format.
tl;dr
LocalDateTime.of( // A `LocalDateTime` represents a set of *potential* moments along a range of about 26-27 hours. Not an actual moment, not a point on the timeline.
LocalDate.systemDefault() , // Get the JVM’s current default time zone. Can change at any moment *during* runtime. When crucial, always confirm with the user.
LocalTime.parse( "14:57" ) // Parse a string in standard ISO 8601 format as a time-of-day without regard for time zone or offset-from-UTC.
) // Returns a `LocalDateTime` object.
.atZone( // Determine an actual moment, a point on the timeline.
ZoneId( "Africa/Tunis" ) // Specify a time zone as `Continent/Region`, never as 3-4 letter pseudo-zones such as `PST`, `CST`, or `IST`.
) // Returns a `ZonedDateTime` object.
.toInstant() // Extracts a `Instant` object from the `ZonedDateTime` object, always in UTC by default.
Details
At the moment it is a bit confusing
Date-time handling is very confusing work.
Tips:
Forget about your own time zone. Think in terms of UTC rather than your own parochial time zone.
Learn the difference between real moments (points on the timeline), and date-time approximations that are not on the timeline, often-called “local” values.
Be careful when reading Stack Overflow or other places on the internet about date-time. You will encounter poor advice and many wrong solutions.
with all the choices; Timestamp, Date, Localtime etc.
Never use the troublesome old legacy date-time classes bundled with the earliest versions of Java. Never use java.sql.Timestamp, java.util.Date, java.util.Calendar, and so on.
➡ Use only classes in the java.time package.
The java.time classes are an industry-leading date-time framework. Extremely well-designed and thought-through, with lessons learned from the Joda-Time project it succeeds.
Anyone has a clue how to do this or could provide some tips / best practices?
You might be sorry you asked. Read on.
At the moment I got a class 'Flight' with Date datatypes; departure and arrival datetime.
So define a Flight class.
In real-life, flights happen far enough out in the future that we risk politicians changing the definition of the time zone. Most commonly these changes are adopting/dropping/altering Daylight Saving Time (DST). But arbitrary changes are made periodically for all kinds of reasons. We could debate the wisdom/sanity of such changes, but the fact is they happen. They happen quite frequently as politicians seemly oddly prone to making these changes around the world in many countries. And nearly all of them do so with little forewarning, sometimes just weeks. Or even with no warning at all, as North Korea did this week.
I have no understanding of how airlines actually work, but from poking around airline schedules and various readings, it seems they try to maintain their schedules using the zoned time of the departing locality. So if a flight is scheduled to depart LAX at 6 AM, they keep that flight schedule on the day before, the day of, and the day after a DST change-over. If this is indeed the general intent, that means sitting-around killing time on one DST cut-over while trying to save an hour on the opposite DST cut-over. Apparently, Amtrak adopts this practice for its trains. Let’s proceed with this approach.
Using this “imaginary” schedule approach means we cannot determine for certain the exact moment when 6 AM will occur in the future. So we need to record our desire for that date and that time-of-day without applying a time zone. But we must record the desired time zone so we know in what context we can later determine the exact moment, when close enough in time that we needn’t worry about zone changes.
So we use LocalDate and LocalTime types, as they purposely lack any concept of time zone (a name in Continent/Region format) or offset-from-UTC (a number of hours-minutes-seconds).
The ZoneId class represents a time zone.
I am using the word Unzoned in the names to remind us that these values do not represent actual moments on the timeline. The word “local” tends to confuse beginners.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId ;
}
As for arrival, store the span-of-time expected for that flight rather than the arrival date-time. You can calculate the arrival, so no need to store it. The Duration class tracks a number of hours, minutes, seconds, and fractional second.
To calculate the arrival, let’s return a single value using the LocalDateTime class, which simply combines a LocalDate with a LocalTime. We could have used this type to make a single departureUnzoned member variable in our class definition. I went with separate LocalDate and LocalTime as building blocks so you would understand the pieces. So many programmers use their intuition rather than the documentation to assume that LocalDateTime means a specific moment in a locality when actually it means just the opposite. (You will find many incorrect Answers on Stack Overflow advising LocalDateTime when actually Instant or ZonedDateTime should be used.)
Let's add a method to calculate that arrival.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
public LocalDateTime arrivalDateTimeUnzoned () {
LocalDateTime departureUnzoned = LocalDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned );
LocalDateTime ldt = departureUnzoned.plus( this.duration );
return ldt;
}
}
But this returned LocalDateTime fails to account for time zone. Usually, airlines and train report to customers the expected arrival time adjusted into the time zone of that region. So we need an arrival time zone. And we can use that zone when calculating the arrival, thereby producing a ZonedDateTime. A ZonedDateTime is a specific moment, it is a point on the timeline, unlike LocalDateTime. But remember, if we are scheduling flights out into the future, the calculated ZonedDateTime will change if our code is run after politicians redefine the time zone.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
}
Back to the part of your Question about determining the date automatically. That requires a time zone. For any given moment, the date varies around the globe. Think about that. A few minutes after midnight in Paris France is a new day, while still “yesterday” in Montréal Québec.
We can ask for the JVM’s current default time zone.
ZoneId userZoneId = ZoneId.systemDefault() ;
But when crucial, you must confirm with the user.
ZoneId userZoneId = ZoneId.of( "America/Montreal" ) ;
So now we can add the constructor you asked for, passing the time-of-day (a LocalTime, and guessing the time zone by using the JVM’s current default.
But we still need all the other pieces. So defaulting the date does not save us much.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
// Constructor
public Flight ( String flightNumber , LocalTime departureTimeUnzoned , ZoneId departureZoneId , Duration duration , ZoneId arrivalZoneId ) {
this.flightNumber = flightNumber;
this.departureTimeUnzoned = departureTimeUnzoned;
this.departureZoneId = departureZoneId;
this.duration = duration;
this.arrivalZoneId = arrivalZoneId;
// Determine today’s date using JVM’s current default time zone. Not advisable in many business scenarios, but specified by our Question at hand.
ZoneId z = ZoneId.systemDefault();
LocalDate today = LocalDate.now( z );
this.departureDateUnzoned = today;
}
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
}
Let’s add a toString method for reporting.
We represent the date-time values as strings in standard ISO 8601 formats. The java.time classes use these standard formats when parsing/generating strings. The Z on the end is pronounced Zulu and means UTC.
While airlines and trains report date-times to their customers in the regions’ time zones, we can assume they use only UTC internally. The Instant class represents values in UTC specifically. So our toString extracts Instant objects from the ZonedDateTime objects.
And we add a main method for demonstration. Here is the complete class, with import etc.
package com.basilbourque.example;
import java.time.*;
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
// Constructor
public Flight ( String flightNumber , LocalTime departureTimeUnzoned , ZoneId departureZoneId , Duration duration , ZoneId arrivalZoneId ) {
this.flightNumber = flightNumber;
this.departureTimeUnzoned = departureTimeUnzoned;
this.departureZoneId = departureZoneId;
this.duration = duration;
this.arrivalZoneId = arrivalZoneId;
// Determine today’s date using JVM’s current default time zone. Not advisable in many business scenarios, but specified by our Question at hand.
ZoneId z = ZoneId.systemDefault();
LocalDate today = LocalDate.now( z );
this.departureDateUnzoned = today;
}
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
#Override
public String toString () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
String flightInUtc = departureZoned.toInstant().toString() + "/" + this.arrivalDateTimeZoned().toInstant().toString();
return "Flight{ " +
"flightNumber='" + this.flightNumber + '\'' +
" | departureDateUnzoned=" + this.departureDateUnzoned +
" | departureTimeUnzoned=" + this.departureTimeUnzoned +
" | departureZoneId=" + this.departureZoneId +
" | departureZoned=" + departureZoned +
" | duration=" + this.duration +
" | arrivalZoneId=" + this.arrivalZoneId +
" | calculatedArrival=" + this.arrivalDateTimeZoned() +
" | flightInUtc=" + flightInUtc +
" }";
}
public static void main ( String[] args ) {
LocalTime lt = LocalTime.of( 6 , 0 ); // 6 AM.
Flight f = new Flight( "A472" , lt , ZoneId.of( "America/Los_Angeles" ) , Duration.parse( "PT6H37M" ) , ZoneId.of( "America/Montreal" ) );
String output = f.toString();
System.out.println( output );
}
}
When run.
Flight{ flightNumber='A472' | departureDateUnzoned=2018-05-06 | departureTimeUnzoned=06:00 | departureZoneId=America/Los_Angeles | departureZoned=2018-05-06T06:00-07:00[America/Los_Angeles] | duration=PT6H37M | arrivalZoneId=America/Montreal | calculatedArrival=2018-05-06T12:37-07:00[America/Los_Angeles] | flightInUtc=2018-05-06T13:00:00Z/2018-05-06T19:37:00Z }
To use this from the console, ask the user for the time-of-day in 24-hour clock. Parse the input string.
String input = "14:56" ; // 24-hour clock.
LocalTime lt = LocalTime.parse( input ) ;
This is far from complete for real-world work. But hopefully it makes for an educational example.
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.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.

How to get number of days between two calendar instance?

I want to find the difference between two Calendar objects in number of days if there is date change like If clock ticked from 23:59-0:00 there should be a day difference.
i wrote this
public static int daysBetween(Calendar startDate, Calendar endDate) {
return Math.abs(startDate.get(Calendar.DAY_OF_MONTH)-endDate.get(Calendar.DAY_OF_MONTH));
}
but its not working as it only gives difference between days if there is month difference its worthless.
Try the following approach:
public static long daysBetween(Calendar startDate, Calendar endDate) {
long end = endDate.getTimeInMillis();
long start = startDate.getTimeInMillis();
return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}
In Java 8 and later, we could simply use the java.time classes.
hoursBetween = ChronoUnit.HOURS.between(calendarObj.toInstant(), calendarObj.toInstant());
daysBetween = ChronoUnit.DAYS.between(calendarObj.toInstant(), calendarObj.toInstant());
This function computes the number of days between two Calendars as the number of calendar days of the month that are between them, which is what the OP wanted. The calculation is performed by counting how many multiples of 86,400,000 milliseconds are between the calendars after both have been set to midnight of their respective days.
For example, my function will compute 1 day's difference between a Calendar on January 1, 11:59PM and January 2, 12:01AM.
import java.util.concurrent.TimeUnit;
/**
* Compute the number of calendar days between two Calendar objects.
* The desired value is the number of days of the month between the
* two Calendars, not the number of milliseconds' worth of days.
* #param startCal The earlier calendar
* #param endCal The later calendar
* #return the number of calendar days of the month between startCal and endCal
*/
public static long calendarDaysBetween(Calendar startCal, Calendar endCal) {
// Create copies so we don't update the original calendars.
Calendar start = Calendar.getInstance();
start.setTimeZone(startCal.getTimeZone());
start.setTimeInMillis(startCal.getTimeInMillis());
Calendar end = Calendar.getInstance();
end.setTimeZone(endCal.getTimeZone());
end.setTimeInMillis(endCal.getTimeInMillis());
// Set the copies to be at midnight, but keep the day information.
start.set(Calendar.HOUR_OF_DAY, 0);
start.set(Calendar.MINUTE, 0);
start.set(Calendar.SECOND, 0);
start.set(Calendar.MILLISECOND, 0);
end.set(Calendar.HOUR_OF_DAY, 0);
end.set(Calendar.MINUTE, 0);
end.set(Calendar.SECOND, 0);
end.set(Calendar.MILLISECOND, 0);
// At this point, each calendar is set to midnight on
// their respective days. Now use TimeUnit.MILLISECONDS to
// compute the number of full days between the two of them.
return TimeUnit.MILLISECONDS.toDays(
Math.abs(end.getTimeInMillis() - start.getTimeInMillis()));
}
Extension to #JK1 great answer :
public static long daysBetween(Calendar startDate, Calendar endDate) {
//Make sure we don't change the parameter passed
Calendar newStart = Calendar.getInstance();
newStart.setTimeInMillis(startDate.getTimeInMillis());
newStart.set(Calendar.HOUR_OF_DAY, 0);
newStart.set(Calendar.MINUTE, 0);
newStart.set(Calendar.SECOND, 0);
newStart.set(Calendar.MILLISECOND, 0);
Calendar newEnd = Calendar.getInstance();
newEnd.setTimeInMillis(endDate.getTimeInMillis());
newEnd.set(Calendar.HOUR_OF_DAY, 0);
newEnd.set(Calendar.MINUTE, 0);
newEnd.set(Calendar.SECOND, 0);
newEnd.set(Calendar.MILLISECOND, 0);
long end = newEnd.getTimeInMillis();
long start = newStart.getTimeInMillis();
return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}
UPDATE The Joda-Time project, now in maintenance mode, advises migration to the java.time classes. See the Answer by Anees A for the calculation of elapsed hours, and see my new Answer for using java.time to calculate elapsed days with respect for the calendar.
Joda-Time
The old java.util.Date/.Calendar classes are notoriously troublesome and should be avoided.
Instead use the Joda-Time library. Unless you have Java 8 technology in which case use its successor, the built-in java.time framework (not in Android as of 2015).
Since you only care about "days" defined as dates (not 24-hour periods), let's focus on dates. Joda-Time offers the class LocalDate to represent a date-only value without time-of-day nor time zone.
While lacking a time zone, note that time zone is crucial in determining a date such as "today". A new day dawns earlier to the east than to the west. So the date is not the same around the world at one moment, the date depends on your time zone.
DateTimeZone zone = DateTimeZone.forID ( "America/Montreal" );
LocalDate today = LocalDate.now ( zone );
Let's count the number of days until next week, which should of course be seven.
LocalDate weekLater = today.plusWeeks ( 1 );
int elapsed = Days.daysBetween ( today , weekLater ).getDays ();
The getDays on the end extracts a plain int number from the Days object returned by daysBetween.
Dump to console.
System.out.println ( "today: " + today + " to weekLater: " + weekLater + " is days: " + days );
today: 2015-12-22 to weekLater: 2015-12-29 is days: 7
You have Calendar objects. We need to convert them to Joda-Time objects. Internally the Calendar objects have a long integer tracking the number of milliseconds since the epoch of first moment of 1970 in UTC. We can extract that number, and feed it to Joda-Time. We also need to assign the desired time zone by which we intend to determine a date.
long startMillis = myStartCalendar.getTimeInMillis();
DateTime startDateTime = new DateTime( startMillis , zone );
long stopMillis = myStopCalendar.getTimeInMillis();
DateTime stopDateTime = new DateTime( stopMillis , zone );
Convert from DateTime objects to LocalDate.
LocalDate start = startDateTime.toLocalDate();
LocalDate stop = stopDateTime.toLocalDate();
Now do the same elapsed calculation we saw earlier.
int elapsed = Days.daysBetween ( start , stop ).getDays ();
Here's my solution using good old Calendar objects:
public static int daysApart(Calendar d0,Calendar d1)
{
int days=d0.get(Calendar.DAY_OF_YEAR)-d1.get(Calendar.DAY_OF_YEAR);
Calendar d1p=Calendar.getInstance();
d1p.setTime(d1.getTime());
for (;d1p.get(Calendar.YEAR)<d0.get(Calendar.YEAR);d1p.add(Calendar.YEAR,1))
{
days+=d1p.getActualMaximum(Calendar.DAY_OF_YEAR);
}
return days;
}
This assumes d0 is later than d1. If that's not guaranteed, you could always test and swap them.
Basic principle is to take the difference between the day of the year of each. If they're in the same year, that would be it.
But they might be different years. So I loop through all the years between them, adding the number of days in a year. Note that getActualMaximum returns 366 in leap years and 365 in non-leap years. That's why we need a loop, you can't just multiply the difference between the years by 365 because there might be a leap year in there. (My first draft used getMaximum, but that doesn't work because it returns 366 regardless of the year. getMaximum is the maximum for ANY year, not this particular year.)
As this code makes no assumptions about the number of hours in a day, it is not fooled by daylight savings time.
tl;dr
java.time.temporal.ChronoUnit // The java.time classes are built into Java 8+ and Android 26+. For earlier Android, get must of the functionality by using the latest tooling with "API desugaring".
.DAYS // A pre-defined enum object.
.between(
( (GregorianCalendar) startCal ) // Cast from the more abstract `Calendar` to the more concrete `GregorianCalendar`.
.toZonedDateTime() // Convert from legacy class to modern class. Returns a `ZonedDateTime` object.
.toLocalDate() // Extract just the date, to get the Question's desired whole-days count, ignoring fractional days. Returns a `LocalDate` object.
,
( (GregorianCalendar) endCal )
.toZonedDateTime()
.toLocalDate()
) // Returns a number of days elapsed between our pair of `LocalDate` objects.
java.time
The Answer by Mohamed Anees A is correct for hours but wrong for days. Counting days requires a time zone. That other Answer uses the Instant which is a moment in UTC, always in UTC. So you are not getting the correct number of calendar days elapsed.
To count days by the calendar, convert your legacy Calendar to a ZonedDateTime, then feed to ChronoUnit.DAYS.between.
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 during runtime(!), so your results may vary. Better to specify your desired/expected time zone explicitly as an argument. If critical, confirm the zone with your user.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-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 ) ; // Capture the current date as seen through the wall-clock time used by the people of a certain region (a time zone).
If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the code becomes ambiguous to read in that we do not know for certain if you intended to use the default or if you, like so many programmers, were unaware of the issue.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Convert from GregorianCalendar to ZonedDateTime
The terrible GregorianCalendar is likely the concrete class behind your Calendar. If so, convert from that legacy class to the modern class, ZonedDateTime.
GregorianCalendar gc = null ; // Legacy class representing a moment in a time zone. Avoid this class as it is terribly designed.
if( myCal instanceof GregorianCalendar ) { // See if your `Calendar` is backed by a `GregorianCalendar` class.
gc = (GregorianCalendar) myCal ; // Cast from the more general class to the concrete class.
ZonedDateTime zdt = gc.toZonedDateTime() ; // Convert from legacy class to modern class.
}
The resulting ZonedDateTime object carries a ZoneId object for the time zone. With that zone in place, you can then calculate elapsed calendar days.
Calculate elapsed days
To calculate the elapsed time in terms of years-months-days, use Period class.
Period p = Period.between( zdtStart , zdtStop ) ;
If you want total number of days as the elapsed time, use ChronoUnit.
long days = ChronoUnit.DAYS.between( zdtStart , zdtStop ) ;
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.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
I have the similar (not exact same) approach given above by https://stackoverflow.com/a/31800947/3845798.
And have written test cases around the api, for me it failed if I passed
8th march 2017 - as the start date and 8th apr 2017 as the end date.
There are few dates where you will see the difference by 1day.
Therefore, I have kind of made some small changes to my api and my current api now looks something like this
public long getDays(long currentTime, long endDateTime) {
Calendar endDateCalendar;
Calendar currentDayCalendar;
//expiration day
endDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
endDateCalendar.setTimeInMillis(endDateTime);
endDateCalendar.set(Calendar.MILLISECOND, 0);
endDateCalendar.set(Calendar.MINUTE, 0);
endDateCalendar.set(Calendar.HOUR, 0);
endDateCalendar.set(Calendar.HOUR_OF_DAY, 0);
//current day
currentDayCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
currentDayCalendar.setTimeInMillis(currentTime);
currentDayCalendar.set(Calendar.MILLISECOND, 0);
currentDayCalendar.set(Calendar.MINUTE, 0);
currentDayCalendar.set(Calendar.HOUR,0);
currentDayCalendar.set(Calendar.HOUR_OF_DAY, 0);
long remainingDays = (long)Math.ceil((float) (endDateCalendar.getTimeInMillis() - currentDayCalendar.getTimeInMillis()) / (24 * 60 * 60 * 1000));
return remainingDays;}
I am not using TimeUnit.MILLISECONDS.toDays that were causing me some issues.
Kotlin solution, purely relies on Calendar. At the end gives exact number of days difference.
Inspired by #Jk1
private fun daysBetween(startDate: Calendar, endDate: Calendar): Long {
val start = Calendar.getInstance().apply {
timeInMillis = 0
set(Calendar.DAY_OF_YEAR, startDate.get(Calendar.DAY_OF_YEAR))
set(Calendar.YEAR, startDate.get(Calendar.YEAR))
}.timeInMillis
val end = Calendar.getInstance().apply {
timeInMillis = 0
set(Calendar.DAY_OF_YEAR, endDate.get(Calendar.DAY_OF_YEAR))
set(Calendar.YEAR, endDate.get(Calendar.YEAR))
}.timeInMillis
val differenceMillis = end - start
return TimeUnit.MILLISECONDS.toDays(differenceMillis)
}
If your project doesn't support new Java 8 classes (as selected answer), you can add this method to calculate the days without being influenced by timezones or other facts.
It is not as fast (greater time complexity) as other methods but it's reliable, anyways date comparisons are rarely larger than hundreds or thousands of years.
(Kotlin)
/**
* Returns the number of DAYS between two dates. Days are counted as calendar days
* so that tomorrow (from today date reference) will be 1 , the day after 2 and so on
* independent on the hour of the day.
*
* #param date - reference date, normally Today
* #param selectedDate - date on the future
*/
fun getDaysBetween(date: Date, selectedDate: Date): Int {
val d = initCalendar(date)
val s = initCalendar(selectedDate)
val yd = d.get(Calendar.YEAR)
val ys = s.get(Calendar.YEAR)
if (ys == yd) {
return s.get(Calendar.DAY_OF_YEAR) - d.get(Calendar.DAY_OF_YEAR)
}
//greater year
if (ys > yd) {
val endOfYear = Calendar.getInstance()
endOfYear.set(yd, Calendar.DECEMBER, 31)
var daysToFinish = endOfYear.get(Calendar.DAY_OF_YEAR) - d.get(Calendar.DAY_OF_YEAR)
while (endOfYear.get(Calendar.YEAR) < s.get(Calendar.YEAR)-1) {
endOfYear.add(Calendar.YEAR, 1)
daysToFinish += endOfYear.get(Calendar.DAY_OF_YEAR)
}
return daysToFinish + s.get(Calendar.DAY_OF_YEAR)
}
//past year
else {
val endOfYear = Calendar.getInstance()
endOfYear.set(ys, Calendar.DECEMBER, 31)
var daysToFinish = endOfYear.get(Calendar.DAY_OF_YEAR) - s.get(Calendar.DAY_OF_YEAR)
while (endOfYear.get(Calendar.YEAR) < d.get(Calendar.YEAR)-1) {
endOfYear.add(Calendar.YEAR, 1)
daysToFinish += endOfYear.get(Calendar.DAY_OF_YEAR)
}
return daysToFinish + d.get(Calendar.DAY_OF_YEAR)
}
}
Unit Tests, you can improve them I didn't need the negative days so I didn't test that as much:
#Test
fun `Test days between on today and following days`() {
val future = Calendar.getInstance()
calendar.set(2019, Calendar.AUGUST, 26)
future.set(2019, Calendar.AUGUST, 26)
Assert.assertEquals(0, manager.getDaysBetween(calendar.time, future.time))
future.set(2019, Calendar.AUGUST, 27)
Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))
future.set(2019, Calendar.SEPTEMBER, 1)
Assert.assertEquals(6, manager.getDaysBetween(calendar.time, future.time))
future.set(2020, Calendar.AUGUST, 26)
Assert.assertEquals(366, manager.getDaysBetween(calendar.time, future.time)) //leap year
future.set(2022, Calendar.AUGUST, 26)
Assert.assertEquals(1096, manager.getDaysBetween(calendar.time, future.time))
calendar.set(2019, Calendar.DECEMBER, 31)
future.set(2020, Calendar.JANUARY, 1)
Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))
}
#Test
fun `Test days between on previous days`() {
val future = Calendar.getInstance()
calendar.set(2019, Calendar.AUGUST, 26)
future.set(2019,Calendar.AUGUST,25)
Assert.assertEquals(-1, manager.getDaysBetween(calendar.time, future.time))
}
#Test
fun `Test days between hour doesn't matter`() {
val future = Calendar.getInstance()
calendar.set(2019, Calendar.AUGUST, 26,9,31,15)
future.set(2019,Calendar.AUGUST,28, 7,0,0)
Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
future.set(2019,Calendar.AUGUST,28, 9,31,15)
Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
future.set(2019,Calendar.AUGUST,28, 23,59,59)
Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
}
#Test
fun `Test days between with time saving change`() {
val future = Calendar.getInstance()
calendar.set(2019, Calendar.OCTOBER, 28)
future.set(2019, Calendar.OCTOBER,29)
Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))
future.set(2019, Calendar.OCTOBER,30)
Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
}
public int getIntervalDays(Calendar c1,Calendar c2){
Calendar first = cleanTimePart(c1);
Calendar second = cleanTimePart(c2);
Long intervalDays = (first.getTimeInMillis() - second.getTimeInMillis())/(1000*3600*24);
return intervalDays.intValue();
}
private Calendar cleanTimePart(Calendar dateTime){
Calendar newDateTime = (Calendar)dateTime.clone();
newDateTime.set(Calendar.HOUR_OF_DAY,0);
newDateTime.set(Calendar.MINUTE,0);
newDateTime.set(Calendar.SECOND,0);
newDateTime.set(Calendar.MILLISECOND,0);
return newDateTime;
}
Calendar day1 = Calendar.getInstance(); Calendar day2 = Calendar.getInstance(); int diff = day1.get(Calendar.DAY_OF_YEAR)
- day2.get(Calendar.DAY_OF_YEAR);

See if the current time falls within a specific range of time in the current day in Java

I am sure this was done 1000 times in 1000 different places. The question is I want to know if there is a better/standard/faster way to check if current "time" is between two time values given in hh:mm:ss format. For example, my big business logic should not run between 18:00:00 and 18:30:00. So here is what I had in mind:
public static boolean isCurrentTimeBetween(String starthhmmss, String endhhmmss) throws ParseException{
DateFormat hhmmssFormat = new SimpleDateFormat("yyyyMMddhh:mm:ss");
Date now = new Date();
String yyyMMdd = hhmmssFormat.format(now).substring(0, 8);
return(hhmmssFormat.parse(yyyMMdd+starthhmmss).before(now) &&
hhmmssFormat.parse(yyyMMdd+endhhmmss).after(now));
}
Example test case:
String doNotRunBetween="18:00:00,18:30:00";//read from props file
String[] hhmmss = downTime.split(",");
if(isCurrentTimeBetween(hhmmss[0], hhmmss[1])){
System.out.println("NOT OK TO RUN");
}else{
System.out.println("OK TO RUN");
}
What I am looking for is code that is better
in performance
in looks
in correctness
What I am not looking for
third-party libraries
Exception handling debate
variable naming conventions
method modifier issues
this is all you should need to do, this method is loosely coupled from the input and highly coherent.
boolean isNowBetweenDateTime(final Date s, final Date e)
{
final Date now = new Date();
return now.after(s) && now.before(e);
}
how you get the Date objects for start and end is irrelevant to comparing them. You are making things way more complicated than you need to with passing String representations around.
Here is a better way to get the start and end dates, again loosely coupled and highly coherent.
private Date dateFromHourMinSec(final String hhmmss)
{
if (hhmmss.matches("^[0-2][0-9]:[0-5][0-9]:[0-5][0-9]$"))
{
final String[] hms = hhmmss.split(":");
final GregorianCalendar gc = new GregorianCalendar();
gc.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hms[0]));
gc.set(Calendar.MINUTE, Integer.parseInt(hms[1]));
gc.set(Calendar.SECOND, Integer.parseInt(hms[2]));
gc.set(Calendar.MILLISECOND, 0);
return gc.getTime();
}
else
{
throw new IllegalArgumentException(hhmmss + " is not a valid time, expecting HH:MM:SS format");
}
}
Now you can make two well named method calls that will be pretty self documenting.
tl;dr
LocalTime now =
ZonedDateTime
.now( ZoneId.of( "America/Montreal" ) )
.toLocalTime() ;
Boolean isBetween =
( ! now.isBefore( LocalTime.of( 18 , 0 ) ) // "not before" means "is equal to OR after".
&&
now.isBefore( LocalTime.of( 18 , 30 ) ) ; // Half-Open, beginning is *inclusive* while ending is *exclusive*.
Using java.time
You are using old date-time classes that have proven to be poorly designed, confusing, and troublesome. They are now legacy, supplanted by the java.time classes.
LocalTime
Do not pass mere strings representing time-of-day values. We now have a type for that, the LocalTime class.
LocalTime start = LocalTime.of( 18 , 0 );
LocalTime stop = LocalTime.of( 18 , 30 );
Pass those instances to your utility method. That method should not have to do any parsing, so no need to throw the parsing exception.
public static boolean isCurrentTimeBetween( LocalTime start , LocalTime stop ) {
…
ZonedDateTime
A time zone is crucial in determining the current date and time-of-day. 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.
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" );
ZonedDateTime zdt = ZonedDateTime.now( z );
To compare the time-of-day of now, we could simply extract a LocalTime from that ZonedDateTime. But we have the problem of anomalies, such as Daylight Saving Time (DST) and politicians redefining time zones. There may not be any 6 PM hour on a particular date. The solution to this conundrum depends on your business context and your business rules. You could either ignore the conundrum and stick with literally asking if the current time is between your target start-stop time. Or you could apply the time zone to your start-stop times-of-day of day and let ZonedDateTime class make adjustments as it sees fit. Let's look at both approaches.
Ignore anomalies
First, ignore any anomalies. Ask simply and literally if the current time-of-day is between the target start and stop times-of-day.
We can extract a time-of-day object from the zoned date-time object.
LocalTime localTimeNow = zdt.toLocalTime(); // Extract a time-of-day from the zoned date-time object.
Compare that to our stop-start times-of-day. Note that we use here the Half-Open approach to defining a span of time. In this approach the beginning is inclusive while the ending is exclusive. This approach is common in date-time work and generally is the wise way to go.
Boolean isNowOnOrAfterStart = ( ! localTimeNow.isBefore( start ) ) ; // A briefer way of asking "is equal to OR is after" is "is not before".
Boolean isNowBeforeStop = localTimeNow.isBefore( stop );
Boolean isNowInTargetZone = ( isNowOnOrAfterStart && isNowBeforeStop ); // Half-Open: beginning is inclusive while ending is exclusive.
Consider anomalies
Next we consider any anomalies. We apply the start and stop times-of-day to the current date within the same time zone. We extract the date-only from the zoned date-time object.
LocalDate localDateToday = zdt.toLocalDate();
ZonedDateTime zdtStart = ZonedDateTime.of( localDateToday , start , z );
ZonedDateTime zdtStop = ZonedDateTime.of( localDateToday , stop , z );
Study the class documentation to understand the behavior of ZonedDateTime.of in resolving invalid time-of-day values. There is no perfect way to resolve nonexistent time-of-day values, so you must decide if this class’ way meets your business rules.
ZonedDateTime.of
public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)
Obtains an instance of ZonedDateTime from a local date and time.
This creates a zoned date-time matching the input local date and time as closely as possible. Time-zone rules, such as daylight savings, mean that not every local date-time is valid for the specified zone, thus the local date-time may be adjusted.
The local date time and first combined to form a local date-time. The local date-time is then resolved to a single instant on the time-line. This is achieved by finding a valid offset from UTC/Greenwich for the local date-time as defined by the rules of the zone ID.
In most cases, there is only one valid offset for a local date-time. In the case of an overlap, when clocks are set back, there are two valid offsets. This method uses the earlier offset typically corresponding to "summer".
In the case of a gap, when clocks jump forward, there is no valid offset. Instead, the local date-time is adjusted to be later by the length of the gap. For a typical one hour daylight savings change, the local date-time will be moved one hour later into the offset typically corresponding to "summer".
Apply the same comparison logic as we saw above.
Boolean isNowOnOrAfterStart = ( ! zdt.isBefore( zdtStart ) ) ; // A briefer way of asking "is equal to OR is after" is "is not before".
Boolean isNowBeforeStop = zdt.isBefore( zdtStop );
Boolean isNowInTargetZone = ( isNowOnOrAfterStart && isNowBeforeStop ); // Half-Open: beginning is inclusive while ending is exclusive.
Alternative way to make the comparison is to use the handy Interval class from the ThreeTen-Extra project. That class takes a pain of Instant objects, which you can extract from your ZonedDateTime objects. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Interval interval = Interval.of( zdtStart.toInstant() , zdtStop.toInstant() );
Boolean isNowInTargetZone = interval.contains( zdt.toInstant() );
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?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use ThreeTenABP….
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.
As pointed out by Kevin, Fuzzy Lollipop's Regex won't pick up times between 14:00 and 19:00.
To get match a full 24 hour clock, you can use this:
if (hhmmss.matches("^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"))
{
// Do stuff here
}
The following Class is something I just created out of some of the code from other answers. It encapsulates the behavior of a 'time period' without relating to specific days. Our system is using this Class to check if the current time is within one of our designated maintenance windows. i.e. 05:00:00 - 07:00:00
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
*
* #author Adam Yocum
*/
public class ExclusionTimePeriod {
private String timeStart;
private String timeEnd;
/**
* #return the timeStart
*/
public String getTimeStart() {
return timeStart;
}
/**
* #param timeStart the timeStart to set
*/
public void setTimeStart(String timeStart) {
if (timeStart.matches("^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"))
{
this.timeStart = timeStart;
}
else
{
throw new IllegalArgumentException(timeStart + " is not a valid time, expecting HH:MM:SS format");
}
}
/**
* #return the timeEnd
*/
public String getTimeEnd() {
return timeEnd;
}
/**
* #param timeEnd the timeEnd to set
*/
public void setTimeEnd(String timeEnd) {
if (timeEnd.matches("^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"))
{
this.timeEnd = timeEnd;
}
else
{
throw new IllegalArgumentException(timeEnd + " is not a valid time, expecting HH:MM:SS format");
}
}
private Date toDate(String hhmmss){
final String[] hms = hhmmss.split(":");
final GregorianCalendar gc = new GregorianCalendar();
gc.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hms[0]));
gc.set(Calendar.MINUTE, Integer.parseInt(hms[1]));
gc.set(Calendar.SECOND, Integer.parseInt(hms[2]));
gc.set(Calendar.MILLISECOND, 0);
Date date = gc.getTime();
return date;
}
public boolean isNowInPeriod()
{
final Date now = new Date();
return now.after(toDate(getTimeStart())) && now.before(toDate(getTimeEnd()));
}
public static void main(String[] args){
//Test All possible hours
for(int hour=0;hour<=23;hour++){
String hourStr = "";
if(hour<=9){
hourStr = "0"+hour;
}else{
hourStr = ""+hour;
}
for(int min=0;min<60;min++){
String minStr = "";
if(min<=9){
minStr = "0"+min;
}else{
minStr = ""+min;
}
for(int sec=0;sec<60;sec++){
String secStr = "";
if(sec<=9){
secStr = "0"+sec;
}else{
secStr = ""+sec;
}
String hhmmss = hourStr+":"+minStr+":"+secStr;
ExclusionTimePeriod period = new ExclusionTimePeriod();
period.setTimeStart(hhmmss);
period.setTimeEnd(hhmmss);
System.out.println(hhmmss+" Ok");
}
}
}
//Test isInPeriod functionality
ExclusionTimePeriod isInTest = new ExclusionTimePeriod();
isInTest.setTimeStart("10:00:00");
isInTest.setTimeEnd("10:43:00");
System.out.println((new Date())+" is between "+isInTest.getTimeStart()+" and "+isInTest.getTimeEnd()+" = "+isInTest.isNowInPeriod());
}
}
The Midnight Problem
Other answers fail to mention it - and the OP doesn't ask - but you should really consider when the interval spans across midnight.
Time is difficult. I purposely left the "long" version of the code and didn't abbreviate logical conditions to make it as clear as possible the what's and the why's.
/**
* Takes into consideration that the interval may span accross midnight
*
* #param clock to make unit testing easier, just replace for Clock.systemUTC() in your code
* #param start the interval start
* #param end the interval end
* #return true if "now" is inside the specified interval
*/
static boolean isNowBetweenLocalTime(Clock clock, final LocalTime start, final LocalTime end) {
LocalTime now = LocalTime.now(clock);
// if interval crosses midnight
if (end.isBefore(start)) {
if (now.isAfter(start) && now.isAfter(end)) {
return true;
}
if (now.isBefore(start) && now.isBefore(end)) {
return true;
}
return false;
}
// if interval does not cross midnight
if (end.isAfter(start)) {
if (now.isAfter(start) && now.isBefore(end)) {
return true;
}
return false;
}
return false; // interval is 0 so start and end always outside interval
}
Verbosity is not always wrong. This method will be buried in a utility class and two years from now you'll thank yourself for understanding what it does!
The dateFromHourMinSec method is flawed as written. It won't allow any hours where the seconde digit is greater than 3, e.g. 18:00:00. If you change it to allow [0-2][0-9] it will allow times such as 29:00:00.
Have a fix for that?

Categories

Resources