Get Dates that match a periodicity and get number of sequence - java

I need to know if a date match a periodicity, for example, periodicity is 1 hour, and date that user gives is 13/09/2021 23:00, the inicial that my java code should take is 13/09/2021 00:00 and check how many times have to add 1 hour to get the date 13/09/2021 23:00.
The idea now is made a loop and add 1hour to the date and save in an array, then check if the date is inside the array and the position. Is there any other way?

If I understand your question correctly, you just want to calculate how many hours there are between two dates. For that, it's cleaner to use the built-in java.time classes. You can read the two dates into LocalDateTime objects and calculate the time span between them with ChronoUnit.HOURS:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
LocalDateTime start = LocalDateTime.parse("13/09/2021 00:00", formatter);
LocalDateTime end = LocalDateTime.parse("13/09/2021 23:00", formatter);
long hours = ChronoUnit.HOURS.between(start, end);
The result will be 23.
For various other units (minutes for example), there's ChronoUnit.MINUTES. Have a look at the documentation. There are a lot of different units to choose from.

I am adding a couple of minor refinements compared to the correct answer by QBrute.
The periodicity can be any amount of time in hours, minutes and seconds.
I am taking time zone into account so we also get correct results across summer time transitions (spring forward and fall back) and other time anomalies.
If there isn’t a whole number of periodicities, I am rounding up to be sure to have at least enough.
ZoneId userTimeZone = ZoneId.of("Africa/Dar_es_Salaam");
Duration periodicity = Duration.ofMinutes(5);
ZonedDateTime userTime = ZonedDateTime.of(2021, 9, 13, 23, 0, 0, 0, userTimeZone);
ZonedDateTime initialTime = ZonedDateTime.of(2021, 9, 13, 0, 0, 0, 0, userTimeZone);
Duration timeBetween = Duration.between(initialTime, userTime);
long numberOfPeriodicities = timeBetween.dividedBy(periodicity);
// Has truncation occurred?
if (initialTime.plus(periodicity.multipliedBy(numberOfPeriodicities)).isBefore(userTime)) {
// Need one more periodicity
numberOfPeriodicities++;
}
System.out.println(numberOfPeriodicities);
Output is:
276
If you want a periodicity of 7.5 minutes, specify Duration.ofMinutes(7).plusSeconds(30). The Duration.dividedBy(Duration) method that I am using was introduced in Java 9.
Link: Oracle tutorial: Date Time explaining how to use java.time.

Related

Trying to write code that will start at beginning of next day

I'm trying to write code to run at 1 am. The idea is to find the amount of time to next day in milliseconds, then do a sleep on that time
I do this by getting the reminder of the current time divided by to days time.
remainder= current time % 8640000
where current time is gotten from Time.getTimeInMillis();
I got 61175831
to get days by divide it by (606024)
witch gives me 16 hours
It's 1pm so 13+16=30 or 6am next day not 1 am in morning
delayTime=24*60*60*1000; // i day
Calendar Time = Calendar.getInstance();
long delay=TimeToNumber%delayTime;
ling days=delay/1000;
days=days/60;
days=days/60
Got 16 for days
Without knowing more about your usage scenario, I can't really comment on whether your suggested approach is the best way to do the actual scheduling. However, I can answer the question of how to determine the number of milliseconds until 1:00 AM.
ZonedDateTime now = ZonedDateTime.now();
LocalTime targetTime = LocalTime.parse("01:00");
ZonedDateTime targetDateTime;
if (now.toLocalTime().isBefore(targetTime)) {
targetDateTime = now.with(targetTime);
} else {
targetDateTime = now.plusDays(1).with(targetTime);
}
long millis = Duration.between(now, targetDateTime).toMillis();
Explanation
The Calendar API is a legacy API that comes with a bunch of challenges and gotchas with using it. Additionally, by attempting to do the calculations yourself mathematically, you're missing the various nuances that are automatically handled for you by the libraries (such as daylight saving time shifts and the like). For these reasons, I strongly suggest using the newer java.time API.
For this particular question, you need to determine the number of milliseconds until the next 1:00 AM. If the time is before 1:00 in the current date, you want to return the current date's 1:00. If it's after 1:00, you want to return tomorrow's 1:00.
ZonedDateTime is a good choice to represent 1:00 today/tomorrow, since in addition to the date & time, a time zone is needed in order to correlate the datetime with a specific instant in time.
Retrieving the next 1:00 can be achieved by comparing the current LocalTime with a LocalTime of 1:00 AM:
ZonedDateTime now = ZonedDateTime.now();
LocalTime targetTime = LocalTime.parse("01:00");
ZonedDateTime targetDateTime;
if (now.toLocalTime().isBefore(targetTime)) {
targetDateTime = now.with(targetTime);
} else {
targetDateTime = now.plusDays(1).with(targetTime);
}
This is assuming you're using the system default time zone. If you want to use a different time zone, this can be specified in the call to ZonedDateTime.now():
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
With this ZonedDateTime representing the next 1:00 AM, you can use a Duration between the current time and that time, and then get the number of milliseconds in the duration:
long millis = Duration.between(now, targetDateTime).toMillis();

Java date time subtraction [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
String date1 = "2020/05/08 16.38.37"
String date2 = "2020/04/08 20.18.10"
I wish to subtract complete date1 with complete date2 (date and time both included) and get the result in hours in java .
How to proceed ?
Knowing how many hours are between 2 given timestamps is not possible unless you tell me where on the planet you want to do the math for. For example, if you ask me the minutes between 01:30 and 03:30, the answer would seem to be 120, but if due to daylight savings that so happens to be exactly the night where the clocks are moved an hour forward, the actual correct answer'd be 60.
If you never want that kind of adjustment, UTC doesn't 'suffer' from weird adjustments like this, so you can always elect to do the math in the UTC zone.
Thus, the steps:
parse your strings (which represent 'local' date/times, given that they include no timezone info at all) into LocalDateTime objects.
Zone these 2 objects by turning them into ZonedDateTime objects at a zone of your choosing.
Now ask the API to calculate the hours, minutes, whichever one you prefer between the two.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH.mm.ss");
ZoneId zone = ZoneId.of("Europe/Amsterdam");
LocalDateTime input1 = LocalDateTime.parse("2020/05/08 16.38.37", formatter);
LocalDateTime input2 = LocalDateTime.parse("2020/04/08 20.18.10", formatter);
ZonedDateTime zoned1 = input1.atZone(zone);
ZonedDateTime zoned2 = input2.atZone(zone);
Duration duration = Duration.between(zoned1, zoned2);
long hours = duration.toHours();
System.out.println(hours);
the above would print -716 (as in, the first stamp is at least 716 hours, and less than 717 hours, before the second.... at least, if you ask someone living in Amsterdam).
NB: If you want to talk about weeks, months, etc - you want Period and not Duration.
java.time
First don’t keep your dates and times as strings in your program. Just as you don’t use strings for keeping your numbers and Boolean values (I hope), you shouldn’t for dates and times either. Use proper date and time types from java.time, the modern Java date and time API.
Assuming that your dates and times are in some well-defined time zone, I suggest using ZonedDateTime for them.
ZoneId zone = ZoneId.of("America/Montreal");
ZonedDateTime dateTime1 = ZonedDateTime.of(2020, 5, 8, 16, 38, 37, 0, zone);
ZonedDateTime dateTime2 = ZonedDateTime.of(2020, 4, 8, 20, 18, 10, 0, zone);
Finding the difference is a one-liner:
long diffHours = ChronoUnit.HOURS.between(dateTime2, dateTime1);
System.out.println("Difference in hours: " + diffHours);
Output from this example snippet is:
Difference in hours: 716
Please insert your desired time zone where I put America/Montreal. Choosing a different time zone may cause us to get an hour more or fewer if the transition to or from summer time (DST) lies between the two dates.
Parsing date-time input
If your dates and times are string input, the first thing you need to do with them is parse them. An example:
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("uuuu/MM/dd HH.mm.ss");
String string1 = "2020/05/08 16.38.37";
String string2 = "2020/04/08 20.18.10";
ZonedDateTime dateTime1 = LocalDateTime.parse(string1, inputFormatter).atZone(zone);
ZonedDateTime dateTime2 = LocalDateTime.parse(string2, inputFormatter).atZone(zone);
System.out.println("Date and time 1: " + dateTime1);
System.out.println("Date and time 2: " + dateTime2);`
Date and time 1: 2020-05-08T16:38:37-04:00[America/Montreal]
Date and time 2: 2020-04-08T20:18:10-04:00[America/Montreal]
Link
Oracle tutorial: Date Time explaining how to use java.time.

Java DST Change issue

I've found an issue about Java DST change.
When the exact moment that DST(Daylight Saving time) happened, the certain time may appears two times.
For example, for the timezone America/Sao_Paulo, it changed backward one hour from Sunday, 21 February 2016, 00:00:00 to Saturday, 20 February 2016, 23:00:00.
The time 23:00 appears twice that day.
In my use case, I'd like to take the second time. For example, when I want the data of this day, I actually mean this day completely (even if maybe it contains 25 hours).
But in my opinion, it seems that Java take the first time of this moment.
Example:
#Test
public void testDSTChange() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
TimeZone tz = TimeZone.getTimeZone("America/Sao_Paulo");
long dateInput = sdf.parse("2016-02-20T23:00:00").getTime();
long dateUTC = dateInput - tz.getOffset(dateInput);
long dateInput2 = sdf.parse("2016-02-21T02:00:00").getTime();
long dateUTC2 = dateInput2 - tz.getOffset(dateInput2);
System.out.println("Difference: " + (dateUTC2 - dateUTC) / 3600 / 1000);
}
In this example, it shows that the difference between these two dates is 4 hours, not the 3 hours that I expected. So I wonder that if there's any other way to do it ?
Well, the time at the DST change will occur twice when the DST changes backward, there is no way around it. There may be another way around, but let me make a suggestion, internally use UTC time (GMT) it has no DST internally, convert to local time (wall clock time) on output for display purposes only. You will never have to worry about it again.
In my use case, I'd like to take the second time. For example, when I want the data of this day, I actually mean this day completely (even if maybe it contains 25 hours).
From this statement, I'll assume you are actually doing some kind of range query to gather data over the entire day. The best way to approach this is to query from the start of the day inclusive, to the start of the next day exclusive. In other words: startOfDay <= dataPoint(s) < startOfNextDay
This works well in your scenario, because the clock doesn't actually hit 00:00 until the next day has started. It ticks like this:
23:58
23:59
23:00
23:01
...
23:58
23:59
00:00 <--- start of next day
So use half-open intervals, and it should just work out.
Do be careful in the spring transition though, when the start of the day is 01:00, not 00:00. :)
Also, Jon's comment about using Java 8's new time APIs, or Joda-Time are spot-on. They both offer you better control over this situation. Older Java APIs do not.
LocalDateTime l1 = LocalDateTime.of(2016, Month.FEBRUARY, 20, 23, 0, 0);
ZoneId id = ZoneId.of("America/Sao_Paulo");
ZoneOffset offset = ZoneOffset.of("-08:00");
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(l1, offset, id);
long el1= zonedDateTime.toEpochSecond();
LocalDateTime l2 = LocalDateTime.of(2016, Month.FEBRUARY, 21, 2, 0, 0);
ZonedDateTime zonedDateTime1 = ZonedDateTime.ofInstant(l2, offset, id);;
long el2= zonedDateTime1.toEpochSecond();
System.out.println("Difference: " + (el2-el1 ) / 3600 );
====================================
on console it prints
Difference: 3

Subtleties between Java Period and Duration

I'm not sure I'm getting the subtleties between Java Period and Duration.
When I read Oracle's explanation, it says that I can find out how many days since a birthday like this (using the example dates they used):
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);
Period birthdayPeriod = Period.between(birthday, today);
int daysOld = birthdayPeriod.getDays();
But as even they point out, this doesn't take into account the time zone you were born in and the time zone you are in now. But this is a computer and we can be precise, right? So would I use a Duration?
ZoneId bornIn = ZoneId.of("America/New_York");
ZonedDateTime born = ZonedDateTime.of(1960, Month.JANUARY.getValue(), 1, 2, 34, 56, 0, bornIn);
ZonedDateTime now = ZonedDateTime.now();
Duration duration = Duration.between(born, now);
long daysPassed = duration.toDays();
Now the actual times are accurate, but if I understand this correctly, the days might not correctly represent calendar days, e.g. with DST and such.
So what am I do to to get a precise answer based upon my time zone? The only thing I can think of is to go back to using LocalDate, but normalize the time zones first from the ZonedDateTime values, and then use a Duration.
ZoneId bornIn = ZoneId.of("America/New_York");
ZonedDateTime born = ZonedDateTime.of(1960, Month.JANUARY.getValue(), 1, 2, 34, 56, 0, bornIn);
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime nowNormalized=now.withZoneSameInstant(born.getZone());
Period preciseBirthdayPeriod = Period.between(born.toLocalDate(), nowNormalized.toLocalDate());
int preciseDaysOld = preciseBirthdayPeriod.getDays();
But that seems really complicated just to get a precise answer.
Your analysis regarding the Java-8-classes Period and Duration is more or less correct.
The class java.time.Period is limited to calendar date precision.
The class java.time.Duration only handles second (and nanosecond) precision but treats days always as equivalent to 24 hours = 86400 seconds.
Normally it is completely sufficient to ignore clock precision or timezones when calculating the age of a person because personal documents like passports don't document the exact time of day when someone was born. If so then the Period-class does its job (but please handle its methods like getDays() with care - see below).
But you want more precision and describe the result in terms of local fields taking into account timezones. Well, the first part (precision) is supported by Duration, but not the second part.
It is also not helpful to use Period because the exact time difference (which is ignored by Period) can impact the delta in days. And furthermore (just printing the output of your code):
Period preciseBirthdayPeriod =
Period.between(born.toLocalDate(), nowNormalized.toLocalDate());
int preciseDaysOld = preciseBirthdayPeriod.getDays();
System.out.println(preciseDaysOld); // 13
System.out.println(preciseBirthdayPeriod); // P56Y11M13D
As you can see, it is quite dangerous to use the method preciseBirthdayPeriod.getDays() in order to get the total delta in days. No, it is only a partial amount of the total delta. There are also 11 months and 56 years. I think it is wise to also print the delta not only in days because then people can easier imagine how big the delta is (see the often seen use-case of printed durations in social media like "3 years, 2 months, and 4 days").
Obviously, you rather need a way to determine a duration including calendar units as well as clock units in a special timezone (in your example: the timezone where someone has been born). The bad thing about Java-8-time-library is: It does not support any combination of Period AND Duration. And importing the external library Threeten-Extra-class Interval will also not help because long daysPassed = interval.toDuration().toDays(); will still ignore timezone effects (1 day == 24 hours) and is also not capable of printing the delta in other units like months etc.
Summary:
You have tried the Period-solution. The answer given by #swiedsw tried the Duration-based solution. Both approaches have disadvantages with respect to precision. You could try to combine both classes in a new class which implements TemporalAmount and realize the necessary time arithmetic yourself (not so trivial).
Side note:
I have myself already implemented in my time library Time4J what you look for, so it might be useful as inspiration for your own implementation. Example:
Timezone bornZone = Timezone.of(AMERICA.NEW_YORK);
Moment bornTime =
PlainTimestamp.of(1960, net.time4j.Month.JANUARY.getValue(), 1, 22, 34, 56).in(
bornZone
);
Moment currentTime = Moment.nowInSystemTime();
MomentInterval interval = MomentInterval.between(bornTime, currentTime);
MachineTime<TimeUnit> mt = interval.getSimpleDuration();
System.out.println(mt); // 1797324427.356000000s [POSIX]
net.time4j.Duration<?> duration =
interval.getNominalDuration(
bornZone, // relevant if the moments are crossing a DST-boundary
CalendarUnit.YEARS,
CalendarUnit.MONTHS,
CalendarUnit.DAYS,
ClockUnit.HOURS,
ClockUnit.MINUTES
);
// P56Y11M12DT12H52M (12 days if the birth-time-of-day is after current clock time)
// If only days were specified above then the output would be: P20801D
System.out.println(duration);
System.out.println(duration.getPartialAmount(CalendarUnit.DAYS)); // 12
This example also demonstrates my general attitude that using units like months, days, hours etc. is not really exact in strict sense. The only strictly exact approach (from a scientific point of view) would be using the machine time in decimal seconds (best in SI-seconds, also possible in Time4J after the year 1972).
The JavaDoc of Period states that it models:
A date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'.
I understand it has no reference to points in time.
You might want to check Interval from project ThreeTen-Extra which models:
an immutable interval of time between two instants.
The project website states the project “[...] is curated by the primary author of the Java 8 date and time library, Stephen Colebourne”.
You can retrieve a Duration from an Interval by invoking toDuration() on it.
I shall transform your code to give an example:
ZoneId bornIn = ZoneId.of("America/New_York");
ZonedDateTime born = ZonedDateTime.of(1960, Month.JANUARY.getValue(), 1, 2, 34, 56, 0, bornIn);
ZonedDateTime now = ZonedDateTime.now();
Interval interval = Interval.of(born.toInstant(), now.toInstant());
long daysPassed = interval.toDuration().toDays();
The main distinction between the two classes is :
that java.time.Period uses date-based values ( May 31, 2018)
while java.time.Duration is more precise, it uses time-based values ( "2018-05-31T11:45:20.223Z" )
java.time.Period is more friendly for human reading
for example Period between A and B is 2 years 3 months 3 days
java.time.Duration is for a machine.

Calculating difference in dates in Java

I find it funny that Java (or the java.util library) does not have a built-in function to calculate difference in dates. I want to subtract one date from another to get the elapsed time between them. What is the best way to do this?
I know the simple way is to take the difference of the time in milliseconds and then convert that into days. However, I wanted to know if this works in all cases (with daylight saving, etc.).
Java's not missing much, if you look at open source: try Joda-Time.
I know the simple way is to take the
difference of the time in milliseconds
and then convert that into days.
However, i wanted to know if this
works in all cases (with daylight
saving, etc.).
If your times are derived from UTC dates, or they are just the difference between two calls to System.getCurrentTimeMillis() measured on the same system, you will get a valid number of milliseconds as the difference, independent of any timezone issues. (which is why everything should be using UTC as a storage format -- it's much easier to go from UTC->local time; if you try to go the other way then you need to store the local timezone along with the local time -- or attempt to infer it, gack!)
As for turning this into a number of days, you should just be able to divide by 86400000... with the caveat that there is an occasional leap second every other year or so.
Use either Joda-Time or the new java.time package in Java 8.
Both frameworks use the Half-Open approach where the beginning is inclusive while the ending is exclusive. Sometimes notated as [). This is generally the best approach for defining spans of time.
java.time
The java.time framework built into Java 8 and later has a Period class to represent a span of time as a number of years, a number of months, and a number of days. But this class is limited to whole days, no representation of hours, minutes, and seconds.
Note that we specify a time zone, crucial for determining a date. For example, a new day dawns earlier in Paris than in Montréal.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate now = LocalDate.now( zoneId );
LocalDate then = LocalDate.of( 2001, 1, 1 );
Period period = Period.between( then, now );
Then: 2001-01-01. Now: 2015-09-07. Period: P14Y8M6D. Days: 5362
For whole days, then Daylight Saving Time (DST) is irrelevant.
If you want a count of total days, use the ChronoUnit enum which includes some calculation methods. Notice the calculations return a long.
long days = ChronoUnit.DAYS.between( then, now ); // "5362" seen above.
I have asked about doing a full period in java.time, including hours, minutes, seconds. Not possible as of Java 8. A surprising workaround using the bundled libraries was suggested by Meno Hochschild: Use a Duration class found in the javax.xml.datatype package.
Joda-Time
Here is some example code in Joda-Time 2.3.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime start = new DateTime( 2014, 1, 2, 3, 4, 5, timeZone );
DateTime stop = new DateTime( 2014, 5, 2, 3, 4, 5, timeZone );
Period period = new Period( start, stop );
Calling toString will get you a string representation in the form defined by the ISO 8601 standard, PnYnMnDTnHnMnS.
With the date4j library:
int numDaysBetween = oneDate.numDaysFrom(anotherDate);
There is simple way to implement it. We can use Calendar.add method with loop.
For example as below,
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date beginDate = dateFormat.parse("2013-11-29");
Date endDate = dateFormat.parse("2013-12-4");
Calendar beginCalendar = Calendar.getInstance();
beginCalendar.setTime(beginDate);
Calendar endCalendar = Calendar.getInstance();
endCalendar.setTime(endDate);
The minus days between beginDate and endDate, and the code as below,
int minusDays = 0;
while (true) {
minusDays++;
// Day increasing by 1
beginCalendar.add(Calendar.DAY_OF_MONTH, 1);
if (dateFormat.format(beginCalendar.getTime()).
equals(dateFormat.format(endCalendar).getTime())) {
break;
}
}
System.out.println("The substractation between two days is " + (minusDays + 1));
Have Fun! #.#
I disagree with the claim that Java doesn't have a mechanism for calculating the difference between dates.
Java was designed for global use. It was designed so that there isn't a concept of date, there is only a concept of "time in milliseconds". Any interpretation of such a universal time as the time-and-date in a specific location under a specific convention is merely a projection or a view.
The calendar class is used to turn this sort of absolute time into dates. You can also add or subtract date components, if you really need to. The only way to provide a difference in term of components between two times would be Calendar generated and specific. Thus, you could argue that the standard library does not include a smart enough Gregorian Calendar, and I would agree that it leaves some to be desired.
That being said, there are numerous implementations of this kind of functionality, I see others have provided examples.
Java's implementation of dates is poor. If you find Joda-Time too complicated, try my little contribution to open source:
http://calendardate.sourceforge.net/javadoc/index.html

Categories

Resources