I've implemented a stopwatch that works fine without considering that bank holidays and weekends shouldn't be counted in the total duration. I was looking for some open-source library where I could get the elapsed time, passing a start instant, end instant and a set of bank holidays (weekends aren't counted in). The only library that makes me things easier is net.sf.jtemporal, but I have still to amplify the functionality.
Could anyone tell me if there is some useful library to get the wanted functionality?
As I have mentioned there, probably the best and easiest approach is to create a table containing information about each day (work day count from beginning / bank holiday, etc; one row per day = 365 rows per year) and then just use count function / with proper selection.
I doubt you can find something that specific. But it's easy enough to create your own logic. Here's some pseudocode...
private long CalculateTimeSpan(DateTime BeginDate, DateTime EndDate, ArrayList<DateTime> BankHollidays)
{
long ticks = 0;
while (BeginDate <= EndDate) // iterate until reaching end
{
if ((BeginDate is holliday?) || (BeginDate is Weekend?))
skip;
else
ticks += (24*60*60*1000);
BeginDate = BeginDate + 1 day; // add one day and iterate
}
return ticks;
}
Do you only count Bank Hours too? 9AM - 3PM? Or is it 24 hours a day?
You should take a look at Joda Time. It is a much better date/time API than the one included with Java
I think this would be a valid solution to what your are looking for. It calculates the elapsed time (considering that one working day has 24 hours) without count the bank holidays and weekends in:
/**
* Calculate elapsed time in milliseconds
*
* #param startTime
* #param endTime
* #return elapsed time in milliseconds
*/
protected long calculateElapsedTimeAux(long startTime, long endTime) {
CustomizedGregorianCalendar calStartTime = new CustomizedGregorianCalendar(this.getTimeZone());
CustomizedGregorianCalendar calEndTime = new CustomizedGregorianCalendar(this.getTimeZone());
calStartTime.setTimeInMillis(startTime);
calEndTime.setTimeInMillis(endTime);
long ticks = 0;
while (calStartTime.before(calEndTime)) { // iterate until reaching end
ticks = ticks + increaseElapsedTime(calStartTime, calEndTime);
}
return ticks;
}
private long increaseElapsedTime(CustomizedGregorianCalendar calStartTime, CustomizedGregorianCalendar calEndTime) {
long interval;
long ticks = 0;
interval = HOURS_PER_DAY*MINUTES_PER_HOUR*SECONDS_PER_MIN*MILLISECONDS_PER_SEC; // Interval of one day
if ( calEndTime.getTimeInMillis() - calStartTime.getTimeInMillis() < interval) {
interval = calEndTime.getTimeInMillis() - calStartTime.getTimeInMillis();
}
ticks = increaseElapsedTimeAux(calStartTime, calEndTime, interval);
calStartTime.setTimeInMillis(calStartTime.getTimeInMillis() + interval);
return ticks;
}
protected long increaseElapsedTimeAux(CustomizedGregorianCalendar calStartTime, CustomizedGregorianCalendar calEndTime, long interval) {
long ticks = 0;
CustomizedGregorianCalendar calNextStartTime = new CustomizedGregorianCalendar(this.getTimeZone());
calNextStartTime.setTimeInMillis(calStartTime.getTimeInMillis() + interval);
if ( (calStartTime.isWorkingDay(_nonWorkingDays) && calNextStartTime.isWorkingDay(_nonWorkingDays)) ) { // calStartTime and calNextStartTime are working days
ticks = interval;
}
else {
if (calStartTime.isWorkingDay(_nonWorkingDays)) { // calStartTime is a working day and calNextStartTime is a non-working day
ticks = (calStartTime.getNextDay().getTimeInMillis() - calStartTime.getTimeInMillis());
}
else {
if (calNextStartTime.isWorkingDay(_nonWorkingDays)) { // calStartTime is a non-working day and calNextStartTime is a working day
ticks = (calNextStartTime.getTimeInMillis() - calStartTime.getNextDay().getTimeInMillis());
}
else {} // calStartTime and calEndTime are non-working days
}
}
return ticks;
}
Related
i have one function
callEach24hourOneTime();
i have to call this function in each 24 hour only one time within 24 once it will executed then i don't have to call callEach24hourOneTime
i am unable to get current time hour minute and second in millis so that i can apply condition i tried below logic but unable to execute.
if(currentmillis=86400000 ){
callEach24hourOneTime();
}
else {
//dont call do other operation
}
please suggest me solution for this .
Logic to always have at least 24 hours between executions of the callEach24hourOneTime() method:
private long nextCallMillis;
long currentMillis = System.currentTimeMillis();
if (currentMillis >= this.nextCallMillis) {
this.nextCallMillis = currentMillis + 86400000/*24 hours*/;
callEach24hourOneTime();
} else {
// ...
}
That will cause a drift in time-of-day for the execution of the method. The speed of the drift is determined by how often the code is execute.
If you instead want method to be called at (around) the same time every day:
private long nextCallMillis;
long currentMillis = System.currentTimeMillis();
if (this.nextCallMillis == 0)
this.nextCallMillis = currentMillis; // Establish time-of-day
if (currentMillis >= this.nextCallMillis) {
this.nextCallMillis += ((currentMillis - this.nextCallMillis) / 86400000 + 1) * 86400000;
callEach24hourOneTime();
} else {
// ...
}
"Same time every day" is in UTC, so when crossing Daylight Savings Time changes, the time-of-day will change by 1 hour.
UPDATE
If you just want "once per day" logic:
private LocalDate lastCallDate;
LocalDate today = LocalDate.now();
if (! today.equals(lastCallDate)) {
this.lastCallDate = today;
callOnceDailyOneTime();
} else {
// ...
}
You can you a cron library.
I don't really know what language or framework you are currently using, but from spring I'd say you can use #Scheduled :
#Scheduled(cron = "0 0 0 * * *")
private void myMethod(){}
For exemple the cron above will make myMethod() execute once a day.
I'm trying to calculate how many minutes are between two dates while excluding periods of time that are arbitrarily defined and occur weekly. I also need to be able to calculate the reverse, where given a time, calculate X number of minutes forward excluding those time periods.
For example, I may have two periods [Fri 5:31pm - Sat 2:26pm] and [Tuesday 3:37am - Thursday 1:14am] that I don't want to count when figuring out the minutes between two dates and when calculating forward.
I currently have code that does this for only one gap, though it's not super efficient and is becoming a strain on my system. I also need to accommodate multiple defined gaps which I currently do not do.
My code which does this for one gap looks like this (hideStart and hideEnter are the start and end DateTime for the gap, absoluteLowValue is the starting time from which I am calculating the distance or time between):
public int absoluteDistance(DateTime high){
long totalMinutes = new Duration(absoluteLowValue,high).getStandardMinutes();
if (!gapHider.isHidingGaps())
return (int)totalMinutes;
int minutesPerWeek = 10080;
long minutesPerHide = new Duration(hideStart, hideEnd).getStandardMinutes();
long numFullWeeks = totalMinutes/minutesPerWeek;
long remainder = totalMinutes%minutesPerWeek;
totalMinutes -= numFullWeeks*minutesPerHide;
DateTime latestEnd = high;
if (latestEnd.getDayOfWeek() == hideEnd.getDayOfWeek() && latestEnd.getSecondOfDay() < hideEnd.getSecondOfDay()){
latestEnd = latestEnd.minusWeeks(1);
}
while (latestEnd.getDayOfWeek() != hideEnd.getDayOfWeek())
latestEnd = latestEnd.minusDays(1);
latestEnd = latestEnd.withTime(hideEnd.getHourOfDay(),
hideEnd.getMinuteOfHour(),
hideEnd.getSecondOfMinute(),
hideEnd.getMillisOfSecond());
DateTime latestStart = high;
if (latestStart.getDayOfWeek() == hideStart.getDayOfWeek() && latestStart.getSecondOfDay() < hideStart.getSecondOfDay()){
latestStart = latestStart.minusWeeks(1);
}
while (latestStart.getDayOfWeek() != hideStart.getDayOfWeek())
latestStart = latestStart.minusDays(1);
latestStart = latestStart.withTime(hideStart.getHourOfDay(),
hideStart.getMinuteOfHour(),
hideStart.getSecondOfMinute(),
hideStart.getMillisOfSecond());
long timeToNearestEnd = new Duration(latestEnd, high).getStandardMinutes();
long timeToNearestStart = new Duration(latestStart, high).getStandardMinutes();
if (timeToNearestEnd < remainder){
totalMinutes -= minutesPerHide;
}else if (timeToNearestStart < remainder){
totalMinutes -= new Duration(latestStart, high).getStandardMinutes();
}
return (int)totalMinutes;
}
public DateTime timeSinceAbsLow(int index){
if (absoluteLowValue != null){
if (!gapHider.isHidingGaps())
return absoluteLowValue.plusMinutes(index);
DateTime date = absoluteLowValue;
long minutesPerWeek = 10080;
long minutesPerHide = new Duration(hideStart, hideEnd).getStandardMinutes();
int difference = (int)(minutesPerWeek - minutesPerHide);
int count = 0;
while (index - count >= difference){
date = date.plusWeeks(1);
count += difference;
}
int remaining = index - count;
DateTime nextStart = date;
while (nextStart.getDayOfWeek() != hideStart.getDayOfWeek())
nextStart = nextStart.plusDays(1);
nextStart = nextStart.withTime(hideStart.getHourOfDay(),
hideStart.getMinuteOfHour(),
hideStart.getSecondOfMinute(),
hideStart.getMillisOfSecond());
long timeDiff = new Duration(date, nextStart).getStandardMinutes();
if (timeDiff < remaining){
date = nextStart.plusMinutes((int)minutesPerHide);
count+= timeDiff;
remaining = index - count;
}
date = date.plusMinutes(remaining);
return date;
}
return new DateTime();
}
Is there a better or easier way of doing this process? I imagine that if I add in the amount of logic to loop through a list of "gaps" that it will just slow it down even more. I'm open to not using Jodatime, I just happen to be using that currently. Any help appreciated!
If I understood correctly, you want to compute the total time between a start and end date, and subtract one ore more periods. So, you could work with Duration objects and then, in the end, convert to whatever you want (minutes, seconds, etc):
// compute all periods you don't want to count
List<Duration> hideList = new ArrayList<>();
// duration between friday and saturday (assuming they have the values of your example)
hideList.add(new Duration(friday, saturday));
// add as many periods you want
// total duration between start and end dates
Duration totalDuration = new Duration(start, end);
// subtract all periods from total
for (Duration duration : hideList) {
totalDuration = totalDuration.minus(duration);
}
// convert to total number of minutes
long totalMinutes = totalDuration.getStandardMinutes();
I'm assuming that all dates used in hideList are between start and end dates (but it's easy to check this using isAfter and isBefore methods).
To do the opposite, you add the totalMinutes to the start date, and then sum all the durations in hideList:
// sum total minutes to start
DateTime dt = start.plusMinutes((int) totalMinutes);
// sum all the periods
for (Duration duration : hideList) {
dt = dt.plus(duration);
}
// dt is the end date
There's one detail: when converting the Duration to a number of minutes, you'll lose the seconds and milliseconds precision, so when doing the opposite algorithm, those fields will be lost. If that's not an issue, just do it as above. But if you want to preserve all the precision, just start by adding the totalDuration object instead of adding the number of minutes:
// sum totalDuratin to start (to preserve seconds and milliseconds precision)
DateTime dt = start.plus(totalDuration);
What we get is an Instant and a "date-grid" defined by a period (which defines the interval of datapoints, e.g.: Every Month, Every 3 Months, etc.) and a start date where we started that grid.
private Instant getValidDate(Instant request, Instant start, Period period) {
if(isOnGrid(request, start, period)) {
return request;
}
else {
return getNextPriorDateOnGrid(request, start, period);
}
}
An example:
Given are the following parameters:
request = Instant("2000-05-02T07:42:00.000Z") //Second May of 2000 7:42 AM
start = Instant("2000-01-01T06:00:00.000Z") //First January of 2000 6:00 AM
period = Period("3M") //Every 3 Months
isOnGrid(request, start, period); //Should return false
getNextPriorDate(request, start, period) //Should return the First April of 2000 6:00 AM
I really have no idea how to get this with reasonable performance (its a critical place in code)
How do you check whether a distant future date (given by the Instant) is exactly on this grid, and if not, what is the next past date that was on the grid?
EDIT: I forgot to mention: All times and dates are assumed to be in UTC Timezone
Here is a simple test case that should match your requirements:
package test;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
public class Java8PeriodAndInstant2 {
public static void main(String[] args) {
// LocalDate request=LocalDate.of(2000, 5, 2);
// LocalDate start=LocalDate.of(2000, 1, 1);
LocalDateTime start = Instant.parse("2000-01-01T06:00:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime();
LocalDateTime request = Instant.parse("2000-05-02T07:42:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime();
Period period = Period.ofMonths(3);
System.out.println("is on grid " + isOnGrid(request, start, period));
System.out.println("is on grid " + isOnGrid(LocalDateTime.of(2000, 4, 2,0,0), start, period));
System.out.println("is on grid " + isOnGrid(LocalDateTime.of(2000, 4, 1,0,0), start, period));
System.out.println("getNextPriorDate " + getNextPriorDate(request, start, period));
System.out.println("isOnGrid " + isOnGrid(Instant.parse("2000-01-03T05:00:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime(), start, Period.ofDays(1)));
System.out.println("isOnGrid " + isOnGrid(Instant.parse("2000-01-03T06:00:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime(), start, Period.ofDays(1)));
System.out.println("getNextPriorDate " + getNextPriorDate(Instant.parse("2000-01-03T05:00:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime(), start, Period.ofDays(1)));
}
private static boolean isOnGrid(LocalDateTime start, LocalDateTime request, Period period) {
if (period.getDays() != 0) {
return ((Duration.between(start, request).toHours()%period.getDays())==0);
}
Period diffPeriod = Period.between(start.toLocalDate(), request.toLocalDate());
if (diffPeriod.getDays()!=0) {
return false;
}
if (period.getMonths() != 0) {
return ((diffPeriod.toTotalMonths()) % (period.toTotalMonths()) == 0);
}
if (diffPeriod.getMonths()!=0) {
return false;
}
if (period.getYears() != 0) {
return ((diffPeriod.getYears()) % (period.getYears()) == 0);
}
return false;
}
private static LocalDateTime getNextPriorDate(LocalDateTime request, LocalDateTime start, Period period) {
if (period.getDays() != 0) {
long hoursDiff=Duration.between(start, request).toHours();
return start.plusDays(hoursDiff/24);
}
Period diffPeriod = Period.between(start.toLocalDate(), request.toLocalDate());
if (period.getMonths() != 0) {
diffPeriod = diffPeriod.withDays(0);
long monthDiff = diffPeriod.toTotalMonths() % period.toTotalMonths();
return start.plus(diffPeriod).minusMonths(monthDiff);
}
if (period.getYears() != 0) {
diffPeriod = diffPeriod.withDays(0);
diffPeriod.withMonths(0);
long yearsDiff = diffPeriod.getYears() % period.getYears();
return start.plus(diffPeriod).minusYears(yearsDiff);
}
return null;
}
}
it works with periods of days or months or years.
You cannot add Periods to Instants. They have a different "scope".
An Instant i simply represents a point in the timeline, counting the amount of millis/nanos from a specific point in time called "Epoch".
At this instant i, the time at the clock at the wall (even the date in a calendar) differs around the world. It depends on the timezone you are in.
A Period respects different lengths of its representation among different timezones starting at differnt dates. For example: A month lasts 30 days in June but 31 days in August. And it is even more complex if daylight saving shifts occur.
An Instant has no idea, what a "month" actually is. You can parse it from a String and output it to it, but internally it does not represent a human understandable form of a month like 'Jan', 'Feb', ... .
This is, why you have to align an Instant to a LocalDateTime or ZonedDateTime using a ZoneId or an ZoneOffset. Theses classes understand and can work with Periods.
The following code converts your Instants to LocalDateTimes to take into account the above comments:
private static Instant getValidDate2(Instant request, Instant start, Period period)
{
assert(!request.isBefore(start));
// multiplication of period only works with days exclusive or
// zero daypart of period
assert(period.getDays() == 0 || (period.getMonths() == 0 && period.getYears() == 0));
ZoneId utcZone = ZoneOffset.UTC;
LocalDateTime ldstart = LocalDateTime.ofInstant(start, utcZone);
LocalDateTime ldreq = LocalDateTime.ofInstant(request, utcZone);
// calculate an approximation of how many periods have to be applied to get near request
Duration simpleDuration = Duration.between(ldstart, ldstart.plus(period));
Duration durationToReq = Duration.between(ldstart, ldreq);
int factor = (int) (durationToReq.toDays() / simpleDuration.toDays()); // rough approximation
// go near to request by a multiple of period
Period jump = Period.of(period.getYears() * factor, period.getMonths() * factor, period.getDays() * factor);
LocalDateTime ldRunning = ldstart.plus(jump);
// make sure ldRunning < request
while (ldRunning.isAfter(ldreq)) {
ldRunning = ldRunning.minus(period);
}
// make sure we pass request and
// save the the last date before or equal to request on the grid
LocalDateTime ldLastbefore = ldRunning;
while (!ldRunning.isAfter(ldreq)) {
ldLastbefore = ldRunning;
ldRunning = ldRunning.plus(period);
}
return ldLastbefore.equals(ldreq) ? request : ldLastbefore.atZone(utcZone).toInstant();
}
Explanation:
To avoid a loop adding period until it gets to request, a rough approximation is done on how often period must be added to start to come to request. A new period being a multiple of the request period is then added and aligned to get the last value of the grid which is less or equal to request. Depending on a comparation between the last value and request, the according instant is returned. In fact, the check is useless besides the fact, that request == request when it was on the grid and not only equal.
Here you can find further informations about java time: https://docs.oracle.com/javase/tutorial/datetime/overview/index.html
I have code which checks if the given "A" time (in milliseconds) is in the given "b" time period.
private static boolean isInTimeInterval(long time, int timePeriod) {
long curTime = Calendar.getInstance().getTimeInMillis();
// time period is in hours, 1 hour is 3600000 ms;
long startTime = curTime - timePeriod * 3600000;
if (time >= startTime && time < curTime){
return true;
}
return false;
}
I take the time from a file and parse it into a long like this:
(Long.parseLong(array[2]))
But it doesn't work correctly, what is wrong ?
To simplify things, I would suggest that you first subtract the start time from the end time, check to see if that is positive and then decide if the remaining milliseconds is smaller than the requested time period.
long difference = Calendar.getInstance().getTimeInMillis() - time;
long timeRange = timePeriod * 3600000;
return (0 <= difference && differance <= timeRange);
It makes the code slightly smaller in lines, but more importantly, it simplifies the math to where you know the code isn't the problem.
As far as the errors you are likely encountering, I'd look to your
Long.parseLong(array[2])
As that is likely grabbing the input in a manner you aren't expecting. For starters, I'd put in some logging or at least one-time println debugging statements to verify the input times are what I thought they were.
I have to show the time that has passed after a message has been posted. I'm writing a method the takes publisheddate as input and returns elapsed time.
d1.gettime-pubdate.gettime=time in milliseconds.
I'm writing an if-condition where
if time==something concat days ago,
elseif hours ago
elseif min ago
elseif seconds ago
elseif justnow.
What should the if-condition contain? E.g. days should be if(time>=1440) (if I take the date in minutes, which can be done by time/1000*60*60); what should I do for minutes, hours, &c.?
Based on the advice given here is what i wrote .any improvement suggestions??
public static String TimeElapsed(final Date date)
{
Date Interval=new Date();
DateTime Interval1 = new DateTime();//Converting java time to joda time
Interval1 = new DateTime(date);
DateTime Interval2 = new DateTime();
Interval2=new DateTime(Interval);
//Now we have two joda time instances
Duration dd=new Duration(Interval1,Interval2);
//get duration between given and current time
Duration rd1=new Duration(1000);//seconds
Duration rd2=new Duration(60000);//minutes
Duration rd7=new Duration(60000*2);//2minutes
Duration rd3=new Duration(3600000);//hours
Duration rd6=new Duration(3600000*2);//2hours
Duration rd4=new Duration(86400000);//days
Duration rd5=new Duration(86400000*2);//2days
if(dd.isShorterThan(rd1)){
String k="just now";
return k;
}
else if(dd.isEqual(rd1)){
String k="just now";
return k;
}
else if(dd.isLongerThan(rd1)&& dd.isShorterThan(rd2))
{
String k=dd.getStandardSeconds()+""+" seconds ago";
return k;
}
else if(dd.isEqual(rd2) || dd.isShorterThan(rd7)){
String k=dd.getStandardMinutes()+""+" minute ago";
return k;
}
else if(dd.isLongerThan(rd2) && dd.isShorterThan(rd3)){
String k=dd.getStandardMinutes()+""+" minutes ago";
return k;
}
else if(dd.isEqual(rd3) || dd.isShorterThan(rd6))
{
String k=dd.getStandardHours()+""+" hour ago";
return k;
}
else if(dd.isLongerThan(rd3) && dd.isShorterThan(rd4)){
String k=dd.getStandardHours()+""+"hours ago";
return k;
}
else if(dd.isEqual(rd4) || dd.isShorterThan(rd5) ) {
String k=dd.getStandardDays()+""+" day ago";
return k;
}
else if(dd.isLongerThan(rd4)){
String k=dd.getStandardDays()+""+" days ago";
return k;
}
else{
String k="";
return k;
}
}
I agree with wrschneider99's suggestion of using Joda-Time, but IMO you should be using a Duration rather than an Interval, assuming you're talking about an elapsed duration which is a fixed number of milliseconds, rather than something more human-centric such as a number of months.
I would suggest something like:
// Assuming this really is a constant...
private static final Duration POST_DURATION = Duration.standardDays(2);
// Then elsewhere...
Instant expiry = post.getTime().plus(POST_DURATION);
// See below
Instant now = clock.now();
if (now.isAfter(expiry)) {
...
}
Note that I'd use some sort of Clock interface with a "just for test" implementation and a "based on system time" implementation and inject that as you would any other dependency, for testability. See Kevin Bourrillion's blog post about pure functions for more on this.
I think you're looking for something like Joda Time's Period.normalizedStandard method.
See:
Period to string