Loss of precision - Java - java

Problem Statement:
Write a method whatTime, which takes an int, seconds, representing the number of seconds since midnight on some day, and returns a String formatted as "::". Here, represents the number of complete hours since midnight, represents the number of complete minutes since the last complete hour ended, and represents the number of seconds since the last complete minute ended. Each of , , and should be an integer, with no extra leading 0's. Thus, if seconds is 0, you should return "0:0:0", while if seconds is 3661, you should return "1:1:1"
My Algorithm:
Here is how my algorithm is supposed to work for the input 3661:
3661/3600 = 1.016944 -> This means the number of hours is 1
Subtract the total number of hours elapsed i.e. 1.016944-1=0.016944
Multiply this with 60 i.e. 0.016944*60=1.016666 -> The number of minutes elapsed is equal to 1
Subtract the total number of minutes elapsed i.e. 1.01666-1=0.01666. Multiply this with 60. This would yield the number of seconds elapsed.
The output produced however is 1:1:0. I tried to use a print statement and it appears that the value of 'answer3' variable is 0.999 and that is why prints the integer part (0). I tried to use the Math.ceil() function to round up the value and it produces a correct output. However I can only score about 60/250 points when I submit my code (TopCoder SRM 144 Div2) . Any insight for improving the algorithm will be helpful.
public class Time
{
public String whatTime(int seconds)
{
double answer1,answer2,answer3; int H;
answer1=(double)seconds/3600;
H=(int)answer1;
answer2=(double)((answer1-H)*60);
int M=(int)answer2;
answer3=answer2-M;
answer3=(double)answer3*60;
int S=(int)answer3;
String answer=Integer.toString(H);
answer=Integer.toString(H)+":"+Integer.toString(M)+":"+Integer.toString(S);
return answer;
}
}

public String whatTime(int seconds) {
int secondVal = seconds % 60;
int minutes = seconds / 60;
int minuteVal = minutes % 60;
int hours = minutes / 60;
int hourVal = hours % 24;
int daysVal = hours / 24;
String answer = "" + daysVal + ":" + hourVal + ":" + minuteVal + ":" + secondVal;
return answer;
}
Could do the formatting more elegantly, but that's the basic idea.

Avoid floating point values, and work entirely with ints or longs.

You could solve this by working with ints :
3661/3600 = 1.016944 -> This means the number of hours is 1
Subtract the number of hours * 3600 - i.e. 3661-(1*3600) = 61
61/60 = 1.0166666 -> The number of minutes elapsed is equal to 1
Subtract the number of minutes * 60 i.e. 61-(1*60)=1. This yields the number of seconds elapsed.

Related

Generate random time within a range of two times with condition

I'm trying to generate 10 random times within a range of two times, and there is a condition that the times generated cannot have less than 30 minutes between them. So, if i Start at 10:00am and end at 05:00pm, the times between these must be a least 30 minutes between them.
I already can get the random times, but don't know how to put the condition there, any ideas?
public LocalTime between(LocalTime startTime, LocalTime endTime) {
int startSeconds = startTime.toSecondOfDay();
int endSeconds = endTime.toSecondOfDay();
int randomTime = ThreadLocalRandom
.current()
.nextInt(startSeconds, endSeconds);
return LocalTime.ofSecondOfDay(randomTime);
}
i put this in a for loop to get 10 of them
For a good random distribution: Out of the 7 hours between your start of 10:00 and your end of 17:00 (on a 24 hour clock, “military hours”), 4 hours 30 minutes are already reserved for your minimal gaps (9 gaps # 30 minutes minimum). So subtract 4:30 from 7, this gives 2 hours 30 minutes of freedom.
Generate 10 random times within 2 hours 30 minutes, for example the way you already do.
Sort them chronologically.
Add 0 minutes to the first time, 30 minutes to the next, 1 hour to the third, etc. So you will be adding 4 hours 30 minutes to the last time. This will make sure that the gaps become at least 30 minutes each and that the last time is still within the 17:00 end time.
You can use isBefore in LocalTime, so check startTime+30 Min is before result and result is before endTime-30 Min
LocalTime result = LocalTime.ofSecondOfDay(randomTime);
if (startTime.plusMinutes(30).isBefore(result) && result.isBefore(endTime.minusMinutes(30))) {
return result;
}
Probably you can use while loop to loop until it get the valid result
while(true) {
LocalTime result = LocalTime.ofSecondOfDay(ThreadLocalRandom. current().nextInt(startSeconds, endSeconds));
if (startTime.plusMinutes(30).isBefore(result) && result.isBefore(endTime.minusMinutes(30))) {
return result;
}
}
You can not generate truly random numbers with computers, you always have to have some strategy. And if your numbers are truly random then, they can not be aware of each other. So, you can not generate truly random time 30 minutes apart separately. You have to generate them all together. You have to find a minimum distance. And start time and end time must have enough distance to generate the number of random times you want.
You can generate any number of random times by the following method in any given range if they have enough distance -
public List<LocalTime> generateRandomTimes(LocalTime startTime, LocalTime endTime, int n) {
if (n < 0) {
throw new RuntimeException("Must be greater than zero");
}
List<LocalTime> localTimeList = new ArrayList<>();
int startSeconds = startTime.toSecondOfDay();
int endSeconds = endTime.toSecondOfDay();
int minimumDistance = LocalTime.of(0, 30).toSecondOfDay();
int standardDistance = (endSeconds - startSeconds) / n;
if (standardDistance <= minimumDistance) {
throw new RuntimeException("Not enough time distance to generate the required number of random times");
}
int randomRange = (endSeconds - (n * minimumDistance)) / n;
for (int i = 1; i <= 10; i++) {
int nextInt = ThreadLocalRandom
.current()
.nextInt(startSeconds, startSeconds + randomRange);
LocalTime time = LocalTime.ofSecondOfDay(nextInt);
localTimeList.add(time);
startSeconds = nextInt + minimumDistance;
}
return localTimeList;
}

Time Difference separating the times

Complete timeDifference that takes two different times and returns a
string with the difference in hours and minutes, separated by ":".
The int argument 0 represents midnight, 700 is 7:00 a.m., 1314 is 14
minutes past 1:00pm, and 2200 is 10 pm.
Leading zeros required
I know the problem requires you to convert both times to minutes, however I don't know how to separate the integers that are four characters long so I can differ between hours and minutes.
Sice your question is on the separation part only, this will do the trick:
int timeDifference(int a, int b)
{
int minsA = a % 100); //remainder
int hrsA = (a / 100);
.....
}
Edit: if you want to get the full time just in minutes you can do:
int fullMinsA = minsA + hrsA*60;

Calculate difference between two hour's strings

I have a string that contain certain hour ex. 14:34, and now I want to calculate the difference between the current hour ex. 21:36-14:34=7 hours 2 minutes (or something like that.) Can someone explain me how can I do that?
It's very easy: You need to separate the string in terms you can add or substract:
String timeString1="12:34";
String timeString2="06:31";
String[] fractions1=timeString1.split(":");
String[] fractions2=timeString2.split(":");
Integer hours1=Integer.parseInt(fractions1[0]);
Integer hours2=Integer.parseInt(fractions2[0]);
Integer minutes1=Integer.parseInt(fractions1[1]);
Integer minutes2=Integer.parseInt(fractions2[1]);
int hourDiff=hours1-hours2;
int minutesDiff=minutes1-minutes2;
if (minutesDiff < 0) {
minutesDiff = 60 + minutesDiff;
hourDiff--;
}
if (hourDiff < 0) {
hourDiff = 24 + hourDiff ;
}
System.out.println("There are " + hourDiff + " and " + minutesDiff + " of difference");
UPDATE:
I'm rereading my answer and I'm surprised is not downvoted. My fault. I wrote it without any IDE check. So, the answer should be minutes1 and 2 for the minutesDiff and obviously and a check to carry the hour difference if the rest of minutes is negative, making minutes (60+minutesDiff). If minutes is negative, rest another hour to the hourDiff. If hours become negative too, make it (24+hourDiff). Now is fixed.
For the sake of fastness, I'm using a custom function. For the sake of scalability, read Nikola Despotoski answer and complete it with this:
System.out.print(Hours.hoursBetween(dt1, dt2).getHours() % 24 + " hours, ");
System.out.println(Minutes.minutesBetween(dt1, dt2).getMinutes() % 60 + " minutes, ");
I would start by using the .split method to get the string into its two components (minutes and hours) then I would convert both times into minutes by mutliplying the hours by 60 and then adding the minutes
String s = "14:34";
String[] sArr = s.split(",");
int time = Integer.parseInt(sArr[0]);
time *= 60;
int time2 = Integer.parseInt(sArr[1]);
time = time + time2;
do this for both strings and then subtract one from the other. You can convert back to normal time by using something like this
int hours = 60/time;
int minutes = 60%time;
The answer labeled as correct will not work. It does not account for if the first time is for example 3:17 and the second is 2:25. You end up with 1 hour and -8 minutes!

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);

JodaTime Calculate total hours worked in a week

Currently I have a function which can take the start time and end time of one day, and calculate the difference between the two, giving me the hours worked in a day. What I would like to do is be able to get the hours worked for 7 days, and return a grand total, while remaining with the display format (HH:mm).
My function for a single day's total:
Period p = new Period(this.startTime[dayIndex], this.endTime[dayIndex]);
long hours = p.getHours();
long minutes = p.getMinutes();
String format = String.format("%%0%dd", 2);//Ensures that the minutes will always display as two digits.
return Long.toString(hours)+":"+String.format(format, minutes);
this.startTime[] & this.endTime[] are both arrays of DateTime objects.
Any suggestions?
You'll need something to hold a week's worth of days, and call your function once for each day.
But that means you'll want to refactor so that your calculator method doesn't format as a string, but instead returns a numeric value, so you can easily add them together.
Another simple solution:
Here is a method that receives separate the hours and minutes.The parameters are:
Start Hour
Start Minutes
End Hour
End Minutes
first, calculate the difference between hours and minutes separate:
int hours = pEndHour - pStartHour;
int minutes = ((60 - pStartMinutes) + pEndMinutes) - 60;
then, validates if the value of "minutes" variable is negative:
// If so, the "negative" value of minutes is our remnant to the next hour
if (minutes < 0) {
hours--;
minutes = 60 + minutes ;
}
Finally you can print the period of time in the hour format:
String format = String.format("%%0%dd", 2);
System.out.println( "*** " + hours + " : " + minutes);
That's all.
Solution I ended with for those interested
Period[] p=new Period[7];
long hours = 0;
long minutes =0;
for(int x=0; x<=this.daysEntered;x++)
{
p[x] = new Period(this.startTime[x], this.endTime[x]);
hours += p[x].getHours();
minutes += p[x].getMinutes();
}
hours += minutes/60;
minutes=minutes%60;
String format = String.format("%%0%dd", 2);
return Long.toString(hours)+":"+String.format(format, minutes);

Categories

Resources