Adding a number of days to a JodaTime Instant - java

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();
}

Related

What is the best solution to get and compare hours + minutes from LocalDateTime with given hour?

I am implementing a service (not going to production anywhere anytime) which should receive a LocalDateTime and a Duration and should check if given time is between company working hours (which are 8:00-22:00), the working hours should be (somehow) configurable:
lets say that I have a:
public class CompanyWorkingHoursService {
private static final Int OPENING_HOUR = 8;
private static final Int CLOSING_HOUR = 22;
private boolean isMeetingBetweenWorkingHours(LocalDateTime beginningDateTime, Duration duration) {
LocalDateTime endingDateTime = beginningDateTime.plus(duration);
}
and I'm stuck.
I can change the type of OPENING_HOUR and CLOSING_HOUR to whatever I want. I can get hours and minutes from LocalDateTime but those are integers. And I don't want to compare whole dates - i need just hours and minutes.
I have found some solutions using java.util.Date but I would like to stay with LocalDateTime if possible...
The "best" thing is to avoid integers. So define the opening and closing hours as LocalTime, and compare the dates using the isAfter(), isBefore() and equals() provided by LocalTime:
private static final LocalTime OPENING_HOUR = LocalTime.of(8, 0);
private static final LocalTime CLOSING_HOUR = LocalTime.of(22, 0);
private boolean isMeetingBetweenWorkingHours(LocalDateTime beginningDateTime, Duration duration) {
LocalDateTime endingDateTime = beginningDateTime.plus(duration);
return !beginningDateTime.toLocalTime().isBefore(OPENING_HOUR)
&& !endingDateTime.toLocalTime().isAfter(CLOSING_HOUR));
}
If the working hours should be (somehow) configurable, you could pass them to the method, too. Afterwards create LocalDateTime instances from those values in combination with the date of the meeting.
Maybe like this:
public static boolean isMeetingBetweenWorkingHours(
LocalDateTime startMeeting, Duration meetingDuration,
int openFrom, int openUntil) { // pass start and end hour of day
/*
* create the working time hours using the hours of day passed
* and using the date of the meeting start passed
*/
LocalDateTime startWorkingHours = LocalDateTime.of(startMeeting.toLocalDate(),
LocalTime.of(openFrom, 0));
LocalDateTime endWorkingHours = LocalDateTime.of(startMeeting.toLocalDate(),
LocalTime.of(openUntil, 0));
// calculate the end time of the meeting
LocalDateTime endMeeting = startMeeting.plus(meetingDuration);
// then return if the meeting fully fits into the working time slot
return !startMeeting.isBefore(startWorkingHours)
&& !endMeeting.isAfter(endWorkingHours);
}

Java 11 time - is (long)timestamp today

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.

Check if current time is in between given time and day names in Java

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

How to determine if ZonedDateTime is "today"?

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()));

How to get first seconds of the next day based on current seconds?

I have to convert seconds in UTC into day then add interval of one day and return seconds in UTC.
Here is what I have:
option #1
public static final long nextDayStartSec(long epochSecondsInUTC) {
return (epochSecondsInUTC / TimeUnit.DAYS.toSeconds(1) + 1) * TimeUnit.DAYS.toSeconds(1);
}
But not all days contain 86400 seconds according to Wikipedia:
Modern Unix time is based on UTC, which counts time using SI seconds,
and breaks up the span of time into days almost always 86400 seconds
long, but due to leap seconds occasionally 86401 seconds.
option #2
public static final long nextDayStartSec(long epochSecondsInUTC) {
return DateUtils.addMilliseconds(DateUtils.round(new Date(TimeUnit.SECONDS.toMillis(epochSecondsInUTC)), Calendar.DATE), -1)
.toInstant().atZone(systemDefault()).toLocalDateTime().toEpochSecond(ZoneOffset.UTC);
}
But it uses wide range of libraries (including Apache Commons) and hard to read.
Is there something simple that I missed?
If you use Java 8, the new time API allows you to write it this way (it adds one day to the given instant):
public static final long nextDayStartSec(long epochSecondsInUTC) {
OffsetDateTime odt = Instant.ofEpochSecond(epochSecondsInUTC).atOffset(ZoneOffset.UTC);
return odt.plusDays(1).toEpochSecond();
}
If you want to get the instant of the start of the next day, it could look like this:
public static final long nextDayStartSec(long epochSecondsInUTC) {
OffsetDateTime odt = Instant.ofEpochSecond(epochSecondsInUTC).atOffset(ZoneOffset.UTC);
return odt.toLocalDate().plusDays(1).atStartOfDay(ZoneOffset.UTC).toEpochSecond();
}

Categories

Resources