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.
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.
The scenario is as follows.
You have to check if the current time is between a given start day + time and end day + time. A specific date range is not given, and the range can be in two different days.
Example:
String startDayAndTime = "SATURDAY 17:00";
String endDayAndTime = "SUNDAY 17:00";
SimpleDateFormat simpleDateFormatForTime = new SimpleDateFormat("HH:mm");
SimpleDateFormat simpleDateFormatForDay = new SimpleDateFormat("EEEE");
simpleDateFormatForTime.setTimeZone(timeZone);
simpleDateFormatForDay.setTimeZone(timeZone);
String deviceTimeString = simpleDateFormatForTime.format(date);
String deviceDayString = simpleDateFormatForDay.format(date).toUpperCase();
// TODO boolean isValidDayRange =
boolean withInRange = deviceTimeString.compareTo(startDayAndTime.split(" ")[1]) >= 0 && deviceTimeString.compareTo(endDayAndTime.split(" ")[1]) <= 0;
if (isValidDayRange && withInRange) {
//do something
}
This above code I'm working on can check for the time but not the date.
How to achieve this? TIA.
PS: Real world example: A shop offers discount for a product on every weekend from 5PM Saturday to 5PM Sunday. Trying to check the current time is eligible for that.
Requirement Update: Has to be in Java 1.7 or lesser
There is nothing in java.time to represent a day and a time (à la MonthDay) but we can quite easily define our own. I have chosen a DayOfWeek and a Duration. Make sure you validate that the time is non-negative and is less than 24 hours.
Note that DayOfWeek has a natural ordering of Monday (lowest) to Sunday (highest).
class DayTime implements Comparable<DayTime>
{
private final DayOfWeek dayOfWeek;
private final Duration time;
//...
public int compareTo(final DayTime o) { /* ... */ }
}
We can then define an event which specifies a start and end time. Make sure to validate that the start is not after the end.
class Event
{
private final DayTime start;
private final DayTime end;
//...
boolean isTimeDuringEvent(final DayTime dayTime) { /* ... */ }
}
I have left the implementation details up to you. It's your assignment.
We can use these classes like so:
final DayTime start = new DayTime(DayOfWeek.SATURDAY, Duration.ofHours(17));
final DayTime end = new DayTime(DayOfWeek.SUNDAY, Duration.ofHours(17));
final Event event = new Event(start, end);
final LocalDateTime now = LocalDateTime.now();
final DayTime test = new DayTime(
now.getDayOfWeek(),
Duration.ofNanos(now.toLocalTime().toNanoOfDay())
);
System.out.println(event.isTimeDuringEvent(test));
If you need to take user input as a string then I would advise you to work on getting the logic correct first (as above, with hardcoded values such as DayOfWeek.SATURDAY) and then when you're sure that that works, work on parsing the input. (you'll probably want DateTimeFormatter for this)
Using TemporalAccessor
You can use TemporalAccessors and a DateTimeFormatter to read the values and then build simple comparators to check your work.
String startDayAndTime = "SATURDAY 17:00";
String endDayAndTime = "SUNDAY 17:00";
DateTimeFormatter format = DateTimeFormatter.ofPattern("EEEE HH:mm");
TemporalAccessor tempNow = LocalDateTime.now();
TemporalAccessor tempStart = format.parse(startDayAndTime.toLowerCase());
TemporalAccessor tempEnd = format.parse(endDayAndTime.toLowerCase());
Comparator<TemporalAccessor> dayCompare = (a, b) -> a.get(ChronoField.DAY_OF_WEEK) - b.get(ChronoField.DAY_OF_WEEK);
Comparator<TemporalAccessor> timeCompare = (a, b) -> LocalTime.from(a).compareTo(LocalTime.from(b));
Comparator<TemporalAccessor> dateCompare = dayCompare.thenComparing(timeCompare);
if (dateCompare.compare(tempStart, tempNow) >= 0 && dateCompare.compare(tempNow, tempEnd) >= 0) {
//do something
}
You can access the date as below
LocalDateTime today = LocalDateTime.now();
LocalDate ld = LocalDate.now();
ld = ld.with(TemporalAdjusters.previous(DayOfWeek.SATURDAY));
LocalDateTime previousMonday = ld.atTime(17, 00);
LocalDate ld2 = LocalDate.now();
ld2 = ld2.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
LocalDateTime nextSunday = ld2.atTime(17, 00);
You can write your logic after that.
As above you can find previousMonday and nextSunday.
if previousMonday and nextSunday time gap less than a week, you are in the gap. Otherwise you are in out.
You can use calendar here
Calendar startDateTime=Calendar.getInstance();
startDateTime.set(Calendar.DAY_OF_WEEK,Calendar.SUNDAY);
startDateTime.set(Calendar.HOUR_OF_DAY,1);
startDateTime.set(Calendar.MINUTE,0);
startDateTime.set(Calendar.SECOND,0);
System.out.println("start Date : "+startDateTime.getTime());
Calendar endDateTime=Calendar.getInstance();
endDateTime.set(Calendar.DAY_OF_WEEK,Calendar.FRIDAY);
endDateTime.set(Calendar.HOUR_OF_DAY,17);
endDateTime.set(Calendar.MINUTE,0);
endDateTime.set(Calendar.SECOND,0);
System.out.println("end Date : "+endDateTime.getTime());
Calendar today = Calendar.getInstance();
if(today.after(startDateTime) && today.before(endDateTime))
{
System.out.println("Yes");
}
But here you have need to maintain order of day because calendar WEEKS start with SUNDAY and ends with SATURDAY. So you have to follow this Order. Smaller is startdate and bigger is end date.
Order is like this
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Sunday.
*/
public final static int SUNDAY = 1;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Monday.
*/
public final static int MONDAY = 2;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Tuesday.
*/
public final static int TUESDAY = 3;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Wednesday.
*/
public final static int WEDNESDAY = 4;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Thursday.
*/
public final static int THURSDAY = 5;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Friday.
*/
public final static int FRIDAY = 6;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Saturday.
*/
public final static int SATURDAY = 7;
But I think it is enough to serve your requirements.
thanks for the answers and the conversations. I managed to solve the issue from both TemporalAccessor and Calendar options given. Later a new requirement was given as to the util class I was working on has to be written in Java 1.7 or lesser since it was to be a library for an android app which didn't support Java 1.8. Hence the answer by #flopcoder was accepted, on the grounds where Sunday was taken as the beginning of the week and the logic was only to be applied on ranges within one week only.
You have written the code to check only day and time, not written the code to check date use simpleDateFormat and send the system current date to check and compare, you are passing "HH: mm" Which will give you system current time and "EEEE" Will give you day, .... So u need to pass "dd" to get current system date only and then compare as per you requirement
I want to get the difference of current time (Which is IST) and the time which is stored in DB(EST). In order to that I am trying to convert current time to EST before calculating the difference. But its not working. In the following approach,
local time is not getting converted to EST only. Could you please suggest me the better way to do it ?
The return type of getModifiedDate is java.sql.Timestamp and the data type
of the column is DATE
Code :
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("EST"));
cal.setTimeInMillis(System.currentTimeMillis());
cal.getTimeInMillis() - emp.getModifiedDate().getTime();
I was trying to do it using SimpleDateFormat , But I am not sure how to proceed with that approach.
If you can provide the code snippet that will be helpful
You can try java.util.TimeZone
long now = System.currentTimeMillis();
long diff = TimeZone.getTimeZone("IST").getOffset(now) - TimeZone.getTimeZone("EST").getOffset(now);
getOffset - Returns the offset of this time zone from UTC at the specified date
If you have access to Java 8, then it may be just as easy to calculate the difference between the two dates directly, rather than adjusting to a target time zone first.
You could do this using ZonedDateTime from the java.time package:
// Our timestamp
Timestamp ts = emp.getModifiedDate();
// Convert timestamp to ZonedDateTime in the correct time zone
ZonedDateTime estTime = ts.toLocalDateTime().atZone(ZoneId.of("America/New_York"));
// Could pass in ZoneId.of("Asia/Kolkata") argument to now(...), but not required
// as "now" is the same instant in all time zones.
ZonedDateTime zonedNow = ZonedDateTime.now();
// Can use other ChronoUnit values if required.
long timeDiff = ChronoUnit.MILLIS.between(zonedNow, estTime);
// Use timeDiff as required
I suggest using the java.time API of JDK 8 which simplifies this to a great extent. Consider the following example:
Timestamp t = emp.getModifiedDate();
Duration.between(t.toInstant(), ZonedDateTime.now(ZoneId.of("Asia/Kolkata")).toInstant());
The timestamp retrieved from DB has been converted to Instant which is in UTC, similarly the current time in Asia/Kolkata zone has been converted to Instant and the Duration between the two has been calculated.You can retrieve the required information from the duration.
You can find it using java.time.Duration which is modelled on ISO-8601 standards and was introduced with Java-8 as part of JSR-310 implementation. With Java-9 some more convenience methods were introduced.
import java.time.Duration;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(formatDuration(diffBetweenTimeZones("Asia/Kolkata", "America/New_York")));
System.out.println(formatDuration(diffBetweenTimeZones("America/New_York", "Asia/Kolkata")));
// You can use the returned value to get the ZoneOffset which you can use for
// other processing e.g.
ZoneOffset offset = ZoneOffset.of(formatDuration(diffBetweenTimeZones("Asia/Kolkata", "America/New_York")));
System.out.println(offset);
System.out.println(OffsetDateTime.now(offset));
}
static Duration diffBetweenTimeZones(String tz1, String tz2) {
LocalDate today = LocalDate.now();
return Duration.between(today.atStartOfDay(ZoneId.of(tz1)), today.atStartOfDay(ZoneId.of(tz2)));
}
static String formatDuration(Duration duration) {
long hours = duration.toHours();
long minutes = duration.toMinutes() % 60;
String symbol = hours < 0 || minutes < 0 ? "-" : "+";
return String.format(symbol + "%02d:%02d", Math.abs(hours), Math.abs(minutes));
// ####################################Java-9####################################
// return String.format(symbol + "%02d:%02d", Math.abs(duration.toHoursPart()),
// Math.abs(duration.toMinutesPart()));
// ####################################Java-9####################################
}
}
Output:
+09:30
-09:30
+09:30
2021-03-24T19:52:29.474858+09:30
Learn more about the modern date-time API from Trail: Date Time.
Note that the java.util date-time API is outdated and error-prone. It is recommended to stop using it completely and switch to the modern date-time API*.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
TimeZone has two methods getRawOffet and getOffset that retrieves the offset of the time zone to UTC in milliseconds. The second one is adjusted for Daylight Saving Time and request the date to check if it is in effect.
TimeZone current = TimeZone.getDefault();
TimeZone db = TimeZone.getTimeZone("US/Eastern"); // or "EST5EDT", or "America/New_York"
System.out.printf("DB: %s Current: %s\n", db, current);
System.out.printf("Raw: %.1f h\n", (db.getRawOffset() - current.getRawOffset())/3_600_000D);
final long now = System.currentTimeMillis();
System.out.printf("DST: %.1f h\n", (db.getOffset(now) - current.getOffset(now))/3_600_000D);
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()));
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();
}