Java - auto generate current day + user input time - java

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.

Related

Convert Instant to OffsetDateTime then Instant.parse() on the OffsetDateTime leads to DateTimeParseException for zero seconds cases

I am receiving instant from a service and below are the cases.
In first case, say instant = "2021-03-23T04:17:35Z" & In second case,
instant = "2021-07-15T05:27:00Z"
Then required to convert instant to offsetDateTime, doing like
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC)
Now I want to calculate hours gap between above offsetDateTime and instant.now
ChronoUnit.HOURS.between(Instant.parse(offsetDateTime), Instant.now())
Results
case 1 : it works fine
case 2 : ERROR : DateTimeParseException: Text '2021-07-15T05:27Z' could not be parsed at index 16
Figured out the reason :
in case 2, if it would have pass the same 2021-07-15T05:27:00Z. it would work but as instant.atOffset(ZoneOffset.UTC) internally will call to below method, where zero would be removed, basically minute part will be sorted out. so below fn will return 2021-07-15T05:27Z , this will lead to DateTimeParseException.
public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) {
Objects.requireNonNull(instant, "instant");
Objects.requireNonNull(zone, "zone");
ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant);
LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
return new OffsetDateTime(ldt, offset);
}
One solution I am assuming manually append the zero but that's might not be good practice.
You do not need any DateTimeFormatter
You do not need any DateTimeFormatter to parse your Date-Time strings. The modern Date-Time API is based on ISO 8601 and does not require using a DateTimeFormatter object explicitly as long as the Date-Time string conforms to the ISO 8601 standards.
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
Instant instant1 = Instant.parse("2021-03-23T04:17:35Z");
Instant instant2 = Instant.parse("2021-07-15T05:27:00Z");
System.out.println(instant1);
System.out.println(instant2);
System.out.println(ChronoUnit.HOURS.between(instant1, instant2));
}
}
Output:
2021-03-23T04:17:35Z
2021-07-15T05:27:00Z
2737
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
The Answer by Avinash is correct.
In addition, let’s look at this code from the Question:
public static OffsetDateTime ofInstant(Instant instant, ZoneId zone)
{
Objects.requireNonNull(instant, "instant");
Objects.requireNonNull(zone, "zone");
ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant);
LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
return new OffsetDateTime(ldt, offset);
}
Firstly, if you want to apply an offset to a moment, there is no need for LocalDateTime class. Simply do this:
OffsetDateTime odt = instant.atOffset( myZoneOffset ) ;
See the tutorial on naming conventions for at…, from…, with…, etc.
When you want to perceive a moment thought the wall-clock time used by the people of a particular region, apply a time zone (ZoneId) to an Instant to get a ZonedDateTime. Use ZonedDateTime rather than OffsetDateTime. A ZonedDateTime is preferable to a mere OffsetDateTime because it contains more information. This information may be critical if adding or subtracting to move to another moment. This information is also useful when producing text to represent the content of this date-time object.
Understand that an offset from UTC is simply a number of hours-minutes-seconds. A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region as decided by politicians.
ZonedDateTime zdt = instant.atZone( myZoneId ) ;
So your method should be something like this.
public static ZonedDateTime ofInstant(Instant instant, ZoneId zone)
{
ZonedDateTime zdt =
Objects.requireNonNull( instant )
.atZone(
Objects.requireNonNull( zone )
)
;
return zdt ;
}
If you really need a OffsetDateTime, despite ZonedDateTime being preferred, extract one.
OffsetDateTime odt = zdt.toOffsetDateTime() ;

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.

Appointment class

I'm trying to create a method that would add an appointment to an arraylist appointmentCalndar. This method would validate the date to see if the user input is equal to the SimpleDateFormat in my code, the startTime and the endTime of the appointment and to see if its in the future or not.
I have tried using the java Date api to check this but when I try to extend the class to get access to the attributes it always causes an error on compile time. So overall my question is what would the best way to compare an object of appointment type to an object of type date? I try using accesors to getDate() and startTime and endTime but it won't allow me to get them also.
public AppointmentDate(String appString)
{
// 1) split ithe string into Date/from/to
// 2) consturct the Date object for the appDate
// 3) consturct the Date object for the startTime
// 4) consturct the Date object for the endTime
String[] appDetails = appString.split(",");
if(appDetails.length == 2)
{
try {
SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
this.appDate = df.parse(appDetails[0]);
DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy,mm:HH");
String dFormat = appDetails[0] + "," + appDetails[1];
this.startTime = formatter.parse(dFormat);
dFormat = appDetails[0] + "," + appDetails[2];
this.endTime = formatter.parse(dFormat);
}
catch (Exception ex)
{
}
}
else
{
System.out.print("User Date is Invalid");
}
}
public void setStartTime(Date startTime)
{
this.startTime = startTime;
}
public Date getStartTime()
{
return startTime;
}
public void setEndTime(Date endTime)
{
this.endTime = endTime;
}
public Date getEndTime()
{
return endTime;
}
public void setAppdate(Date appDate)
{
this.appDate = appDate;
}
public Date getAppDate()
{
return appDate;
}
public void add(Appointment a)
{
if (a.equals(a.getDate()))
{
if(a.getStartTime() < a.getEndTime())
{
}
}
else
{
System.out.print("");
}
}
Static block (almost)
Your code to exercise the class is in the wrong place. You have it stuck at the top of the class which is not syntactically correct. We can run code at the top, as a static block, but it needs to be labeled static { … }. A static block is not commonly used in my experience. And certainly is not the right place for what you are doing there.
main method
Instead you should be using a main method. This non-OOP little thingie is a trick, a hack, to solve the chicken-or-the-egg conundrum, to get us from no-app-running to our OOP idea of heaven with a bunch of objects floating around and passing messages to one another.
When first learning Java, do not try to understand all the syntax and purpose of the main method. Just accept it as a necessary evil to get the app running, it is merely the entrance point to execute the app. Focus on learning the OOP concepts and practices. Later, the main method and syntax will make more sense.
Accessors
Here is a simplified re-write of your example code. We are just using a LocalDate for simplicity, just enough to show (a) a main method, and (b) getter/setter accessor methods.
package com.basilbourque.example;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class AppointmentDate {
private LocalDate localDate;
// Constructor
public AppointmentDate ( LocalDate localDate ) {
this.localDate = localDate;
}
public LocalDate getLocalDate ( ) {
return localDate;
}
public void setLocalDate ( LocalDate localDate ) {
this.localDate = localDate;
}
#Override
public String toString ( ) {
return "AppointmentDate{ " +
"localDate=" + localDate +
" }";
}
// Not really a part of this class. A `main` method is just a hack to get our app launched.
public static void main ( String[] args ) {
String input = "23/01/2018";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );
LocalDate ld = LocalDate.parse( input , f );
AppointmentDate ad = new AppointmentDate( ld );
ad.setLocalDate( ld.plusWeeks( 1 ) );
LocalDate newValue = ad.getLocalDate();
System.out.println( newValue.toString() ); // Generate text representing the value of this `LocalDate` object in standard ISO 8601 format.
List < AppointmentDate > list = new ArrayList <>( 3 );
list.add( ad );
list.add( new AppointmentDate( LocalDate.parse( "2018-02-13" ) ) );
list.add( new AppointmentDate( LocalDate.parse( "2018-12-21" ) ) );
System.out.println( list );
}
}
2018-01-30
[AppointmentDate{ localDate=2018-01-30 }, AppointmentDate{ localDate=2018-02-13 }, AppointmentDate{ localDate=2018-12-21 }]
java.time
You are using terrible old date-time classes that were supplanted years ago by the java.time classes. Never use Date, Calendar, SimpleDateFormat, etc.
Appointments are tricky
While appointment tracking may seem intuitively simple, you are actually working on a very tricky subject.
The core problem is that politicians around the world are fond of redefining the time zone(s) under their jurisdiction. They do so quite frequently. They do so in both times of relative quiet and in times of turmoil.
The US and Canada have changed their offsets multiple times in recent decades. Turkey and Russia have changed their minds about going on or off DST multiple times in the last several years.
And politicians change their time zones with very little advance notice. And the notice seems to be growing shorter, despite the increased disturbance this causes in ever-more-computerized societies. Just last month, Morocco announced their country would stay on Daylight Saving Time (DST) permanent, cancelling on a Friday the DST cutover scheduled for that Sunday, leaving 0 business days warning — what a mess for IT staff. In May this year, North Korea slipped their clock a half-hour to sync with South Korea, with apparently immediate effect (no advance notice at all).
These frequent and unpredictable changes mean we cannot responsibly track future appointments as moments, as specific points on the timeline. When we say something like “3 PM on January 23rd” we usually mean 3 PM after politicians may have made their changes to the clock.
So we must store future appointments as a date and a time-of-day without a time zone or offset-from-UTC. Then, when calculating a calendar we must dynamically apply the rules for the intended time zone as currently defined on that day. If we perform this dynamic-determination of a moment today, and again in three days, if the politicians have announced a change in the time zone definition, and if we have been able to update our tzdata data files in our operating systems, database engines, Java Virtual Machines, and various libraries, then we will arrive at a different moment in time.
LocalDateTime
The Local… types in Java purposely lack any concept of time zone or offset-from-UTC. So they cannot represent a moment. So we never use these to pinpoint actual events that happened in the past. But these types are what we need for future appointments.
The LocalDateTime class represents a date with a time-of-day without any zone/offset.
LocalDate ld = LocalDate.of( 2018 , Month.JANUARY , 23 ) ;
LocalTime lt = LocalTime.of( 15 , 0 ) ; // 3 PM in 24-hour time.
LocalDateTime ldt= LocalDateTime.of( ld , lt ) ;
ZonedDateTime
When calculating a calendar, when we need a specific moment, we apply a time zone (ZoneId) to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ; // Determine a moment, a specific point on the timeline.
Instant
We can view that same moment in UTC by extracting a Instant.
Instant instant = zdt.toInstant() ; // Adjust to UTC.
Duration
Appointments are generally best stored as a starting point plus a duration. No need to store the stopping point as that can be calculated.
Duration d = Duration.ofHours( 1 ) ; // A one-hour appointment.
While we often want to adjust into a time zone for presentation to a user, generally behind-the-scenes it is best practice to track moments in UTC. So the starting and stopping points of an appointment calculated as moments should be done as a pair of Instant objects.
Instant start = ldt.atZone( z ).toInstant() ;
Instant stop = start.plus( d ) ;
Interval
We can leverage a class to represent this pair of Instant objects, Interval.
This class is found in the ThreeTen-Extra library, a project led by the same man than led the Joda-Time, JSR 310, and java.time projects, Stephen Colebourne.
This class has very handy methods for comparison such as abuts, overlaps, contains, and so on. You will likely want to use these methods in a scheduling app.
Appointment.java
Put this all together and we get a class like this:
package com.basilbourque.example;
import org.threeten.extra.Interval;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Appointment {
private LocalDateTime start;
private Duration duration;
// Constructor.
public Appointment ( LocalDateTime start , Duration duration ) {
this.start = start;
this.duration = duration;
}
// Might add some getter/setter methods in here.
// Dynamically determine the start and stop points of this appointment, given today’s definition of the intended time zone.
public Interval toInterval ( ZoneId zoneId ) {
ZonedDateTime zdtStart = this.start.atZone( zoneId );
Interval interval = Interval.of( zdtStart.toInstant() , this.duration );
return interval;
}
}
When you generate a Interval by calling your toInterval method, you may want the individual start and stop moments.
Instant start = interval.getStart() ;
Instant stop = interval.getEnd() ;
Those two Instant objects are in UTC by definition. If you want to see them through the lens of the wall-clock time used by the people of a particular region, apply a ZoneId to get a ZonedDateTime object.
ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtStart = start.atZone( z ) ; // Adjust from UTC to some time zone. Same moment, same point on the timeline, different wall-clock time.
ZonedDateTime zdtStop = stop.atZone( z ) ;
Future
You asked about checking to see if this appointment is in the future. Again, we need a time zone to properly answer than. The time zones around the world currently cover a range of about 26 to 27 hours. So within in that many hours of the current moment we cannot tell if a LocalDateTime is in the future or past without considering a time zone.
So let's add a method testing for the future that requires passing a time zone.
// Dynamically determine if this appointment will be in the future for some specific time zone.
public Boolean isFuture ( ZoneId zoneId ) {
Objects.requireNonNull( zoneId , "Must pass a time zone to determine if an appointment is in the future. Message # e1c64bc1-9a44-4d15-b20d-e68414fb5ab5.");
ZonedDateTime zdtStart = this.start.atZone( zoneId );
ZonedDateTime zdtNow = ZonedDateTime.now( zoneId );
boolean isInTheFuture = zdtNow.isBefore( zdtStart );
return isInTheFuture ;
}
Start/Stop moments
Continuing the same theme on dynamically determining moments, let's add some methods to return the starting moment (inclusive) and stopping moment (exclusive). As discussed above, this requires passing a time zone.
The calling programmer could do this work herself. But I suspect this might be frequently needed enough to warrant adding these methods as a convenience.
// Get start moment for a particular time zone.
public ZonedDateTime toStartMoment ( ZoneId zoneId ) {
ZonedDateTime zdt = this.toInterval( zoneId ).getStart().atZone( zoneId );
return zdt;
}
// Get stop moment for a particular time zone.
public ZonedDateTime toStopMoment ( ZoneId zoneId ) {
ZonedDateTime zdt = this.toInterval( zoneId ).getEnd().atZone( zoneId );
return zdt;
}
Notice that I did not name these methods with the get…. Accessor methods, getters & setters, by convention imply accessing a simple property stored within the object. But here we are not storing the ZonedDateTime objects. These are dynamically-determined, so using a get… method could be misleading. Instead, I am trying to follow the naming conventions laid down in the java.time project.
Immutable objects
Another lesson to learn from the java.time project is the immutable objects pattern.
Certain kinds of classes lend themselves to being read-only, created but not modified. The java.time classes certainly qualify. Whereas an invoice, for example, is expected to “mutate” (change), intuitively as a programmer I do not expect the date on the invoice to change unless I explicitly replace the date with a new object. So I want invoice to be a mutable object, but I want the LocalDate object stored on that invoice to be immutable.
I suspect our Appointment class might also be best designed an an immutable. So we have no setter methods involved. To effectively alter an appointment in your scheduling app, create a new Appointment object based on some of the values of the existing Appointment object. Notice in the java.time classes how this is done with various with methods, where the methods return a new object based on the original’s values but with some alteration.
Appointment.java version 2
Let's put all that together into one example class.
And let's add a main method to exercise this class. First we create one appointment, and look at its dynamically-determined moments in UTC. Second, we collect some Appointment objects in a collection.
We have added a toString method override to report on the object’s state.
package com.basilbourque.example;
import org.threeten.extra.Interval;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
// An example class to show date-time handling for future appointments.
// Not necessarily ready for production use. Use at your own risk.
// Methods named according to the java.time naming conventions:
// https://docs.oracle.com/javase/tutorial/datetime/overview/naming.html
public class Appointment {
private LocalDateTime start;
private Duration duration;
// Constructor.
public Appointment ( LocalDateTime start , Duration duration ) {
this.start = start;
this.duration = duration;
}
// Dynamically determine the start and stop points of this appointment, given today’s definition of the intended time zone.
public Interval toInterval ( ZoneId zoneId ) {
Objects.requireNonNull( zoneId , "Must pass a time zone to get the start/stop interval of an appointment. Message # bbf021e6-baa7-468d-83ad-cf73acb6702e." );
ZonedDateTime zdtStart = this.start.atZone( zoneId );
Interval interval = Interval.of( zdtStart.toInstant() , this.duration );
return interval;
}
// Get start moment for a particular time zone.
public ZonedDateTime toStartMoment ( ZoneId zoneId ) {
ZonedDateTime zdt = this.toInterval( zoneId ).getStart().atZone( zoneId );
return zdt;
}
// Get stop moment for a particular time zone.
public ZonedDateTime toStopMoment ( ZoneId zoneId ) {
ZonedDateTime zdt = this.toInterval( zoneId ).getEnd().atZone( zoneId );
return zdt;
}
// Dynamically determine if this appointment will be in the future for some specific time zone.
public Boolean isFuture ( ZoneId zoneId ) {
Objects.requireNonNull( zoneId , "Must pass a time zone to determine if an appointment is in the future. Message # e1c64bc1-9a44-4d15-b20d-e68414fb5ab5." );
ZonedDateTime zdtStart = this.start.atZone( zoneId );
ZonedDateTime zdtNow = ZonedDateTime.now( zoneId );
boolean isInTheFuture = zdtNow.isBefore( zdtStart );
return isInTheFuture;
}
// -----------| Object overrides |---------------------------
#Override
public String toString ( ) {
return "Appointment{ " +
"start=" + start +
" | duration=" + duration +
" }";
}
// -----------| main |-------------
public static void main ( String[] args ) {
// See if a new appointment is in the future.
Appointment a = new Appointment( LocalDateTime.of( 2018 , 12 , 25 , 0 , 0 , 0 , 0 ) , Duration.ofHours( 2 ) );
ZoneId z = ZoneId.of( "America/Montreal" );
System.out.println( "For time zone: " + z + ", appointment interval is: " + a.toInterval( z ) );
System.out.println( "Start: " + a.toStartMoment( z ) );
System.out.println( "Stop: " + a.toStopMoment( z ) );
Boolean isFuture = a.isFuture( z );
System.out.println( a.toString() + " is future t/f: " + isFuture );
// Collect some appointments.
List < Appointment > list = new ArrayList <>( 3 );
list.add( a );
list.add( new Appointment( LocalDateTime.of( 2018 , 12 , 13 , 15 , 0 , 0 , 0 ) , Duration.ofMinutes( 90 ) ) );
list.add( new Appointment( LocalDateTime.of( 2018 , 12 , 30 , 16 , 0 , 0 , 0 ) , Duration.ofHours( 1 ) ) );
System.out.println( list );
}
}
When run.
For time zone: America/Montreal, appointment interval is: 2018-12-25T05:00:00Z/2018-12-25T07:00:00Z
Start: 2018-12-25T00:00-05:00[America/Montreal]
Stop: 2018-12-25T02:00-05:00[America/Montreal]
Appointment{ start=2018-12-25T00:00 | duration=PT2H } is future t/f: true
[Appointment{ start=2018-12-25T00:00 | duration=PT2H }, Appointment{ start=2018-12-13T15:00 | duration=PT1H30M }, Appointment{ start=2018-12-30T16:00 | duration=PT1H }]
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, 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.

Check if two instances of "Instant" are on the same date in Java 8

I have two instances of the Instant class from java.time such as this:
Instant instant1 = Instant.now();
Instant instant2 = Instant.now().plus(5, ChronoUnit.HOURS);
Now I would like to check if the two instances of Instant are actually on the same date (day, month and year match). Easy I thought, let's just use the shiny new LocalDate and the universal from static method:
LocalDate localdate1 = LocalDate.from(instant1);
LocalDate localdate2 = LocalDate.from(instant2);
if (localdate1.equals(localdate2)) {
// All the awesome
}
Except that universal from method is not so universal and Java complains at runtime with an exception:
java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: 2014-11-04T18:18:12Z of type java.time.Instant
Which leaves me back at square 1.
What is the recommended/fastest way check if two instances of Instant actually have the same date (have the same day, month, and year)?
The Instant class does not work with human units of time, such as
years, months, or days. If you want to perform calculations in those
units, you can convert an Instant to another class, such as
LocalDateTime or ZonedDateTime, by binding the Instant with a time
zone. You can then access the value in the desired units.
http://docs.oracle.com/javase/tutorial/datetime/iso/instant.html
Therefore I suggest the following code:
LocalDate ld1 = LocalDateTime.ofInstant(instant1, ZoneId.systemDefault()).toLocalDate();
LocalDate ld2 = LocalDateTime.ofInstant(instant2, ZoneId.systemDefault()).toLocalDate();
if (ld1.isEqual(ld2)) {
System.out.println("blubb");
}
Alternatively you could use
instant.atOffset(ZoneOffset.UTC).toLocalDate();

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