I'm trying to calculate the number of complete contiguous Periods in an Interval in Joda Time (where the Period is arbitrary but constant).
The simple solution I've come up with is a linear search using a while loop:
public static long periodsInAnInterval(Interval interval, Period period) {
int periods = -1;
DateTime marker = interval.getStart();
while (marker.isBefore(interval.getEnd()) || marker.isEqual(interval.getEnd())) {
marker = marker.plus(period);
periods++;
}
return periods;
}
An O(n) solution is obviously pretty horrible, so can anyone think of a better way? I'm wondering whether some kind of binary search could be used...
Here's a test case: https://gist.github.com/Mahoney/9899832
Edit - remember a Period does not have a known number of seconds; Period.toStandardDuration() is just an approximation assuming years have 365 days, months have 30 days and days have 24 hours. (Actually a quick test reveals Period.toStandardDuration bombs out with an exception if you have years or months in the period.)
Edit 2 - I'm happy to assume that the first period begins at the start of the interval - otherwise I suspect the answer might vary depending on whether the remainder time were at the beginning, the end or both.
Here's my preferred solution: use average length of a period to form a best guess and then refine it. This seems the most efficient and elegant way to do it.
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import org.joda.time.*;
import static com.google.common.collect.FluentIterable.from;
import static java.util.Arrays.asList;
import static org.joda.time.DurationFieldType.*;
public class PeriodArithmetic {
public static long periodsInAnInterval(Interval interval, Period period) {
int bestGuess = (int) (interval.toDurationMillis() / toAverageMillis(period));
if (bestGuess < 0) return 0;
if (startPlusScaledPeriodIsAfterEnd(interval, period, bestGuess + 1)) {
return searchDownwards(interval, period, bestGuess);
} else {
return searchUpwards(interval, period, bestGuess);
}
}
private static long searchDownwards(Interval interval, Period period, int currentGuess) {
if (startPlusScaledPeriodIsAfterEnd(interval, period, currentGuess)) {
return searchDownwards(interval, period, currentGuess - 1);
} else {
return currentGuess;
}
}
private static long searchUpwards(Interval interval, Period period, int currentGuess) {
if (!startPlusScaledPeriodIsAfterEnd(interval, period, currentGuess + 1)) {
return searchUpwards(interval, period, currentGuess + 1);
} else {
return currentGuess;
}
}
private static boolean startPlusScaledPeriodIsAfterEnd(Interval interval, Period period, int scalar) {
return interval.getStart().plus(period.multipliedBy(scalar)).isAfter(interval.getEnd());
}
private static final long MILLIS_IN_DAY = Days.ONE.toStandardSeconds().getSeconds() * 1000L;
private static final long MILLIS_IN_YEAR = Days.ONE.toStandardSeconds().getSeconds() * 365250L;
private static final ImmutableMap<DurationFieldType, Long> averageLengthMillis
= ImmutableMap.<DurationFieldType, Long>builder()
.put(millis(), 1L)
.put(seconds(), 1000L)
.put(minutes(), Minutes.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(hours(), Hours.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(halfdays(), MILLIS_IN_DAY / 2)
.put(days(), MILLIS_IN_DAY)
.put(weeks(), Weeks.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(months(), MILLIS_IN_YEAR / 12)
.put(years(), MILLIS_IN_YEAR)
.put(weekyears(), MILLIS_IN_YEAR)
.put(centuries(), MILLIS_IN_YEAR * 100)
.put(eras(), Long.MAX_VALUE)
.build();
private static long toAverageMillis(Period period) {
final Iterable<Long> milliValues = from(asList(period.getFieldTypes())).transform(toAverageMillisForFieldType(period));
return total(milliValues);
}
private static Function<DurationFieldType, Long> toAverageMillisForFieldType(final Period period) {
return new Function<DurationFieldType, Long>() {
#Override
public Long apply(DurationFieldType durationFieldType) {
final Long averageDuration = averageLengthMillis.get(durationFieldType);
return period.get(durationFieldType) * averageDuration;
}
};
}
private static long total(Iterable<Long> milliValues) {
long acc = 0;
for (Long milliValue : milliValues) {
acc += milliValue;
}
return acc;
}
}
I've put the beginnings of a binary search solution here: https://gist.github.com/Mahoney/9899936
It's much more complicated than the linear search; on the other hand it's roughly 100x faster at finding the number of months in 1,000 years, for instance.
It's also unfinished - more of an experiment than anything, so I'm sure there are untested edge cases (negative periods?).
Still keen to know if anyone has a more elegant solution (or just one I don't have to write, test & maintain!).
Related
I am writing a stats object. I want to track the average with minimal information
I currently have a method:
public synchronized void setSuccessful(long time) {
int succCount = _successful.get(); // atomic int
_successful.incrementAndGet();
long maxTime = _maxSuccessfulTimeNano.get(); // atomic long
if (time > maxTime) {
_maxSuccessfulTimeNano.set(time);
}
long avg = ((_avgSuggessfulTimeNano.get() /* atomic long */ * succCount) + time) / (succCount + 1);
_avgSuggessfulTimeNano.set(avg);
}
Is there a better way of implementing this? or is there a better stats to acquire
I have client-server application in which i need to measure the rate of request arrival per second(Request rate). For this, i have a timer object that activates after every seconds, reads a synchronized counter and then sets it to zero. The counter increments on each request arrival.I used following code to detect request rate. There are so many other threads and timers in my application running.The problem is "due to the inaccuracy of timers i am not getting the perfect request rate". Is there any alternative of measuring request rate other than using timers.
public class FrequencyDetector extends TimerTask {
RequestCounter requestCounter;
FrequencyHolder frequencyHolder;
public FrequencyDetector(RequestCounter requestCounter,FrequencyHolder frequencyHolder){
this.frequencyHolder=new FrequencyHolder();
this.frequencyHolder=frequencyHolder;
}
#Override
public void run() {
int newFrequency=requestCounter.getCounter();
frequencyHolder.setFrequency(newFrequency);
requestCounter.setCounterToZero();
//calls to other fuctions
}
}
Instead of checking counter per unit time you can check time per unit counter. That will probably give you more accurate results. Algorithm is given below.
Increment counter on every request.
When counter reaches a certain FIXED_LIMIT calculate frequency by frequency=FIXED_LIMIT/duration since last record
Reset the counter and start with step 1
However this will record frequency at unpredictable intervals and if frequency of request decreases the duration between successive records will increase.
To handle it we can implement an adaptive algorithm, algorithm is given below.
Increment the counter on every request.
When counter reaches a certain ADAPTIVE_LIMIT record frequency as frequency=ADAPTIVE_LIMIT/duration since last record
Change ADAPTIVE_LIMIT as ADAPTIVE_LIMIT=frequency * DESIRED RECORD INTERVAL
Reset counter and start with step 1.
Above algorithm will reset the limit based on frequency last recorded. It's given that it will not be recording at optimal intervals but it will be pretty close.
Also it will give you highly accurate frequencies as it does not depend on any scheduled thread.
Following is an implementation of such an adaptive counter.
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
public class TestCounter {
//Keep initial counterInterval to a small value otherwise first record may take long time
final AtomicLong counterInterval = new AtomicLong(10);
AtomicLong requestCounter = new AtomicLong();
volatile long lastTime;
/**OPTIMAL_DURATION is the duration after which frequency is expected to be recorded
* Program adaptively tries to reach this duration
*/
static final double OPTIMAL_DURATION = 1.0; // 1 second
static final Random random = new Random();
public static void main(String[] args) {
System.out.println("Started ");
TestCounter main = new TestCounter();
for(int i = 0; i < 1000; i++) {
main.requestArrived();
}
}
/*
* Simulating requests
*/
public void requestArrived() {
printCounter();
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//This will be in some Utility class
private void printCounter() {
requestCounter.incrementAndGet();
long currentTime = System.nanoTime();
long currentInterval = counterInterval.get();
if(requestCounter.get() > currentInterval) {
if(lastTime != 0) {
long timeDelta = currentTime - lastTime;
long frequency = (long)(currentInterval / (timeDelta / 1e9));
System.out.printf("time=%.2f, frequency=%d\n", (timeDelta / 1e9), frequency);
//updating the currentInterval for the miss
long newCounterInterval = (long)(frequency * OPTIMAL_DURATION);
counterInterval.set(newCounterInterval);
}
requestCounter.set(0);
lastTime = currentTime;
}
}
}
Output
Started
time=0.54, frequency=18
time=0.98, frequency=18
time=1.01, frequency=17
time=0.96, frequency=17
time=0.99, frequency=17
time=0.85, frequency=19
time=0.96, frequency=19
time=0.82, frequency=23
time=1.08, frequency=21
time=0.98, frequency=21
time=0.94, frequency=22
time=1.06, frequency=20
time=1.07, frequency=18
time=0.99, frequency=18
time=0.98, frequency=18
time=1.02, frequency=17
time=0.92, frequency=18
time=0.92, frequency=19
time=0.89, frequency=21
time=0.82, frequency=25
time=1.31, frequency=19
time=1.02, frequency=18
In a java ee application with JSF and JPA,I have to calculate totals of attributes in lists of objects. I have used LambdaJ library to achieve the calculation of sums.
As I use this sort of summations in several places of the application with possible implications to the overall performance, I designed the following test java application to test the efficacy of LambdaJ.
This application demonstrated a gross lack of efficacy of LambdaJ.
Is there any error in the testing programme or should I replace all lambdaJ occurrences with iterations ?
The result
Time taken to calculate a single total by iteration is 15 milliseconds.
Time taken to calculate a single total by LambdaJ is 199 milliseconds.
Time taken to calculate multiple totals by iteration is 23 milliseconds.
Time taken to calculate multiple totals by LambdaJ is 114 milliseconds.
The Main Method
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.lakmedi;
import ch.lambdaj.Lambda;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
/**
*
* #author buddhika
*/
public class main {
public static void main(String[] args) {
List<Bill> bills = new ArrayList<>();
Random r = new Random();
for (int i = 0; i < 100000; i++) {
Bill b = new Bill();
b.setGrossTotal(r.nextDouble());
b.setDiscount(r.nextDouble());
b.setNetTotal(b.getGrossTotal() - b.getDiscount());
bills.add(b);
}
calSingleByIteration(bills);
calSingleByLambdja(bills);
calMultipleByIteration(bills);
calMultipleByLambdja(bills);
}
public static void calSingleByIteration(List<Bill> bills) {
Date startTime = new Date();
double grossTotal = 0.0;
for (Bill b : bills) {
grossTotal += b.getGrossTotal();
}
Date endTime = new Date();
long timeTaken = endTime.getTime() - startTime.getTime();
System.out.println("Time taken to calculate a single total by iteration is " + timeTaken + " milliseconds.");
}
public static void calSingleByLambdja(List<Bill> bills) {
Date startTime = new Date();
double grossTotal = Lambda.sumFrom(bills).getGrossTotal();
Date endTime = new Date();
long timeTaken = endTime.getTime() - startTime.getTime();
System.out.println("Time taken to calculate a single total by LambdaJ is " + timeTaken + " milliseconds.");
}
public static void calMultipleByIteration(List<Bill> bills) {
Date startTime = new Date();
double grossTotal = 0.0;
double discount = 0.0;
double netTotal = 0.0;
for (Bill b : bills) {
grossTotal += b.getGrossTotal();
discount += b.getDiscount();
netTotal += b.getNetTotal();
}
Date endTime = new Date();
long timeTaken = endTime.getTime() - startTime.getTime();
System.out.println("Time taken to calculate multiple totals by iteration is " + timeTaken + " milliseconds.");
}
public static void calMultipleByLambdja(List<Bill> bills) {
Date startTime = new Date();
double grossTotal = Lambda.sumFrom(bills).getGrossTotal();
double discount = Lambda.sumFrom(bills).getDiscount();
double netTotal = Lambda.sumFrom(bills).getNetTotal();
Date endTime = new Date();
long timeTaken = endTime.getTime() - startTime.getTime();
System.out.println("Time taken to calculate multiple totals by LambdaJ is " + timeTaken + " milliseconds.");
}
}
The Bill Class
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.lakmedi;
/**
*
* #author buddhika
*/
public class Bill {
private double grossTotal;
private double netTotal;
private double discount;
public double getGrossTotal() {
return grossTotal;
}
public void setGrossTotal(double grossTotal) {
this.grossTotal = grossTotal;
}
public double getNetTotal() {
return netTotal;
}
public void setNetTotal(double netTotal) {
this.netTotal = netTotal;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
}
Assuming you mean efficiency and not efficacy, then your measured performance is more or less as expected - see LambdaJ's own performance analysis.
You might get better results using Java 8 Lambda Expressions, but the main goal here is to improve readability and maintainability of source code and not speed. Are you sure that the time needed for performing the summations is critical to the overall performance of the application? I doubt it considering (I/O) waiting times for HTTP-traffic (JSF) and database queries (JPA) in which case this is premature optimization (the root of all evil).
Firstly, check you are using the latest version of LambdaJ, because the project site indicates that recent versions have some performance improvements.
However, I think you should expect lambdaj to be slower than writing the equivalent for-loop. Behind the more concise syntax, the JVM is doing a lot more work.
You could consider dropping lambdaj and using Java 8 streams and lambda expressions.
Here's what the author of lambdaj has to say on the matter:
Lemme clarify this once again: after the release of Java8 I don't see any point in developing/maintaining #lambdaj. Move to Java8 & be happy
-- Mario Fusco, May 2014
I'm about to write lines of some simple math and wanted to make sure that there wasn't some simple high level construct in Joda-Time to do this already.
I have an object that represents a day of the week, an hour of the day, and a minute of the hour. For example "Wednesday at 10:14am".
I want to calculate the number of milliseconds until the next occurrence. For example if now is Thursday at 10:14 it would be 6 days worth of milliseconds. This is because Wednesday has already passed so it will take 6 days to get to the next Wednesday. If now is Wednesday at 10:13.0001 it will be 999.
Is there a high level construct in Joda-Time so I can do this in one or two lines of code or do I need to do the math myself (including edge cases to wrap on stuff like DOW < DOW_NOW).
Thanks!
Here's my novice try that does not yet work to give you some reference:
public MutableDateTime getDateTime() {
MutableDateTime date = MutableDateTime.now();
date.setDayOfWeek(this.day);
date.setHourOfDay(this.hour);
return date;
}
public long getTimeUntilNextFrom( DateTime from ) {
MutableDateTime to = getDateTime();
if (to.isBefore( from )) {
to.setWeekOfWeekyear(from.getWeekOfWeekyear() + 1);
}
return new Interval(from, to).toDurationMillis();
}
You could do something like this:
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.Interval;
import org.joda.time.LocalTime;
public class Main {
public static void main(String[] args) {
Interval interval = betweenNowAndNext(DateTimeConstants.MONDAY, new LocalTime(10, 14));
System.out.println(interval.toDurationMillis());
}
public static Interval betweenNowAndNext(int dayOfWeek, LocalTime time) {
DateTime now = DateTime.now();
DateTime closest = time.toDateTime(now).withDayOfWeek(dayOfWeek);
return new Interval(now, closest.isBefore(now) ? closest.plusWeeks(1) : closest);
}
}
I would do it like this:
public class DistanceCalculator {
public long getMillisecondTillNext(int dayOfWeek, int hourOfDay, int minuteOfHour) {
DateTime now = DateTime.now();
DateTime next = DateTime.now().withDayOfWeek(dayOfWeek).withHourOfDay(hourOfDay).withMinuteOfHour(minuteOfHour);
long distance = next.getMillis() - now.getMillis();
return distance > 0 ? distance : week() - distance;
}
private long week() {
return new DateTime(0).plusWeeks(1).getMillis();
}
}
Haven't hear of any readymade method to get this in Joda...
Here's what I came up with on my own. Still, would be nice to have a solution in fewer lines of code. They DayHour is the class I am working with. It contains a day of the week and the hour of the day.
public class DayHour {
int day;
int hour;
public DayHour(int day, int hour) {
this.day = day;
this.hour = hour;
}
public MutableDateTime getDateTime(DateTime base) {
MutableDateTime date = base.toMutableDateTime();
date.setDayOfWeek(this.day);
date.setHourOfDay(this.hour);
return date;
}
public long getTimeUntilNextFrom(DateTime from) {
MutableDateTime to = getDateTime(from);
if (to.isBefore(from)) {
to.setWeekOfWeekyear(from.getWeekOfWeekyear() + 1);
}
return new Interval(from, to).toDurationMillis();
}
}
#Test
public void testDayHour() {
DateTime now = DateTime.now();
DayHour date = new DayHour(now.getDayOfWeek(), now.getHourOfDay());
MutableDateTime yesterday = now.toMutableDateTime();
yesterday.addDays(-1);
assertEquals(TimeUnit.DAYS.toMillis(1), date.getTimeUntilNextFrom(yesterday.toDateTime()));
MutableDateTime tomorrow = now.toMutableDateTime();
tomorrow.addDays(1);
assertEquals(TimeUnit.DAYS.toMillis(6), date.getTimeUntilNextFrom(tomorrow.toDateTime()));
}
Not quite sure what you are doing. But if what you want is a countdown in milliseconds, I would use the Joda-Time Seconds class with its secondsBetween method. Multiply by 1,000 to report approximate milliseconds. On the last second, switch gears to use the .getMillis method if you truly need that.
For example, I have input parameter this format: "04:00-06:00" or "23:00-24:00". Type of parameter - String.
And in my method I must check, that time range in input parameter NOT before current time. How I can do it?
More details:
input time range: "12:00-15:00"
current time: 16:00.
In this case, method must return false.
Another example:
input time range: "10:30-12:10"
current time: 09:51.
method must return true.
Can you please give me some idea or algorithm? How I can implement this method?
First off, you should probably just learn to use Joda time.
That said, since the times are all zero padded, you can just compare strings lexically.
public static boolean inRange(String time, String range) {
return time.compareTo(range.substring(0, 5)) >= 0
&& time.compareTo(range.substring(6)) <= 0;
}
It's good practice to fail fast on malformed inputs.
private static final Pattern VALID_TIME = Pattern.compile("[012][0-9]:[0-5][0-9]");
private static final Pattern VALID_RANGE = Pattern.compile("[012][0-9]:[0-5][0-9]-[012][0-9]:[0-5][0-9]");
and then put an assert at the top of inRange:
assert VALID_TIME.matcher(time).matches() : time
assert VALID_RANGE.matcher(range).matches() : range
EDIT:
If you really need to represent the current time as a Date, then you should compare it this way:
public final class Range {
/** Inclusive as minutes since midnight */
public final int start, end;
public Range(int start, int end) {
assert end >= start;
}
/** #param time in minutes since midnight */
public boolean contains(int time) {
return start <= time && time <= end;
}
public static Range valueOf(String s) {
assert VALID_RANGE.matcher(s).matches() : s;
return new Range(minutesInDay(s.substring(0, 5)),
minutesInDay(s.substring(6));
}
private static int minutesInDay(String time) {
return Integer.valueOf(time.substring(0, 2)) * 60
+ Integer.valueOf(time.substring(3));
}
}
Use Range.valueOf to convert from a String, convert your Date to a number of minutes since midnight in whatever timezone you like using whatever calendar implementation you like, and then use Range.contains.
Date currentDate = new Date();
Date maxDate;
Date minDate;
//Parse range to two substrings
//parse two substrings to [HH, MM]
//for HH && MM parseInt()
//
minDate= new Date.SetHour(HH); minDate.SetMinute(MM);
//repeat for max date
if(currentDate.Before(maxDate) && currentDate.After(minDate))
{
return true;
}
else
return false;