I assumed this would have already been asked, but I couldn't find anything.
Using java.time what is the best way to determine if a given ZonedDateTime is "today"?
I've come up with at least two possible solutions. I'm not sure if there are any loopholes or pitfalls with these approaches. Basically the idea is to let java.time figure it out and not do any math myself:
/**
* #param zonedDateTime a zoned date time to compare with "now".
* #return true if zonedDateTime is "today".
* Where today is defined as year, month, and day of month being equal.
*/
public static boolean isZonedDateTimeToday1(ZonedDateTime zonedDateTime) {
ZonedDateTime now = ZonedDateTime.now();
return now.getYear() == zonedDateTime.getYear()
&& now.getMonth() == zonedDateTime.getMonth()
&& now.getDayOfMonth() == zonedDateTime.getDayOfMonth();
}
/**
* #param zonedDateTime a zoned date time to compare with "now".
* #return true if zonedDateTime is "today".
* Where today is defined as atStartOfDay() being equal.
*/
public static boolean isZoneDateTimeToday2(ZonedDateTime zonedDateTime) {
ZonedDateTime now = ZonedDateTime.now();
LocalDateTime atStartOfToday = now.toLocalDate().atStartOfDay();
LocalDateTime atStartOfDay = zonedDateTime.toLocalDate().atStartOfDay();
return atStartOfDay == atStartOfToday;
}
If you mean today in the default time zone:
return zonedDateTime.toLocalDate().equals(LocalDate.now());
//you may want to clarify your intent by explicitly setting the time zone:
return zonedDateTime.toLocalDate().equals(LocalDate.now(ZoneId.systemDefault()));
If you mean today in the same timezone as the ZonedDateTime:
return zonedDateTime.toLocalDate().equals(LocalDate.now(zonedDateTime.getZone()));
Related
I have a method wherein have to check whether a LocalDate falls in between two java.util.Date values.
there are methods after and before in java.util.Date
and there are methods isAfter and isBefore in LocalDate.
The code snippet which i have is as :
/**
* checks if date passed falls between start & end date
*
* #param date
* #param startDate
* #param endDate
* #return
*/
public static boolean isBetween(Date date, Date startDate, Date endDate) {
return (startDate == null || date.after(startDate) || date.equals(startDate))
&& (endDate == null || date.before(endDate) || date.equals(endDate));
}
There is no method in the API to compare across..
You need to decide on many corner and edge cases. Here’s a shot.
public static boolean isBetween(LocalDate date, Date startDate, Date endDate) {
ZoneId zone = ZoneId.systemDefault();
// Is before start?
if (startDate != null) {
LocalDate startLocalDate = startDate.toInstant().atZone(zone).toLocalDate();
if (date.isBefore(startLocalDate)) {
return false;
}
}
// Is after end?
if (endDate != null) {
LocalDate endLocalDate = endDate.toInstant().atZone(zone).toLocalDate();
if (date.isAfter(endLocalDate)) {
return false;
}
}
// If we end up here, the date is between start and end inclusive
return true;
}
I am assuming that the old-fashioned Date objects are to be interpreted in the default time zone of the JVM. On one hand this is standard, on the other hand the default time zone can be changed at any time, also from other programs running in the same JVM, so this is fragile. I am discarding the time of day part of the thus interpreted date. Whether the time is 00:00 or it’s 23:59:59.999, I deem the LocalDate inside the interval if the date agrees. You may want quite different behaviour.
If you wanted to take the time into account, you should probably convert everything to Instant or ZonedDateTime instead.
As you can see, mixing old-fashioned and modern classes leads to quite some complication. I am converting Date to LocalDate in order to take advantage of java.time, which in turn also gets more complicated since the Dates may be null.
Convert LocalDate to String and convert this String to java.util.Date and you can use your method.
To convert LocalDate to String:
LocalDate localDate = LocalDate.now();
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String localDateToString = localDate.format(dateFormatter1);
And to convert String to Date:
SimpleDateFormat formatter=new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
Date date1=formatter1.parse(sDate1);
Once you've converted LocalDate to Date, you can compare dates using your method.
You will have to convert java.util.Date to LocalDate instances for comparison. Since you have to compare LocalDate with other dates, so losing the time part seems to be a logical compromise here.
public static boolean isBetween(LocalDate localDate, Date start, Date end){
LocalDate startLocalDate = Instant.ofEpochMilli(start.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate endLocalDate = Instant.ofEpochMilli(end.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
return (localDate.isEqual(startLocalDate) || localDate.isEqual(endLocalDate)) || (localDate.isAfter(startLocalDate) && localDate.isBefore(endLocalDate));
}
I have made the endDate as inclusive upper limit.
This question already has answers here:
How to check if a date is greater than another in Java? [duplicate]
(4 answers)
Closed 2 years ago.
My date time formatter is , "yyyy-MM-DD"
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-DD")
I want to check the date is later than today's date or not. I checked it using this validation.
if(dateFormatter.format(given_Date).compareTo(dateFormatter.format(new Date())) > 0){ ...}
But every time this returns false when the given date is later or not. Can anyone help with this me, please?
tl;dr
You asked:
check the date is later than today's date
LocalDate // Represent a date-only value, without time-of-day and without time zone or offset-from-UTC.
.parse( "2020-01-23" ) // No need to specify formatting pattern when parsing a string in standard ISO 8601 format. Returns a `LocalDate` object.
.isAfter // Compare one `LocalDate` object to another.
(
LocalDate.now // Capture the current date as seen in a particular time zone.
(
ZoneId.of( "Africa/Tunis" ) // or: ZoneId.systemDefault()
) // Returns a `LocalDate` object.
) // Returns `boolean`.
Details
Modern solution uses java.time classes, specifically java.time.LocalDate. Compare with isAfter method. You are using terrible date-time classes that were years ago supplanted by java.time.
No need to specify a formatting pattern. Your input strings comply with the ISO 8601 standard used by default in java.time.
By the way, formatting codes are case-sensitive, and day-of-month is dd rather than the DD you used. So the formatting pattern used here by default is akin to uuuu-MM-dd.
boolean isFuture = LocalDate.parse( "2020-01-23" ).isAfter( LocalDate.now() ) ;
Better to explicitly specify desired/expected time by which to determine today’s date. If omitted, the JVM’s current default time zone is implicitly applied.
boolean isFuture = LocalDate.parse( "2020-01-23" ).isAfter( LocalDate.now( ZoneId.of( "America/Edmonton" ) ) ) ;
If you can work with the variable givenDate as a String there is another way. Check out my code:
import java.util.Calendar;
public class compareDates {
public static void main(String[] args){
Calendar c = Calendar.getInstance();
String givenDate = "2021-01-10";
boolean later = false;
int yr_now = c.get(Calendar.YEAR);
int m_now = c.get(Calendar.MONTH) + 1;
int day_now = c.get(Calendar.DAY_OF_MONTH);
int given_yr = Integer.parseInt(givenDate.substring(0,4));
int given_m = Integer.parseInt(givenDate.substring(5,7));
int given_day = Integer.parseInt(givenDate.substring(8,10));
//is "given date" later than today's date?
if(yr_now > given_yr) {
System.out.print(later);
}
else if (yr_now == given_yr && m_now > given_m){
System.out.print(later);
}
else if (m_now == given_m && day_now >= given_day){
System.out.print(later);
}
else {later = true; System.out.print(later);}
}}
I have a cronjob string saved in a mongo document field. I get the next valid (long)time by
CronExpression exp = new CronExpression(billing.getReminder());
long nextReminder = exp.getNextValidTimeAfter(new Date()).getTime();
My idea is to check if this "nextReminder" isToday() then create some task.
What is the best way to check it with java 11?
You could use java.time for a comparison...
There is an Instant representing a moment in time like a timestamp in epoch milliseconds does (⇒ your long nextReminder) as well as OffsetDateTime.now() for the actual moment now and LocalDate as the part describing the date-part only.
You could find out if the nextReminder is today by using a method like this:
/**
* <p>
* Checks if the day (or date) of a given timestamp (in epoch milliseconds)
* is the same as <em>today</em> (the day this method is executed).<br>
* <strong>Requires an offset in order to have a common base for comparison</strong>
* </p>
*
* #param epochMillis the timestamp in epoch milliseconds to be checked
* #param zoneOffset the offset to be used as base of the comparison
* #return <code>true</code> if the dates of the parameter and today are equal,
* otherwise <code>false</code>
*/
public static boolean isToday(long epochMillis, ZoneOffset zoneOffset) {
// extract the date part from the parameter with respect to the given offset
LocalDate datePassed = Instant.ofEpochMilli(epochMillis)
.atOffset(zoneOffset)
.toLocalDate();
// then extract the date part of "now" with respect to the given offset
LocalDate today = Instant.now()
.atOffset(zoneOffset)
.toLocalDate();
// then return the result of an equality check
return datePassed.equals(today);
}
and then just call it like
boolean isNextReminderToday = isToday(nextReminder, ZoneOffset.systemDefault());
which would use the time offset of the system. Maybe, ZoneOffset.UTC could be a smart choice, too.
The answer by deHaar is correct. However, I felt to write this one because in this case, using the Zone ID (instead of Zone Offset) makes the code a bit simpler and also easier to understand.
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
// A test data
long nextReminder = 1597754387710L;
// Your time-zone e.g. Europe/London
ZoneId zoneId = ZoneId.of("Europe/London");
// Next reminder date
Instant instant = Instant.ofEpochMilli(nextReminder);
LocalDate nextReminderDate = instant.atZone(zoneId).toLocalDate();
// Today at the time-zone of Europe/London
LocalDate today = LocalDate.now(zoneId);
if (today.equals(nextReminderDate)) {
System.out.println("The next reminder day is today");
}
}
}
Output:
The next reminder day is today
Using apache commons DateUtils.isToday(nextReminder)
Using your own method.
private static final long MILLIS_PER_DAY = 86400000;
public static boolean isToday(long timestamp) {
long now = System.currentTimeMillis();
long today = now.getTime() / MILLIS_PER_DAY;
long expectedDay = timestamp / MILLIS_PER_DAY;
return today == expectedDay;
}
Note: Consider using UTC when working with date/time.
Is there a good way to get the date of the coming Wednesday?
That is, if today is Tuesday, I want to get the date of Wednesday in this week; if today is Wednesday, I want to get the date of next Wednesday; if today is Thursday, I want to get the date of Wednesday in the following week.
Thanks.
The basic algorithm is the following:
Get the current date
Get its day of week
Find its difference with Wednesday
If the difference is not positive, add 7 (i.e. insist on next coming/future date)
Add the difference
Here's a snippet to show how to do this with java.util.Calendar:
import java.util.Calendar;
public class NextWednesday {
public static Calendar nextDayOfWeek(int dow) {
Calendar date = Calendar.getInstance();
int diff = dow - date.get(Calendar.DAY_OF_WEEK);
if (diff <= 0) {
diff += 7;
}
date.add(Calendar.DAY_OF_MONTH, diff);
return date;
}
public static void main(String[] args) {
System.out.printf(
"%ta, %<tb %<te, %<tY",
nextDayOfWeek(Calendar.WEDNESDAY)
);
}
}
Relative to my here and now, the output of the above snippet is "Wed, Aug 18, 2010".
API links
java.util.Calendar
java.util.Formatter - for the formatting string syntax
tl;dr
LocalDate // Represent a date-only value, without time-of-day and without time zone.
.now() // Capture the current date as seen in the wall-clock time used by the people of a specific region (a time zone). The JVM’s current default time zone is used here. Better to specify explicitly your desired/expected time zone by passing a `ZoneId` argument. Returns a `LocalDate` object.
.with( // Generate a new `LocalDate` object based on values of the original but with some adjustment.
TemporalAdjusters // A class that provides some handy pre-defined implementations of `TemporalAdjuster` (note the singular) interface.
.next( // An implementation of `TemporalAdjuster` that jumps to another date on the specified day-of-week.
DayOfWeek.WEDNESDAY // Pass one of the seven predefined enum objects, Monday-Sunday.
) // Returns an object implementing `TemporalAdjuster` interface.
) // Returns a `LocalDate` object.
Details
Using Java8 Date time API you can easily find the coming Wednesday.
LocalDate nextWed = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));
next(DayOfWeek dayOfWeek) - Returns the next day-of-week adjuster, which adjusts the date
to the first occurrence of the specified day-of-week after the date
being adjusted.
Suppose If you want to get previous Wednesday then,
LocalDate prevWed = LocalDate.now().with(TemporalAdjusters.previous(DayOfWeek.WEDNESDAY));
previous(DayOfWeek dayOfWeek) - Returns the previous day-of-week adjuster, which adjusts
the date to the first occurrence of the specified day-of-week before
the date being adjusted.
Suppose If you want to get next or current Wednesday then
LocalDate nextOrSameWed = LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY));
nextOrSame(DayOfWeek dayOfWeek) - Returns the next-or-same day-of-week
adjuster, which adjusts the date to the first occurrence of the
specified day-of-week after the date being adjusted unless it is
already on that day in which case the same object is returned.
Edit:
You can also pass ZoneId to get the current date from the system clock in the specified time-zone.
ZoneId zoneId = ZoneId.of("UTC");
LocalDate nextWed = LocalDate.now(zoneId).with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));
For more information refer TemporalAdjusters
Using JodaTime:
LocalDate date = new LocalDate(System.currentTimeMillis());
Period period = Period.fieldDifference(date, date.withDayOfWeek(DateTimeConstants.WEDNESDAY));
int days = period.getDays();
if (days < 1) {
days = days + 7;
}
System.out.println(date.plusDays(days));
Calendar c= Calendar.getInstance();
c.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
c.add(Calendar.DAY_OF_MONTH, 7);
c.getTime();
Use java.util.Calendar. You get the current date/time like this:
Calendar date = Calendar.getInstance();
From there, get date.get(Calendar.DAY_OF_WEEK) to get the current day of week and get the difference to Calendar.WEDNESDAY and add it.
public static void nextWednesday() throws ParseException
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
int weekday = calendar.get(Calendar.DAY_OF_WEEK);
int days = Calendar.WEDNESDAY - weekday;
if (days < 0)
{
days += 7;
}
calendar.add(Calendar.DAY_OF_YEAR, days);
System.out.println(sdf.format(calendar.getTime()));
}
I'm trying to write a simple utility method for adding aninteger number of days to a Joda time instant. Here is my first stab.
/**
* Adds a number of days specified to the instant in time specified.
*
* #param instant - the date to be added to
* #param numberOfDaysToAdd - the number of days to be added to the instant specified
* #return an instant that has been incremented by the number of days specified
*/
public static Instant addNumberOfDaysToInstant(final Instant instant, final int numberOfDaysToAdd) {
Days days = Days.days(numberOfDaysToAdd);
Interval interval = new Interval(instant, days);
return interval.getEnd().toInstant();
}
This works fine for the most part except when you consider the example when the number of days added takes you across the BST / GMT boundary. Here is a small example.
public class DateAddTest {
/**
* Zone to use for input and output
*/
private static final DateTimeZone ZONE = DateTimeZone.forId("Europe/London");
/**
* Formatter used to translate Instant objects to & from strings.
*/
private static final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern(DATE_FORMAT).withZone(ZONE);
/**
* Date format to be used
*/
private static final String DATE_FORMAT = "dd/MM/yyyy";
public static void main(String[] args) {
DateTime dateTime = FORMATTER.parseDateTime("24/10/2009");
Instant toAdd = dateTime.toInstant();
Instant answer = JodaTimeUtils.addNumberOfDaysToInstant(toAdd, 2);
System.out.println(answer.toString(FORMATTER)); //25/10/2009
}
}
I think this problem is because the interval does not take into acount the fact that it has crossing the bst boundary. Any ideas of a better way to implement this would be appreciated.
If you want to deal with dates, don't use instants. I suspect it's correctly adding 48 hours to the instant.
Use a LocalDate instead, and then the plusDays method.
If you want to know the instant that occurs n days after the specified instant, at the same time of day, we could no doubt work out a way of doing that (split the instant into a LocalDate and a LocalTime, advance the LocalDate and then reassemble, or check whether LocalDateTime does what you want) but you need to work out what you want to happen if the original time occurs twice on the new day, or doesn't occur at all.
EDIT: Okay, so you need to work with an instant. Does that have to be in an original time zone? Could you use UTC? That would take away the DST issues. If not, what do you want it to do in cases of ambiguity or non-existence (e.g. at 12.30am before each of the transitions).
Assuming the rest of your code:
public static void main(String[] args) {
DateTime dateTime = FORMATTER.parseDateTime("24/10/2009");
Instant pInstant = dateTime.withFieldAdded(DurationFieldType.days(),2).toInstant();
System.out.println("24/10/2009 + 2 Days = " + pInstant.toString(FORMATTER));
}
This is the solution that was chosen.
/**
* Zone to use for input and output
*/
private static final DateTimeZone ZONE = DateTimeZone.forId("Europe/London");
/**
* Adds a number of days specified to the instant in time specified.
*
* #param instant - the date to be added to
* #param numberOfDaysToAdd - the number of days to be added to the instant specified
* #return an instant that has been incremented by the number of days specified
*/
public static Instant addNumberOfDaysToInstant(final Instant instant, final int numberOfDaysToAdd) {
return instant.toDateTime(ZONE).withFieldAdded(DurationFieldType.days(), numberOfDaysToAdd).toInstant();
}