i want to make a simple progressbar showing me how much time some process takes. At the moment of creation of it I have only actual percentage (as int) and time that is left(as String formatted HH:mm:ss). I want it to update every second and show me the actual state of process. I've tried everything and it doesn't work. Current version looks like this - tell me please what I'm doing wrong...
int initialProgress = 35; // %
// time to finish process
Date endDate = new SimpleDateFormat("HH:mm:ss").parse("07:07:07");
Date now = new Date();
long totalDuration = (long) (((double)(endDate.getTimeInMillis()
- now.getTimeInMillis()) * 100.0 / (double)initialProgress);
and then every second I repeat:
now = new Date();
int currentProgress = (totalDuration - endDate.getTimeInMillis()
+ now.getTimeInMillis())/totalDuration;
It simply is not working. Total duration is even something strange...
The issue seems to be that you have a time remaining String and you want to parse it to percentage of work done.
The first thing you need, obviously, is the total expected time. Lets assume that this is also a String.
First write a method for parsing your HH:mm:ss String to a long representing time remaining in seconds.
public long parseToSeconds(final String duration) throws ParseException {
final MessageFormat durationFormat = new MessageFormat("{0,number,#}:{1,number,#}:{2,number,#}");
final Object[] parsedTimeRemaining = durationFormat.parse(duration);
final long totalDuration = TimeUnit.HOURS.toSeconds((Long) parsedTimeRemaining[0])
+ TimeUnit.MINUTES.toSeconds((Long) parsedTimeRemaining[1])
+ (Long) parsedTimeRemaining[2];
return totalDuration;
}
What we do here is use a MessageFormat to parse your String into an array of Object. As we have told the MessageFormat that these are numbers, it will automagically convert (or try to convert, hence the exception) to Long.
Once we have those numbers we scale them all to seconds using the (very useful) TimeUnit class.
A couple of quick tests to ensure we're on the right track:
System.out.println(parseToSeconds("00:00:01"));
System.out.println(parseToSeconds("00:01:00"));
System.out.println(parseToSeconds("01:00:00"));
System.out.println(parseToSeconds("01:01:01"));
Output:
1
60
3600
3661
Looks good.
Lets assume that right as the start of the process we get a time remaining, for simplicity, of "04:04:04", this gives 14644. Now we just need to store that and calculate the percentage against any new duration String. This should do the trick:
public int asPercentage(final long totalTime, final long remaining) {
final double percentage = remaining / ((double) totalTime);
return (int) (percentage * 100);
}
Note the fact that I cast (seemingly pointlessly) one of the items to a double. This is because in Java any operation on an integral type always returns another integral type. Casting to a double forces it to return a double.
Lets do a quick check again:
final long totalDuration = 14644;
System.out.println(asPercentage(totalDuration, parseToSeconds("03:03:03")));
System.out.println(asPercentage(totalDuration, parseToSeconds("02:02:02")));
System.out.println(asPercentage(totalDuration, parseToSeconds("01:01:01")));
Output:
75
50
25
Looks good, that is the time remaining as a percentage of the total. Maybe to quite what we want for a progress bar. Lets invert it:
public static int asPercentage(final long totalTime, final long remaining) {
final double percentage = remaining / ((double) totalTime);
return 100 - (int) (percentage * 100);
}
Output:
25
50
75
Ah-ha. Much better.
Related
Lets say i have long currentMillis and long oldMillis. The difference between the two timestamps is very tiny and always less than 1 second.
If i want to know the difference between the timestamps in milleseconds, i can do the following:
long difference = currentmillis-oldmillis;
And if i want to convert difference to seconds, i can just divide it by 1000. However if the difference in milliseconds is less than 1000 milliseconds(<1 second), dividing it by 1000 will result in 0.
How can i get the difference between the two timestamps if the difference is less than a second? For example, if the difference is 500 milliseconds, the desired output is 0.5 seconds.
Using float/double instead of long always returns 0.0 for some reason i don't understand.
My code:
private long oldmillis = 0, difference = 0;
private long calculateDifference()
{
long currentMillis = System.currentTimeMillis();
if (oldMillis == 0) oldMillis = currentMillis;
difference = currentMillis - oldMillis;
oldMillis = currentMillis;
return difference;
}
The method calculateDifference is called randomly with a small random time interval.
It sounds like you just need to convert the results into double before the division:
// This will work
double differenceMillis = currentMillis - oldMillis;
double differenceSeconds = differenceMillis / 1000;
// This will *not* work
double differenceSecondsBroken = (currentMillis - oldMillis) / 1000;
In the latter code, the division is performed using integer arithmetic, so you'll end up with a result of 0 that is then converted to a double.
An alternative which would work is to divide by 1000.0, which would force the arithmetic to be done using floating point:
double differenceSeconds = (currentMillis - oldMillis) / 1000.0;
I am having some strange output when I am attempting to get an average from a long value and converting it to a String (in HH:mm:ss) format. I'm using the Joda time library, which mostly has been a life saver.
Here's what I have so far:
//this is in a static class
public static Duration calculateTime(Date start, Date end) {
Duration duration = new Duration(start.getTime(), end.getTime());
return duration;
}
public static String convertMillisToTime(long milliseconds) {
return String.format("%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(milliseconds),
TimeUnit.MILLISECONDS.toMinutes(milliseconds) -
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milliseconds)),
TimeUnit.MILLISECONDS.toSeconds(milliseconds) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));
}
long averageRunTime = 0;
averageRunTime += calculateTime(drs.startTime, drs.endTime).getMillis();
Since this is done in an array list, to get the average, I am taking
averageRunTime = averageRunTime / (long)arrayList.size();
when I get to the point of
convertMillisToTime(averageRunTime);
It's returning a value that is higher than any times I have. IE( I have no job times that are over 11 hours) but this is returning me a String with 11:mm:ss. Not quite sure WHY?
I used the convertMillisToTime from: How to convert milliseconds to "hh:mm:ss" format?
Can anyone point me in the right direction?
Thank you in advance,
RR
Edit: I've modified the code that converts the long to time using TimeUnit. It's strange though, I grabbed three runs (time streams) and got 09:14:17, 08:57:18, 09:10:25 for the length of each run, and the average is coming out as: 10:27:26.
You could just use a formatter from Joda Time instead, for example...
public static final PeriodFormatter PERIOD_FORMATTER = new PeriodFormatterBuilder().printZeroAlways().
appendHours().appendSeparator(":").
appendMinutes().appendSeparator(":").
appendSeconds().appendSeparator(".").
appendMillis().toFormatter();
public static String toString(Duration duration) {
return duration == null ? "[invalid duration]" : duration.toPeriod().toString(PERIOD_FORMATTER);
}
I'm trying to make a timing mechanism using threads, and I'm having a problem in getting the time difference between two Dates, and using that difference to get a current percentage of the time left. Here is the concept I'm trying to prototype:
And here is my implementation:
long startMilisecs = System.currentTimeMillis();
long currentMilisecs;
long endDateMilisecs = getEndDate().getTime();
int diffMillisecs = ((int)(endDateMilisecs - startMilisecs) / 1000) / 60;
int currPerc;
while (startMilisecs <= endDateMilisecs)
{
currentMilisecs = (int) System.currentTimeMillis();
currPerc = ((int)currentMilisecs * 100) / diffMillisecs;
System.out.println(" Current Percentage: " + currPerc);
}
The problem with this code is that the percentage is not starting from 0 but rather in the 20's to 40 percent.
Can you tell me what is wrong with this? and for this problem I have been restricted to using only threads.
check below:
public static int getPercentageLeft(Date start, Date end) {
long now = System.currentTimeMillis();
long s = start.getTime();
long e = end.getTime();
if (s >= e || now >= e) {
return 0;
}
if (now <= s) {
return 100;
}
return (int) ((e - now) * 100 / (e - s));
}
You need to subtract the starting time like this
currPerc = ((currentMilisecs - startMilisecs) * 100) / diffMillisecs;
to get the correct percentage.
The problem is with the System.currentTimeMillis();. Taken from the javadoc:
public static long currentTimeMillis()
Returns the current time in milliseconds. Note that while the unit of
time of the return value is a millisecond, the granularity of the
value depends on the underlying operating system and may be larger.
For example, many operating systems measure time in units of tens of
milliseconds.
See the description of the class Date for a discussion of slight
discrepancies that may arise between "computer time" and coordinated
universal time (UTC).
Returns:
the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
So your current time in milliseconds is based on January 1, 1970 UTC, not on your start date.
You need to calculate current time by subtracting start time from the value that is given by System.currentTimeMillis();.
I am basically formulating your linked image here. Other alternative calculations can also be carried out.
Regarding the following method:
private long getCountdownLeft(Integer seconds) {
long now = System.currentTimeMillis();
long elapsedMillis = now - seconds;
long millisLeft = seconds * 1000 - elapsedMillis;
return millisLeft/1000;
}
public static void Main(String[] args) {
getApi().getLogger.debug("TimeLeft " + getCountDownLeft(3600)); //base time
}
It is returning a value of something like: -12039495960, why is that?
If 3600 is the value you are passing in, you're obviously going to get a negative value.
Print out System.currentTimeMillis(), youll see that it is quite a large value.
currentTimeMillis() is defined as:
the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
It's extremely hard to tell based on the information given, but for this method to work, seconds must be initialized with System.currentTimeMillis() / 1000.
Boiled down into one statement, you have
return (seconds * 999 - System.currentTimeMillis())/1000;
If System.currentTimeMillis() is unix time (1343874904292 right now), then OF COURSE this will be negative. I'm not sure what you meant to write, but this obviously not it...
Your problem is you are subtracting seconds from milliseconds.
Try this:
long elapsedMillis = now - (seconds * 1000); // convert seconds to millseconds
It seems you are really just trying to do this:
private long getCountdownLeft(Integer seconds) {
return seconds * 1000;
}
I had a bug that caused an integer overflow, resulting in wrong (negative) timestamps being written to the database. The code is fixed already, but I want to fix the wrong data, too.
I thought, I could just take the wrong results and add Integer.MAX_VALUE, but that didn't seem to work, it left me with to high values. I have the offset value in the code snippet below, but the input values are not stored.
The following code reproduces the bug:
#Test
public void testArexxConversion()
{
// The input values represent seconds since midnight, Jan 1, 2000 UTC
final int sample = 361450072; // A sample input value drawn from production
// I use the offset from the UNIX epoch to convert the vakue to UNIX seconds
final int offset = 946684800; // midnight, Jan 01 2000 UTC in UNIX seconds
// This was the buggy line in my code, the assertion will fail
long result = (sample + offset) * 1000;
// Prints 'Result is negative: -1830153280'
Assert.assertTrue(result > 0, String.format("Result is negative: %d", result));
// This is for comparison
Date dt = new Date(offset * 1000);
Assert.assertEquals(dt.getTime() + sample * 1000, result);
}
How to fix the bug in your database
To fix the bug in your database you can do the following addition to all the buggy data:
long new_result = old_buggy_result + 1309965025280L;
The constant number was found like this:
Check the buggy result value
Find what should the correct result value be?
Do an addition to the buggy result value to find the correct `result.
But this is only possible if you have saved sample and offset in your database or somewhere else.
Otherwise, it depends on the number of wraps that occured during the original calculation:
long size_of_int = (long)Math.pow(2, 32);
int number_of_wraps = 305 // Only correct in your example!
// You can't deduct the number of wraps from
// the wrong value alone, because that information
// is lost in the modulo (the "wrap")
long correct_number = wrong_number + size_of_int * number_of_wraps;
If the numbers in your database are close enough to your sample value, this means, you can do the above, using 305 as the number of wraps.
Explanation of the bug (for future readers)
The operation here:
(sample + offset) * 1000;
is computed using int and not long. But the result is "too big" to be saved on an int variable. That's why you have an overflow.
Change it to:
((long) sample + offset) * 1000L;
So now the + and * operations will be done using long values, and the result will be a long value which won't overflow.
That would be like this:
long result = ... ; // bad negative from database
long new_result = (long)((int)result - Integer.MAX_VALUE) + Integer.MAX_VALUE;
Replace this line.
long result = (long)(sample + offset) * 1000L;