I want a custom calendar like this:
enum TradingDays {Monday, Tuesday, Wednesday, Thursday, Friday};
Then I need to iterate over it and check if a particular enum element is the day of week TODAY. The problem is that the JAVA calendar does not match to days of week from my calendar. So:
Calendar now = Calendar.getInstance();
TradingDays.Monday is not equal to any of now.get(Calendar.DAY_OF_WEEK);
So how do I assign Monday, Tuesday etc from my calendar TradingDays the same type (in this case an integer value) from the JAVA calendar?
P.S. I need to have that calendar TradingDays like that because it is then shown to the user so he/she chooses on which days to trade.
tl;dr
Set<DayOfWeek> tradingDays = EnumSet.range( DayOfWeek.MONDAY , DayOfWeek.FRIDAY ) ; // Mon, Tue, Wed, Thu, & Fri.
Boolean todayIsTradingDay = tradingDays.contains( LocalDate.now( ZoneId.of( "America/Montreal" ) ).getDayOfWeek() ) ;
Details
This functionality is built into Java.
java.time.DayOfWeek enum
Java includes the java.time.DayOfWeek enum.
Pass around objects of this enum rather than mere integers 1-7 to make your code more self-documenting, provide type-safety, and ensure a range of valid values.
invoices.printReportForDayOfWeek( DayOfWeek.MONDAY );
Numbering is 1-7 for Monday to Sunday, per ISO 8601 standard.
Get a localized name of the day by calling getDisplayName.
String output = DayOfWeek.MONDAY.getDisplayName( TextStyle.FULL , Locale.CANADA_FRENCH ) ; // Or Locale.US, etc.
Collection of day-of-week objects
To track multiple days of the week, use an EnumSet (implementation of Set) or EnumMap (implementation of Map).
Set<DayOfWeek> weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;
…
DayOfWeek dowToday =
LocalDate
.now( ZoneId.of( "America/Montreal" ) )
.getDayOfWeek() ;
Boolean todayIsWeekend = weekend.contains( dowToday ) ;
Or in the case of this Question specifically, a collection of the weekdays. Perhaps define as a static final constant if the definition does not change during execution of the app.
static final Set<DayOfWeek> tradingDays = EnumSet.of( DayOfWeek.MONDAY , DayOfWeek.TUESDAY , DayOfWeek.WEDNESDAY , DayOfWeek.THURSDAY , DayOfWeek.FRIDAY ) ;
Or make that even shorter by defining an EnumSet as a range of enum objects defined in a sequential order. Specify MONDAY & FRIDAY and let EnumSet fill in the values in between. Use EnumSet.range.
static final Set<DayOfWeek> tradingDays = EnumSet.range( DayOfWeek.MONDAY , DayOfWeek.FRIDAY ) ; // Mon, Tue, Wed, Thu, & Fri.
Then test for today. Note that a time zone is crucial in determining the current date. For any given moment the date varies around the globe by zone.
ZoneId zoneId = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( zoneId ) ;
DayOfWeek dowToday = today.getDayOfWeek() ;
Boolean todayIsTradingDay = tradingDays.contains( dowToday ).getDayOfWeek() ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in 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.
You can try using a constructor inside your enum, like in this example:
public enum Currency {
PENNY(1),
NICKLE(5),
DIME(10),
QUARTER(25);
private final int value;
private Currency(int value) {
this.value=value;
}
}
While iterating you can use coin.value like this:
for(Currency coin: Currency.values()){
System.out.println(coin+" "+coin.value);
if(coin.value==1){
System.out.println("THIS is the PENNY");
}
}
Which output is:
PENNY 1
THIS is the PENNY
NICKLE 5
DIME 10
QUARTER 25
With enums you can do a lot of things that probably are familiar to you:
File TradingDays.java:
public enum TradingDays {
Monday(Calendar.MONDAY),
Tuesday(Calendar.TUESDAY),
Wednesday(Calendar.WEDNESDAY),
Thursday(Calendar.THURSDAY),
Friday(Calendar.FRIDAY);
private int calendarValue;
TradingDays(int calendarValue) {
this.calendarValue = calendarValue;
}
public static TradingDays today() {
return fromCalendarValue(Calendar.getInstance().get(Calendar.DAY_OF_WEEK));
}
public static TradingDays fromCalendarValue(int calendarValue) {
for(TradingDays td : TradingDays.values()) {
if(td.calendarValue == calendarValue) {
return td;
}
}
throw new IllegalArgumentException(calendarValue + " is not a valid TradingDays calendarValue");
// or simply return null
}
};
Create a constructor inside your enum which takes an int value:
public class CalendarMain {
enum TradingDays {
Monday(2), Tuesday(3), Wednesday(4), Thursday(5), Friday(6);
private int value;
private TradingDays(int value) {
this.value = value;
}
};
public static void main(String[] args) {
Calendar now = Calendar.getInstance();
if (now.get(Calendar.DAY_OF_WEEK) == TradingDays.Tuesday.value) {
// Chose Wednesday as the day to trade
System.out.println(now.get(Calendar.DAY_OF_WEEK));
System.out.println(TradingDays.Tuesday.value);
}
}
}
Related
I need some assistance in try to pretty much assert each date within an element against the actual dates.
Basically I have 5 date tiles in my application that are displayed like so:
Mon 20th May
Tue 21st May
Wed 22nd May
Thu 23rd May
Fri 24th May
Now the elements for these tiles are the same where the day has an element of ‘id=day’ (e.g. Mon, Tue, Wed) and the date has an element of ‘id=date’ (e.g. 20th May, 21st May, 22nd May) etc.
Below explains what I am trying to do:
In the application it always shows 5 dates
The 5 dates shown always starts from tomorrow’s date and shows the next 5 dates excluding Sunday.
I want to perform an assertion to check the 5 days from the date tiles
(e.g. Mon, Tue, Wed) matches the above criteria.
I want to also perform an assertion to check the 5 dates from the date
tiles (e.g. 20th May, 21st May, 22nd May) matches the above criteria.
How can this be achieved?
I have set a little date picker below to start off with but I am not a developer so will require somebody with a little more coding knowledge and logic to help achieve this:
SimpleDateFormat sdf = new SimpleDateFormat("E-dd-MMM");
Calendar cal = Calendar.getInstance();
// get start date
cal.add(Calendar.DAY_OF_YEAR, +1);
// loop adding one day in each iteration
for(int i = 0; i< 5; i++){
cal.add(Calendar.DAY_OF_YEAR, 1);
System.out.println(sdf.format(cal.getTime()));
}
Avoid legacy classes
You are using terrible date-time classes that were supplanted years ago by the java.time classes defined in JSR 310.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone or offset-from-UTC.
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 ) ;
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.
Or specify a date. You may set the month by a number, with sane numbering 1-12 for January-December.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Or, better, use the Month enum objects pre-defined, one for each month of the year. Tip: Use these Month objects throughout your codebase rather than a mere integer number to make your code more self-documenting, ensure valid values, and provide type-safety. Ditto for Year & YearMonth.
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
DayOfWeek
If you need to track the day-of-week (not clear in your Question), use the DayOfWeek enum.
DayOfWeek dow = ld.getDayOfWeek() ;
Tile
Your Tile class should hold a member variable of type LocalDate. Use smart objects rather than dumb strings. Generate localized text for display to your users, but your internal business logic should be using objects of types specific to the task.
Your class should implement the overridden toString method to generate text helpful when debugging or logging. Define a separate method named something like getDisplayName to generate text appropriate for display to the user.
public class Tile {
LocalDate localDate ;
static private DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "EEE dd MMM" , Locale.US ) ;
public Tile( LocalDate localDateArg ) {
this.localDate = localDateArg ;
}
public String getDisplayName() {
String output = this.localDate.format( Tile.formatter ) ;
return output ;
}
#Override
public String toString() {
String output = Tile.class.getSimpleName() + "{ localDate:" + this.localDate.toString() + " }" ;
}
}
If you really need the th, st, on your numbers, you will need to take extra steps. This feature is not built into DateTimeFormatter. Search on Stack Overflow for "ordinal date" as this has been covered multiple times already. I suggest adding this feature last, after you have the rest working.
Generating tiles
The 5 dates shown always starts from tomorrow’s date and shows the next 5 dates excluding Sunday.
So we need a list of tiles. For each new tile, discard if found to be a Sunday.
int limit = 5 ;
List< Tile > tiles = new ArrayList<>( limit ) ;
Get today's date. Increment to each next date, omitting Sunday.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate tomorrow = LocalDate.now( z ).plusDays( 1 ) ; // Tomorrow is today plus one day.
LocalDate localDate = tomorrow ;
for ( int i = 1 ; i <= limit ; i ++ ) {
if( localDate.getDayOfWeek().equals( DayOfWeek.SUNDAY ) { // If this date is a Sunday…
localDate = localDate.plusDays( 1 ) ; // Omit Sunday, bump to the next date.
}
Tile tile = new Tile( localDate ) ;
tiles.add( tile ) ;
// Set up the next loop.
localDate = localDate.plusDays( 1 ) ;
}
I want to perform an assertion to check the 5 days from the date tiles (e.g. Mon, Tue, Wed) matches the above criteria.
Write a little method to check our collection of tiles. I used logic similar to the method that creates the list of tiles.
public boolean areTilesValidByDate( final List< Tile > tiles ) {
Objects.requireNonNull( tiles ) ; // Verify we were passed a list.
int limit = 5 ;
if( tiles.size() != limit ) { // If unexpected size, the list cannot be valid.
return false ;
}
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate tomorrow = LocalDate.now( z ).plusDays( 1 ) ; // Tomorrow is today plus one day.
LocalDate localDate = tomorrow ;
// Loop through the five dates we expect to find in the existing tiles.
for ( int i = 1 ; i <= limit ; i ++ ) {
if( localDate.getDayOfWeek().equals( DayOfWeek.SUNDAY ) { // If this date is a Sunday…
localDate = localDate.plusDays( 1 ) ; // Omit Sunday, bump to the next date.
}
Tile tile = tiles.get( i - 1 ) ; // Subtract one for annoying zero-based index counting rather than 1-based ordinal counting. Index-counting comes from simplistic array and byte-jumping code in old C-style languages, and is a habit the industry finds difficult to shake off.
if( ! tile.localDate.isEqual( localDate ) {
return false ;
}
// Set up the next loop.
localDate = localDate.plusDays( 1 ) ;
}
// If we make it to this point, the list must be valid.
return true ;
}
In real work, I would not be hard-coding the time zone (the ZoneId).
Caveat: I never ran any of this code. You may need to fix. (⇐ The understatement is a developer joke.)
P.S. If you are making software in use by people, then you are a developer. Welcome to the club.
Tip: Search Stack Overflow thoroughly before posting. I helped you out this time, but perhaps should not have as we generally discourage simple duplicates on this site. All this material has been covered many many times on Stack Overflow. Struggling a bit to pull together various found nuggets will help you learn more.
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.
I am searching for a method to find the last date of a input day of week from now on? For example: If today is Monday, the 5th march 2018 the results should look like this:
Input Output
Monday 05.03.2018
Tuesday 27.02.2018
Wednesday 28.02.2018
Thursday 01.03.2018
Friday 02.03.2018
Saturday 03.03.2018
Sunday 04.03.2018
I hope you get the idee. I couldn't really find a post with something similar, so any help would be much appreciated
As posted in comments, this is the code I have atm:
private String getLastDateOfDayOfWeek(String day, String returnDateFormat) throws ParseException {
int dayOfWeek = parseDayOfWeek(day, Locale.ENGLISH);
Calendar cal = Calendar.getInstance(); // Today, now
if (cal.get(Calendar.DAY_OF_WEEK) != dayOfWeek) {
// ...
}
return new SimpleDateFormat(returnDateFormat).format(cal.getTime());
}
private static int parseDayOfWeek(String day, Locale locale)
throws ParseException {
SimpleDateFormat dayFormat = new SimpleDateFormat("EEEE", locale);
Date date = dayFormat.parse(day);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
return dayOfWeek;
}
Atm I have two functions: one that can convert a string to a Calender dayOfWeek number and another one, which is the method i am searching for. Currently it only handles todays day of week correct, the part that should do the work for every other day of the week is missing(comment with ...)
tl;dr
LocalDate.now().with( // Get today’s date, then move to another date.
TemporalAdjusters.previousOrSame( // An implementation of `TemporalAdjuster` interface, used for algorithms to move to another date.
DayOfWeek.valueOf( “Monday”.toUpperCase() ) // Get the enum Object with te corresponding hard-coded name in English such as `DayOfWeek.MONDAY`.
)
)
DayOfWeek
The DayOfWeek enum holds seven objects, one for each day of the week.
English
The names of these seven objects are in English, all uppercase. So you can get the Object from the English word.
String input = “Monday”.toUpperCase() ;
DayOfWeek dow = DayOfWeek.valueOf( input ) ;
Other languages
For languages other than English, define a List populated with the name of each day-of-week. Use the name generated from DayOfWeek::getDisplayName, a method that automatically localizes. Start the list with Monday, per the ISO 8601 standard. Search that list to find a match with your input. Get the ordinal number of your match, 1-7 (not the index number 0-6). Pass that number to DayOfWeek.valueOf to get a DayOfWeek object. In some languages you’ll need a pair of such lists to be searched, as an alternate spelling may be invoked for a “standalone” use of the day-of-week without the context of a date.
Here is an example of such a class. Beware: I just whipped up this class without much thought and without any serious testing. Use at your own risk. Usage: DayOfWeekDelocalizer.of( Locale.CANADA_FRENCH ).parse( "lundi" ) → DayOfWeek.MONDAY
package com.basilbourque.example;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.DayOfWeek;
import java.time.format.TextStyle;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
// For a given name of day-of-week in some language, determine the matching `java.time.DayOfWeek` enum object.
// This class is the opposite of `DayOfWeek.getDisplayName` which generates a localized string for a given `DayOfWeek` object.
// Usage… DayOfWeekDelocalizer.of( Locale.CANADA_FRENCH ).parse( "lundi" ) → DayOfWeek.MONDAY
// Assumes `FormatStyle.FULL`, for day-of-week names without abbreviation.
// USE AT YOUR OWN RISK. Rough draft, quickly written. No serious testing.
public class DayOfWeekDelocalizer
{
#NotNull
private Locale locale;
#NotNull
private List < String > dayOfWeekNames, dayOfWeekNamesStandalone; // Some languages use an alternate spelling for a “standalone” day-of-week used without the context of a date.
// Constructor. Private, for static factory method.
private DayOfWeekDelocalizer ( #NotNull Locale locale )
{
this.locale = locale;
// Populate the pair of arrays, each having the translated day-of-week names.
int daysInWeek = 7; // Seven days in the week.
this.dayOfWeekNames = new ArrayList <>( daysInWeek );
this.dayOfWeekNamesStandalone = new ArrayList <>( daysInWeek );
for ( int i = 1 ; i <= daysInWeek ; i++ )
{
this.dayOfWeekNames.add( DayOfWeek.of( i ).getDisplayName( TextStyle.FULL , this.locale ) );
this.dayOfWeekNamesStandalone.add( DayOfWeek.of( i ).getDisplayName( TextStyle.FULL_STANDALONE , this.locale ) );
}
// System.out.println( this.dayOfWeekNames );
// System.out.println( this.dayOfWeekNamesStandalone );
}
// Constructor. Private, for static factory method.
// Personally, I think it unwise to default implicitly to a `Locale`. But I included this in case you disagree with me, and to follow the lead of the *java.time* classes. --Basil Bourque
private DayOfWeekDelocalizer ( )
{
this( Locale.getDefault() );
}
// static factory method, instead of constructors.
// See article by Dr. Joshua Bloch. http://www.informit.com/articles/article.aspx?p=1216151
// The `Locale` argument determines the human language and cultural norms used in de-localizing input strings.
synchronized static public DayOfWeekDelocalizer of ( #NotNull Locale localeArg )
{
DayOfWeekDelocalizer x = new DayOfWeekDelocalizer( localeArg ); // This class could be optimized by caching this object.
return x;
}
// Attempt to translate the name of a day-of-week to look-up a matching `DayOfWeek` enum object.
// Returns NULL if the passed String value is not found to be a valid name of day-of-week for the human language and cultural norms of the `Locale` specified when constructing this parent object, `DayOfWeekDelocalizer`.
#Nullable
public DayOfWeek parse ( #NotNull String input )
{
int index = this.dayOfWeekNames.indexOf( input );
if ( - 1 == index )
{ // If no hit in the contextual names, try the standalone names.
index = this.dayOfWeekNamesStandalone.indexOf( input );
}
int ordinal = ( index + 1 );
DayOfWeek dow = ( ordinal > 0 ) ? DayOfWeek.of( ordinal ) : null; // If we have a hit, determine the DayOfWeek. Else return null.
return dow;
}
// `Object` overrides.
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
DayOfWeekDelocalizer that = ( DayOfWeekDelocalizer ) o;
return locale.equals( that.locale );
}
#Override
public int hashCode ( )
{
return locale.hashCode();
}
public static void main ( String[] args )
{
// Quick testing.
// DayOfWeekDelocalizer x = DayOfWeekDelocalizer.of( Locale.JAPAN );
if ( DayOfWeekDelocalizer.of( Locale.CANADA_FRENCH ).parse( "lundi" ).equals( DayOfWeek.MONDAY ) )
{
System.out.println( "GOOD - Canada French 'lundi' is parsing to DayOfWeek.MONDAY." );
} else
{
System.out.println( "BAD - Canada French 'lundi' is NOT parsing to DayOfWeek.MONDAY." );
}
}
}
Tip: Using a String to represent a DayOfWeek is clumsy. Your code should instead be passing around a DayOfWeek enum object.
LocalDate
Next we need the current date.
LocalDate today = LocalDate.now() ;
Better to explicitly state your desired/expected time zone than rely implicitly on the JVM’s current default time zone.
ZoneId z = ZoneId.of( “Africa/Tunis” ) ;
LocalDate today = LocalDate.now( z ) ;
TemporalAdjuster
Move to another date by applying a TemporalAdjuster. The TemporalAdjusters class offers the implementation we need.
TemporalAdjuster ta = TemporalAdjusters.previousOrSame( dow ) ;
LocalDate ld = today.with( ta ) ;
Note that java.time uses Immutable Objects. So rather than modify an object, methods produce a new distinct object with altered values based on the original.
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, 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.
I'd like to have a random millisecond value from an (inverse) linear distribution of values (if I get the term right).
In essence I want to have a random point-in-time t (Date in my case) between two time points early and late where a t towards early has a much greater probability then those towards late. late itself may have a probability of 0.0.
My current java code just uses uniform distribution, so I plan to modify this to a (inverse) linear distribution:
public Date getRandomDate(Date early, Date late) {
long diff = late.getTime() - early.getTime();
final int randVal = rand.nextInt((int) diff);
Calendar cal = Calendar.getInstance();
cal.setTime(early);
cal.add(Calendar.MILLISECOND, randVal);
return cal.getTime();
}
Piggy-backing off of this answer to a similar question, you could just take the minimum of two rand calls:
final int randVal = Math.min(rand.nextInt((int) diff), rand.nextInt((int) diff));
Finally, here is another more complex way that solves for x using the cumulative distribution function (x^2):
int randVal = (int) Math.floor(diff * (1.0 - Math.sqrt(rand.nextDouble())));
if(randVal >= diff) randVal = 0; // handle the edge case
To meet your specified requirements, the square root has been subtracted from 1.0 to invert the distribution, i.e. putting the greater density at the bottom of the range.
The accepted Answer by Parker seems to be correct and well-done.
Using java.time
The Question uses outmoded troublesome date-time classes that are now legacy, supplanted by the java.time classes. Here is the same kind of code, along with Parker’s solution, rewritten in java.time.
Instant
First, if you must work with java.util.Date objects, convert to/from Instant. 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). To convert, look to new methods added to the old classes.
Instant instant = myJavaUtilDate.toInstant(); // From legacy to modern class.
java.util.Date myJavaUtilDate = java.util.Date.from( instant ) ; // From modern class to legacy.
Let's rewrite the method signature but passing and returning Instant objects.
public Instant getRandomDate( Instant early , Instant late) {
Verify the early argument is indeed earlier than the later argument. Alternatively, assert that Duration seen below is not negative ( ! duration.isNegative() ).
if( early.isAfter( late) ) { … } // Assert `early` is not after `late`.
Half-Open
Calculate the delta between the earliest and latest moments. This is done in the Half-Open approach often used to define spans of time, where the beginning is inclusive and the ending is exclusive.
Duration
The Duration class represents such a span in terms of a total number of seconds plus a fractional second in nanoseconds.
Duration duration = Duration.between( early , late ) ;
To do our random math, we want a single integer. To handle nanoseconds resolution, we need a 64-bit long rather than a 32-bit int.
ThreadLocalRandom
Tip: If generating these values across threads, use the class ThreadLocalRandom. To quote the doc:
When applicable, use of ThreadLocalRandom rather than shared Random objects in concurrent programs will typically encounter much less overhead and contention.
We can specify the range in Half-Opened style with the origin being inclusive and the bound being exclusive by calling ThreadLocalRandom::nextLong( origin , bound ).
long bound = duration.toNanos() ;
long nanos1 = ThreadLocalRandom.current().nextLong( 0 , bound );
long nanos2 = ThreadLocalRandom.current().nextLong( 0 , bound );
long nanos = Math.min( nanos1 , nanos2 ); // Select the lesser number.
Instant instant = early.plusNanos( nanos );
return instant ;
}
Live example
See the code below run live at IdeOne.com.
We extract the number of date-time values generated for each date-only (LocalDate) as a casual way to survey the results to verify our desired results skewed towards earlier dates.
The test harness shows you how to assign a time zone (ZoneId) to an Instant to get a ZonedDateTime object, and from there extract a LocalDate. Use that as a guide if you wish to view the Instant objects through the lens of some particular region’s wall-clock time rather than in UTC.
/* package whatever; // don't place package name! */
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.concurrent.ThreadLocalRandom ;
import java.util.TreeMap ;
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Ideone app = new Ideone();
app.doIt();
}
public void doIt() {
ZoneId z = ZoneId.of( "America/Montreal" ) ;
int count = 10 ;
LocalDate today = LocalDate.now( z );
LocalDate laterDate = today.plusDays( count );
Instant start = today.atStartOfDay( z ).toInstant();
Instant stop = laterDate.atStartOfDay( z ).toInstant();
// Collect the frequency of each date. We want to see bias towards earlier dates.
List<LocalDate> dates = new ArrayList<>( count );
Map<LocalDate , Integer > map = new TreeMap<LocalDate , Integer >();
for( int i = 0 ; i <= count ; i ++ ) {
LocalDate localDate = today.plusDays( i ) ;
dates.add( localDate ); // Increment to next date and remember.
map.put( localDate , new Integer( 0 ) ); // Prepopulate the map with all dates.
}
for( int i = 1 ; i <= 10_000 ; i ++ ) {
Instant instant = this.getRandomInstantBetween( start , stop );
LocalDate localDate = instant.atZone( z ).toLocalDate();
Integer integer = map.get( localDate );
map.put( localDate , integer + 1); // Increment to count each time get a hit on this date.
}
System.out.println( map );
}
public Instant getRandomInstantBetween( Instant early , Instant late) {
Duration duration = Duration.between( early , late ) ;
// Assert the duration is positive or zero: ( ! duration.isNegative() )
long bound = duration.toNanos() ;
ThreadLocalRandom random = ThreadLocalRandom.current() ;
long nanos1 = random.nextLong( 0 , bound ); // Zero means the `early` date is inclusive, while `bound` here is exclusive.
long nanos2 = random.nextLong( 0 , bound );
long nanos = Math.min( nanos1 , nanos2 ); // Select the lesser number.
Instant instant = early.plusNanos( nanos );
return instant;
}
}
Here are some sample results. These look good to me, but I'm no statistician. Use at your own risk.
{2017-02-24=1853, 2017-02-25=1697, 2017-02-26=1548, 2017-02-27=1255, 2017-02-28=1130, 2017-03-01=926, 2017-03-02=706, 2017-03-03=485, 2017-03-04=299, 2017-03-05=101, 2017-03-06=0}
{2017-02-25=930, 2017-02-26=799, 2017-02-27=760, 2017-02-28=657, 2017-03-01=589, 2017-03-02=470, 2017-03-03=342, 2017-03-04=241, 2017-03-05=163, 2017-03-06=49, 2017-03-07=0}
{2017-02-25=878, 2017-02-26=875, 2017-02-27=786, 2017-02-28=676, 2017-03-01=558, 2017-03-02=440, 2017-03-03=370, 2017-03-04=236, 2017-03-05=140, 2017-03-06=41, 2017-03-07=0}
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, andfz more.
Perhaps you could apply analogy to Date as shown in this answer.
Java: random integer with non-uniform distribution
I have a list of Date objects, and a target Date. I want to find the date in the list that's nearest to the target date, but only dates that are before the target date.
Example:
2008-10-1
2008-10-2
2008-10-4
With a target date of 2008-10-3, I want to get 2008-10-2
What is the best way to do it?
Sietse de Kaper solution assumes a reverse sorted list, definitely not the most natural thing to have around
The natural sort order in java is following the ascending natural ordering. (see Collection.sort http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collections.html#sort(java.util.List) documentation)
From your example,
target date = 2008-10-03
list = 2008-10-01 2008-10-02 2008-10-04
If another developper uses your method with a naive approach he would get 2008-10-01 which is not what was expected
Don't make assumptions as to the ordering of the list.
If you have to for performance reasons try to follow the most natural convention (sorted ascending)
If you really have to follow another convention you really should document the hell out of it.
private Date getDateNearest(List<Date> dates, Date targetDate){
Date returnDate = targetDate
for (Date date : dates) {
// if the current iteration'sdate is "before" the target date
if (date.compareTo(targetDate) <= 0) {
// if the current iteration's date is "after" the current return date
if (date.compareTo(returnDate) > 0){
returnDate=date;
}
}
}
return returnDate;
}
edit - I also like the Treeset answer but I think it might be slightly slower as it is equivalent to sorting the data then looking it up => nlog(n) for sorting and then the documentation implies it is log(n) for access so that would be nlog(n)+log(n) vs n
private Date getDateNearest(List<Date> dates, Date targetDate){
return new TreeSet<Date>(dates).lower(targetDate);
}
Doesn't require a pre-sorted list, TreeSort fixes that. It'll return null if it can't find one though, so you will have to modify it if that's a problem. Not sure of the efficency either :P
I currently use the following method, but I'm not sure it's the most effective one, because this assumes an already sorted list, and (potentially) iterates over every single date in the list.
private Date getDateNearest(List<Date> dates, Date targetDate){
for (Date date : dates) {
if (date.compareTo(targetDate) <= 0) return date;
}
return targetDate;
}
Although the answer from Keeg is valid in 1.6 in 1.5 there is no method lower() (We're unfortunate to develop against 1.5 :-( )
this one works in 1.5
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.TreeSet;
public class GetNearestDate {
public static void main( String[] args ) throws ParseException {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "dd.MM.yyyy HH:mm:ss" );
List< Date > otherDates = Arrays.asList( new Date[]{
simpleDateFormat.parse( "01.01.2008 01:00:00" ) ,
simpleDateFormat.parse( "01.01.2008 01:00:02" ) } );
System.out.println( simpleDateFormat.parse( "01.01.2008 01:00:00" ).equals(
get( otherDates , simpleDateFormat.parse( "01.01.2008 01:00:01" ) ) ) );
System.out.println( simpleDateFormat.parse( "01.01.2008 01:00:02" ).equals(
get( otherDates , simpleDateFormat.parse( "01.01.2008 01:00:03" ) ) ) );
System.out.println( null == get( otherDates , simpleDateFormat.parse( "01.01.2008 01:00:00" ) ) );
}
public static Date get( List< Date > otherDates , Date dateToApproach ) {
final TreeSet< Date > set = new TreeSet< Date >( otherDates );
set.add( dateToApproach );
final ArrayList< Date > list = new ArrayList< Date >( set );
final int indexOf = list.indexOf( dateToApproach );
if ( indexOf == 0 )
return null;
return list.get( indexOf - 1 );
}
}
NavigableSet::lower
The Answer by Keeg is cleverly brief. The idea there is to make use of the lower method defined in the NavigableSet interface and implemented in the TreeSet class.
But like the other answers it uses the old outmoded date-time classes bundled with the earliest versions of Java. Below is an updated version using java.time classes.
The old question and answers are using either java.util.Date which is a moment on the timeline in UTC representing both a date and a time-of-day, or the java.sql.Date which awkwardly extends util.Date while pretending it does not have a time-of-day. A confusing mess.
java.time
Those troublesome old classes have been supplanted by the java.time classes built into Java 8 and later. See Oracle Tutorial. Much of the functionality has been back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone. While these objects store no time zone, note that time zone (ZoneId) is crucial in determining the current date. For any given moment the date varies around the globe by time zone.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId ); // 2016-06-25
ISO 8601
Tip: Pad those month and day-of-month numbers with a leading zero. This makes them comply with the ISO 8601 standard date-time formats. These formats are used by default in java.time when parsing/generating strings that represent date-time values.
So use 2008-10-01 rather than 2008-10-1. If padding is not feasible, parse using DateTimeFormatter.
NavigableSet dates = new TreeSet( 3 );
dates.add( LocalDate.parse( "2008-10-01" );
dates.add( LocalDate.parse( "2008-10-02" );
dates.add( LocalDate.parse( "2008-10-04" );
LocalDate target = LocalDate.parse( "2008-10-03" );
LocalDate hit = dates.lower( target );
// Reminder: test for `null == hit` to see if anything found.
Have you looked at the JodaTime API? I seem to recall a feature like this being available.