How does this CountDownTimer code works? - java

I have seen this code on stackoverflow but I don't understand how it works, I feel so frustrated
new CountDownTimer(9000000, 1000) {
public void onTick(long millisUntilFinished) {
long millis = millisUntilFinished;
String time = String.format("%02d:%02d:%02d",
// From here,, I don't understand what is "going on "
TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis)-
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis)-
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
}
9000000 milliseconds is : 02:30:00
Hour : TimeUnit.MILLISECONDS.toHours(millis),
9000000 milliseconds to hours is : 2,5 hours
Up to this, it's gonna show : 02:00:00
OK, I get it
Minutes : TimeUnit.MILLISECONDS.toMinutes(millis)- TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis))
I've converted and soustracted :
TimeUnit.MILLISECONDS.toMinutes(millis) = 150
TimeUnit.MILLISECONDS.toHours(millis) = 2,5
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)) = 150
150-150 = 0 , shouldn't I have something equal to 30 ??
Seconds : TimeUnit.MILLISECONDS.toSeconds(millis)-TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
Same thing, it's gonna give me 0
How does this work ? Thank you
}

Try doing it manually. First work out how many whole hours. Then subtract the number of whole hours (in milliseconds) from the total milliseconds and use that result to calculate then number of whole minutes. You can do the same for seconds (but to get the number of seconds, it’s ok to simply subtract off the whole minutes because there’s a whole number of minutes in a hour so the hours are automatically handled).
Maybe dummy variables would help:
WholeHours = TimeUnit.MILLISECONDS.toHours(millis)
WholeMinutes = TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis))
WholeSeconds = TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))

Related

Long converting to wrong time

I am trying to format a long until days, hours, minutes, seconds and found a method online.
public static String getTime(long time) {
long enlapsed = System.currentTimeMillis() - time;
long days = TimeUnit.MILLISECONDS.toDays(enlapsed);
enlapsed -= TimeUnit.DAYS.toMillis(days);
long hours = TimeUnit.MILLISECONDS.toHours(enlapsed);
enlapsed -= TimeUnit.HOURS.toMillis(hours);
long minutes = TimeUnit.MILLISECONDS.toMinutes(enlapsed);
enlapsed -= TimeUnit.MINUTES.toMillis(minutes);
long seconds = TimeUnit.MILLISECONDS.toSeconds(enlapsed);
String result = days + "d " + hours + "h " + minutes + "m " + seconds + "s ";
return result.replace("0d ", "").replace("0h ", "").replace("0m ", "").replace("0s ", "");
}
and it was working but now suddenly inputting 4380 is giving 18151d 15h 33m 34s. What did I do wrong?
The method you found online calculates how much time has elapsed between now, and a given timestamp. You can test that this method works going to a site like this, and multiplying the timestamp by 1000 to convert it to milliseconds, and passing that long to getTime.
The timestamp 4380 represents 4380ms after the Java Epoch, which is approximately 1 January 1970, which is about 18000 days ago.
If you want a more recent example, the timestamp 1568312340000 is just a few minutes before I started writing this, and doing System.out.println(getTime(1568312340000L)); gives a smaller duration.
What you seem to want is not to calculate the elapsed time between now and a certain timestamp. You seem to just want to convert an amount of milliseconds to a duration expressed in days, hours, minutes and seconds.
The method works by first calculating the difference in milliseconds between now and the given timestamp, putting the difference into a variable called enlapsed (spelling mistake!) Then it calculates how many days, hours, minutes and seconds goes into enlapsed milliseconds. What you want is everything this method does except the first part where it calculates the difference, so you should delete this line:
long enlapsed = System.currentTimeMillis() - time;
and replace it with:
long enlapsed = time;
because you just want it to directly calculate what duration the time parameter represents.
An input of 4380 to your method would return the time from the epoch minus about 4 seconds. The epoch is 1970-01-01, so 18151 days/365.25 = 49.7 years - which in September of 2019 seems right to me.

Convert hh:mm:ss to mmm:ss

I have a Java function that convert seconds to an specific format (hh:mm:ss):
public static String formatChronometer(long seconds) {
return String.format("%02d:%02d:%02d", TimeUnit.SECONDS.toHours(seconds),
TimeUnit.SECONDS.toMinutes(seconds) % TimeUnit.HOURS.toMinutes(1),
TimeUnit.SECONDS.toSeconds(seconds) % TimeUnit.MINUTES.toSeconds(1));
}
My client want to show this chronometer without the "hour" labels. Example:
if 100 hours => 6000:00
if 0 hours and 50 minutes => 50:00
if 1 hour and 12 minutes and 30 seconds => 72:30
if 10 hours and 5 minutes => 600:05
if 0 hours, 0 minutes and 0 seconds => 00:00
How should I change my method?
For flexible minutes number you have to use %d instead of %02d which specify how many digit you want, your solution should look like this :
return String.format("%d:%02d",
TimeUnit.SECONDS.toMinutes(seconds),
TimeUnit.SECONDS.toSeconds(seconds) % TimeUnit.MINUTES.toSeconds(1)
);
Example
long[] times = {360000, 3000, 4350, 36300, 0};
for (long time : times) {
String result = String.format("%d:%02d",
TimeUnit.SECONDS.toMinutes(time),
TimeUnit.SECONDS.toSeconds(time) % TimeUnit.MINUTES.toSeconds(1));
System.out.println(String.format("%d : %s", time, result));
}
Outputs
360000 : 6000:00
3000 : 50:00
4350 : 72:30
36300 : 605:00
0 : 0:00
Just ditch the hours bit.
return String.format("%04d:%02d",
TimeUnit.SECONDS.toMinutes(seconds),
TimeUnit.SECONDS.toSeconds(seconds) % TimeUnit.MINUTES.toSeconds(1));
public static String formatChronometer(long seconds) {
Duration dur = Duration.ofSeconds(seconds);
return String.format("%02d:%02d", dur.toMinutes(), dur.toSecondsPart());
}
When I fed 36005 into this method, it returned 600:05.
I take it that your chronometer seconds denote a duration, an amount of time. Either an elapsed time or a value to count down from. Not a time of day (you mentioned 100 hours an example, but the time of day is never 100 hours). In that case I would clearly use the Duration class for modelling it. It’s the most correct thing to do. Some use the word self-documenting about code written with such a consideration in mind.
The Duration.toSecondsPart method was only introduced in Java 9. If you are using Java 8 (or Java 6 or 7 with the ThreeTen Backport), get the seconds part by subtracting the minutes and then converting to seconds:
long minutes = dur.toMinutes();
dur = dur.minusMinutes(minutes);
return String.format("%02d:%02d", minutes, dur.toSeconds());
Then the result is the same.
PS If your seconds denoted a time-of-day instead, the correct and self-documenting solution would be to use the LocalTime class:
LocalTime chrono = LocalTime.ofSecondOfDay(seconds);
return String.format("%02d:%02d",
chrono.get(ChronoField.MINUTE_OF_DAY), chrono.getSecond());
This will reject seconds values greater than 86399 with an exception since the time of day cannot be 24:00 or greater.
Remove your hours from the formatting
public static String formatChronometer(long seconds) {
return String.format("%02d:%02d",
TimeUnit.SECONDS.toMinutes(seconds),
TimeUnit.SECONDS.toSeconds(seconds) % TimeUnit.MINUTES.toSeconds(1));
}

Convert Minutes to Hours using JodaTime

I'm having Minutes in java.lang.Long and want to convert this value to java.math.BigDecimal, ie. as Hours.
BigDecimal hours = BigDecimal.valueOf(minutes)
.divide(BigDecimal.valueOf(DateTimeConstants.MINUTES_PER_HOUR))
.setScale(2,RoundingMode.HALF_DOWN);
Tried the above method. It return hours, but no the way actually i want it
How i need is :
240 Minutes : 4 Hours
230 Minutes : 3.50 hours
Any help?
I would convert your Minutes to a Period object:
Minutes minutes = ...;
Long millisec = minutes*60*1000;
Period period = new Period(millisec);
Then use the Period object you can ask the Hours. Anything you want...
Note: 230 minutes is not 3.50 hours, it's 3.83 hours, i'm assuming you mean "3 hours and 50 minutes".
So what you want is the hh:mm representation.
You don't need BigDecimals. Use this:
long minutes = 230;
long hours = minutes / 60;
long minnutesRemaining = minutes % 60;
System.out.println(hours + "." + minnutesRemaining);
I'm betting the OP actually wants to convert minutes into hours and minutes. This is as easy as:
int minutes = 230;
System.out.println(
String.format("%d Minutes: %d:%02d Hours", minutes, (minutes/60), (minutes%60)));
Just printing the minutes divided by 60 (using integer arithmetic) and the modulo of minutes divided by 60 (formatted as two digits with leading zeros by the "%02d" format.
You can do this using BigDecimal easy. You can use divideAndRemainder()
long minutes = 230L;
BigDecimal min = new BigDecimal(minutes);
BigDecimal constant = new BigDecimal(60);
BigDecimal[] val=min.divideAndRemainder(constant);
System.out.println(val[0]+"."+val[1]+" Hours");
Out put:
3.50 Hours
I don't know in what universe 230 minutes equals 3.5 hours, so I'm afraid that some string manipulation is your best bet:
BigDecimal hours = new BigDecimal(
String.format("%d.%d", minutes / 60, minutes % 60));
Printing out the value of hours yields 3.50, as per your requirement.
Use integer and modulo arithmetic:
long hours = minutes / 60; /*implicit round-down*/
long numberAfterDecimal = (minutes % 1.0 /*pull out the remainder*/) * 60;
Then format these two numbers as you wish.

milliseconds to days

i did some research, but still can't find how to get the days... Here is what I got:
int seconds = (int) (milliseconds / 1000) % 60 ;
int minutes = (int) ((milliseconds / (1000*60)) % 60);
int hours = (int) ((milliseconds / (1000*60*60)) % 24);
int days = ????? ;
Please help, I suck at math, thank's.
For simple cases like this, TimeUnit should be used. TimeUnit usage is a bit more explicit about what is being represented and is also much easier to read and write when compared to doing all of the arithmetic calculations explicitly. For example, to calculate the number days from milliseconds, the following statement would work:
long days = TimeUnit.MILLISECONDS.toDays(milliseconds);
For cases more advanced, where more finely grained durations need to be represented in the context of working with time, an all encompassing and modern date/time API should be used. For JDK8+, java.time is now included (here are the tutorials and javadocs). For earlier versions of Java joda-time is a solid alternative.
If you don't have another time interval bigger than days:
int days = (int) (milliseconds / (1000*60*60*24));
If you have weeks too:
int days = (int) ((milliseconds / (1000*60*60*24)) % 7);
int weeks = (int) (milliseconds / (1000*60*60*24*7));
It's probably best to avoid using months and years if possible, as they don't have a well-defined fixed length. Strictly speaking neither do days: daylight saving means that days can have a length that is not 24 hours.
Go for TImeUnit in java
In order to import use, java.util.concurrent.TimeUnit
long millisec=System.currentTimeMillis();
long seconds=TimeUnit.MILLISECONDS.toSeconds(millisec);
long minutes=TimeUnit.MILLISECONDS.toMinutes(millisec);
long hours=TimeUnit.MILLISECONDS.toMinutes(millisec);
long days=TimeUnit.MILLISECONDS.toDays(millisec);
java.time
You can use java.time.Duration which is modelled on ISO-8601 standards and was introduced with Java-8 as part of JSR-310 implementation. With Java-9 some more convenient methods were introduced.
Demo:
import java.time.Duration;
public class Main {
public static void main(String[] args) {
// Duration between the two instants
Duration duration = Duration.ofMillis(1234567890L);
// Print Duration#toString
System.out.println(duration);
// Custom format
// ####################################Java-8####################################
String formattedElapsedTime = String.format(
"%d Day %02d Hour %02d Minute %02d Second %d Millisecond (%d Nanosecond)", duration.toDays(),
duration.toHours() % 24, duration.toMinutes() % 60, duration.toSeconds() % 60,
duration.toMillis() % 1000, duration.toNanos() % 1000000000L);
System.out.println(formattedElapsedTime);
// ##############################################################################
// ####################################Java-9####################################
formattedElapsedTime = String.format("%d Day %02d Hour %02d Minute %02d Second %d Millisecond (%d Nanosecond)",
duration.toDaysPart(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(),
duration.toMillisPart(), duration.toNanosPart());
System.out.println(formattedElapsedTime);
// ##############################################################################
}
}
A sample run:
PT342H56M7.89S
14 Day 06 Hour 56 Minute 07 Second 890 Millisecond (890000000 Nanosecond)
14 Day 06 Hour 56 Minute 07 Second 890 Millisecond (890000000 Nanosecond)
Learn more about the modern date-time API from Trail: Date Time.
int days = (int) (milliseconds / 86 400 000 )
public static final long SECOND_IN_MILLIS = 1000;
public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
You could cast int but I would recommend using long.
You can’t. Sorry. Or more precisely: you can if you know a time zone and a start time (or end time). A day may have a length of 23, 24 or 25 hours or some other length. So there isn’t any sure-fire formula for converting from milliseconds to days. So while you can safely rely on 1000 milliseconds in a second, 60 seconds in a minute (reservation below) and 60 minutes in an hour, the conversion to days needs more context in order to be sure and accurate.
Reservation: In real life a minute is occasionally 61 seconds because of a leap second. Not in Java. Java always counts a minute as 60 seconds because common computer clocks don’t know leap seconds. Common operating systems and Java itself do know not only summer time (DST) but also many other timeline anomalies that cause a day to be shorter or longer than 24 hours.
To demonstrate. I am writing this on March 29, 2021, the day after my time zone, Europe/Copenhagen, and the rest of the EU switched to summer time.
ZoneId myTimeZone = ZoneId.of("Europe/Copenhagen");
ZonedDateTime now = ZonedDateTime.now(myTimeZone);
ZonedDateTime twoDaysAgo = now.minusDays(2);
ZonedDateTime inTwoDays = now.plusDays(2);
System.out.println(ChronoUnit.MILLIS.between(twoDaysAgo, now));
System.out.println(ChronoUnit.MILLIS.between(now, inTwoDays));
Output:
169200000
172800000
So how many milliseconds are in two days depends on which two days you mean. And in which time zone.
So what to do?
If for your purpose you can safely define a day as 24 hours always, for example because your days are counted in UTC or your users are fine with the inaccuracy, use either Duration or TimeUnit. Since Java 9 the Duration class will additionally tell you how many hours, minutes and seconds there are in addition to the whole days. See the answer by Arvind Kumar Avinash. For the TimeUnit enum see the answers by whaley and Dev Parzival. In any case the good news is that it doesn’t matter if you suck at math because the math is taken care of for you.
If you know a time zone and a starting point, use ZonedDateTime and ChronoUnit.DAYS. In this case too the math is taken care of for you.
ZonedDateTime start = LocalDate.of(2021, Month.MARCH, 28).atStartOfDay(myTimeZone);
long millisToConvert = 170_000_000;
ZonedDateTime end = start.plus(millisToConvert, ChronoUnit.MILLIS);
long days = ChronoUnit.DAYS.between(start, end);
System.out.format("%d days%n", days);
2 days
If you additionally want the hours, minutes and seconds:
Duration remainingTime = Duration.between(start.plusDays(days), end);
System.out.format(" - and an additional %s hours %d minutes %d seconds%n",
remainingTime.toHours(),
remainingTime.toMinutesPart(),
remainingTime.toSecondsPart());
- and an additional 0 hours 13 minutes 20 seconds
If instead you had got an endpoint, subtract your milliseconds from the endpoint using the minus method (instead of the plus method used in the above code) to get the start point.
Under no circumstances do the math yourself as in the question and in the currently accepted answer. It’s error-prone and results in code that is hard to read. And if your reader sucks at math, he or she can spend much precious developer time trying to verify that you have done it correctly. Leave the math to proven library methods, and it will be much easier for your reader to trust that your code is correct.
In case you solve a more complex task of logging execution statistics in your code:
public void logExecutionMillis(LocalDateTime start, String callerMethodName) {
LocalDateTime end = getNow();
long difference = Duration.between(start, end).toMillis();
Logger logger = LoggerFactory.getLogger(ProfilerInterceptor.class);
long millisInDay = 1000 * 60 * 60 * 24;
long millisInHour = 1000 * 60 * 60;
long millisInMinute = 1000 * 60;
long millisInSecond = 1000;
long days = difference / millisInDay;
long daysDivisionResidueMillis = difference - days * millisInDay;
long hours = daysDivisionResidueMillis / millisInHour;
long hoursDivisionResidueMillis = daysDivisionResidueMillis - hours * millisInHour;
long minutes = hoursDivisionResidueMillis / millisInMinute;
long minutesDivisionResidueMillis = hoursDivisionResidueMillis - minutes * millisInMinute;
long seconds = minutesDivisionResidueMillis / millisInSecond;
long secondsDivisionResidueMillis = minutesDivisionResidueMillis - seconds * millisInSecond;
logger.info(
"\n************************************************************************\n"
+ callerMethodName
+ "() - "
+ difference
+ " millis ("
+ days
+ " d. "
+ hours
+ " h. "
+ minutes
+ " min. "
+ seconds
+ " sec."
+ secondsDivisionResidueMillis
+ " millis).");
}
P.S. Logger can be replaced with simple System.out.println() if you like.

Set the start of the task according to freqency and start of server

I have a frequency and a time range with which the frequency has to be executed.
For eg, if the current time is 2AM and frequency is 360 mins, it has to assume that
since it just passed midnight , the task should execute at 360 mins after 12 means 6 AM
the current time is server start time from midnight.
so if server starts at 5AM and frequncy is 360 the task should be run at 6AM
but if the task is 5am and frequency is 240, since it has already passed 240 mins from midnight
the next run should be at 8AM
If the server starts at 10AM and frequency is 300 then starting from midnight one run is elapsed at 5AM ans next as calculated at 10AM so will start immediately at 10AM
The time ranges are divded into 4 quarters, 12AM to 6AM to 12PM ,12PM to 6PM and 6PM to 12AM next day
This is the code below which is working fine for 240 and 360 frequency but going wrong when its 60 frequency.
Some values which are provided below:
stNow----serverTime
sElapsed ---time elapsed from midnight
currFreq ----frequecy
protected int _getInitWorkerTime()
{
int vRun=0;
STime stNow= new STime(PWMSystem.newDate());
int currFreq = _findAlertFrequency()/60;
int sElapsed =Constants.MINUTES_PER_DAY-stNow.elapsedMinutes(STime.midnight);
_log.error("now time as 24------"+stNow.getHourNo24());
_log.error("now currFreq------"+currFreq);
_log.error("now sElapsed-----"+sElapsed);
if(sElapsed == 1440)
sElapsed=0;
if(stNow.getHourNo24()>=0 && stNow.getHourNo24()<=6)
{
_log.error("now time as 24-inside cond-1-----"+stNow.getHourNo24());
if(currFreq>sElapsed)
vRun= currFreq-sElapsed;
else
vRun= -(currFreq-sElapsed);
}
if(stNow.getHourNo24()>6 && stNow.getHourNo24()<=12)
{
_log.error("now time as 24-inside cond-2-----"+stNow.getHourNo24());
if(currFreq>sElapsed)
vRun=360-(currFreq-sElapsed);
else
vRun=360-(-(currFreq-sElapsed));
}
if(stNow.getHourNo24()>12 && stNow.getHourNo24()<=18)
{
_log.error("now time as 24-inside cond-3-----"+stNow.getHourNo24());
if(currFreq>sElapsed)
vRun=720-(currFreq-sElapsed);
else
vRun=720-(-(currFreq-sElapsed));
}
if(stNow.getHourNo24()>18 && (stNow.getHourNo24()<=24 ||stNow.getHourNo24()<=0))
{
_log.error("now time as 24-inside cond-4-----"+stNow.getHourNo24());
if(currFreq>sElapsed)
vRun=1080-(currFreq-sElapsed);
else
vRun=1080-(-(currFreq-sElapsed));
}
// vRun=_MAX_FREQUENCY_DELAY_IN_SEC+ sElapsed*60+_findAlertFrequency()+_BOOT_DELAY_SECONDS_START;*/
//vRun=stNow.elapsedMinutes(STime.midnight)*60+_findAlertFrequency()+_BOOT_DELAY_SECONDS_START;
return (vRun*60 + _BOOT_DELAY_SECONDS_START);
}
Isn't it just a case of saying:
int nextTime = ((timeSinceMidnight / (period-1)) + 1) * period;
(Where the division is done with integer arithmetic. The "period-1" bit is to catch the situation where it's exactly on time, such as the third test case below.)
Sample situations:
Period Minutes since midnight Result
360 120 (2am) 360 (6am)
240 300 (5am) 480 (8am)
300 600 (10am) 600 (10am)
All looks correct to me. You mention dividing time ranges into quarters, but I don't see how that's desirable or relevant... aren't you really just talking about "minutes since midnight" in every case? Note that the way I've expressed it, you could change it to "seconds since midnight" or "milliseconds since midnight" - or even "minutes since the start of the month"; so long as you have a consistent origin and time unit, it should work out fine.
(Note that I've referred to period rather than frequency - normally a higher frequency means something happens more often, not less.)
I found out to be..
protected int _getInitWorkerTime()
{
int vRun = 0;
STime stNow = new STime(PWMSystem.newDate());
int currFreq = _findAlertFrequency() / Constants.SECONDS_PER_MINUTE;
int sElapsed = Constants.MINUTES_PER_DAY- stNow.elapsedMinutes(STime.midnight);
int runsCompleted = sElapsed / currFreq;
int remainLeft = (runsCompleted + 1) * currFreq;
if (remainLeft > sElapsed)
vRun = remainLeft - sElapsed;
else
vRun = sElapsed - remainLeft;
return (vRun * 60 + _BOOT_DELAY_SECONDS_START);

Categories

Resources