1
\$\begingroup\$

My logic is as follow:

  1. Convert actual time to minutes.
  2. Extract seconds from minutes (30:04 -> 4 seconds).
  3. Subtract seconds value from minutes (step1-step2) to get time only in seconds.
  4. Convert time in minutes (step 3 output/60) to hours.

Now suppose i got time as 5404 which corresponds to 1:30:04. When I divide 5404, I get value as 90.0666666667. 0666666667 is actually 4 seconds to get value as 4 seconds. I convert the data to 60 unites by using percentage formula i.e

value * 60 / 100

I know there is a better solution here.

import java.math.BigDecimal;
import java.math.RoundingMode;

public class TimeCalc {

    public static void main(String[] args) {

        // Inital Time in seconds
        BigDecimal actualTime = new BigDecimal(5404);

        // 60 seconds
        BigDecimal seconds = new BigDecimal(60);

        // dividing original time to get time in mintues
        BigDecimal timeinMinutes = actualTime.divide(seconds, 2, RoundingMode.CEILING);

        // to get value of seconds from minutes
        double secondsValue = timeinMinutes.doubleValue() - Math.floor(timeinMinutes.doubleValue());

        // Seconds will be removed from Minutes to get only Minutes value
        BigDecimal deductiableSecondValue = new BigDecimal(secondsValue);
        timeinMinutes = timeinMinutes.subtract(deductiableSecondValue);
        timeinMinutes = new BigDecimal(Math.ceil(timeinMinutes.doubleValue()));


        // dividing minutes by 60 to get actual time in hours
        BigDecimal timeinHours = timeinMinutes.divide(seconds, 2, RoundingMode.CEILING);
        double hoursdecimalValue = timeinHours.doubleValue();

        // suppose time is 1.30 , below code is to get the 30 minutes
        double minutes = hoursdecimalValue - Math.floor(hoursdecimalValue);

        // converting value in units of Time since it is always measured wrt 60 
        minutes = convertDateinTimeUnit(minutes);

        // suppose time is 1.30 below code is get 1 value
        String str = new Double(hoursdecimalValue).toString();
        str = str.substring(0, str.indexOf('.'));
        double v = Double.valueOf(str);

        // converting value in units of Time since it is always measured wrt 60 
        secondsValue = convertDateinTimeUnit(secondsValue);

        secondsValue = Math.ceil(secondsValue);

        minutes = Math.ceil(minutes);

        System.out.println(" secondsValue " + secondsValue);
        System.out.println(" minutes decimal value " + minutes);
        System.out.println(" hours " + v);

    }

    static double convertDateinTimeUnit(double value) {
        double valueInTimeUnit = 60 * value / 100;
        return valueInTimeUnit * 100;

    }

}
\$\endgroup\$
  • 1
    \$\begingroup\$ If you know the other solution you link to is better, why knowingly implement one that is worse? \$\endgroup\$ – rolfl Jul 16 '15 at 13:19
  • \$\begingroup\$ I just want feedback on my level of thinking \$\endgroup\$ – VIckyb Jul 16 '15 at 13:26
4
\$\begingroup\$
        // Inital Time in seconds
        BigDecimal actualTime = new BigDecimal(5404);

Why store this in a BigDecimal? First, you aren't including a fractional part. Second, it's just not that big. The thirty-one bits of an int is enough for almost seventy years worth of seconds. Sixty-three bits (long) is enough for over two hundred billion years of seconds. You're only producing hours as output. If you need more than twenty bits, you should probably add at least days if not weeks.

        BigDecimal seconds = new BigDecimal(60);

This seems like a constant, which you would probably define outside the function.

         private final static int SECONDS_PER_MINUTE = 60;

I also find the name misleading. When I see seconds, I don't think that there are sixty seconds in a minute. I think that this is the seconds portion of the time.

Don't put everything in main

You have more logic in your main method than you should. Part of what makes the other solution better is that it only has two lines in its main method. As a general rule, you want to put all your programming logic in other methods. The main method should just handle input, output, and calling the methods that do the real work.

Why do floating point operations?

        // dividing original time to get time in mintues
        BigDecimal timeinMinutes = actualTime.divide(seconds, 2, RoundingMode.CEILING);

        // to get value of seconds from minutes
        double secondsValue = timeinMinutes.doubleValue() - Math.floor(timeinMinutes.doubleValue());

You don't need floating point operations here. You're essentially approximating fast integer operations with slower floating point operations.

        int seconds = actualTime;
        int minutes = seconds / SECONDS_PER_MINUTE;
        seconds %= SECONDS_PER_MINUTE;

This also gets rid of all the extraneous doubleValue() and Math.floor calls.

Note that the use of doubleValue reinforces the point that you don't need BigDecimal. If all your values fit into a double without loss of precision, why put them into a BigDecimal?

This may work. But I would be worried that the rounding would sometimes cause the wrong value. Particularly since two decimal digits make a repeating fractional part in binary. Perhaps there's no value that makes this not work, but why take that chance?

Don't repurpose constants by value

        // dividing minutes by 60 to get actual time in hours
        BigDecimal timeinHours = timeinMinutes.divide(seconds, 2, RoundingMode.CEILING);

This is just horrible. It would be better to just say 60 than reuse seconds to mean the number of minutes in an hour.

Eek

        // suppose time is 1.30 , below code is to get the 30 minutes

If the hours are 1.30, then that .30 is not 30 minutes. It's 18 minutes.

        double minutes = hoursdecimalValue - Math.floor(hoursdecimalValue);

        // converting value in units of Time since it is always measured wrt 60 
        minutes = convertDateinTimeUnit(minutes);

and

    static double convertDateinTimeUnit(double value) {
        double valueInTimeUnit = 60 * value / 100;
        return valueInTimeUnit * 100;

    }

Other than potentially odd rounding effects, the net effect of this method is to multiply value by 60. I'm not sure what you think is happening here, but you are just multiplying by sixty (with some loss of precision).

A side issue is that this only works for seconds and minutes expressed as fractions of minutes and hours respectively. I'm not sure why you mention "Date" at all.

The camelCase standard has you capitalizing every word after the first, so this would more normally be written convertDateInTimeUnit.

Use Math.floor to truncate

        // suppose time is 1.30 below code is get 1 value
        String str = new Double(hoursdecimalValue).toString();
        str = str.substring(0, str.indexOf('.'));
        double v = Double.valueOf(str);

You could have just said

        double hours = Math.floor(hoursDecimalValue.doubleValue());

and saved the extra overhead of converting to and from a string. Especially since you were already using Math.floor to calculate this value.

You're essentially reinventing

        int hours = minutes / MINUTES_PER_HOUR;
        minutes %= MINUTES_PER_HOUR;

Only you're using a lot more code that only approximates these values. If you want the integer mathematics behavior, why not just work with integers?

Binary division

When you store .1 as a binary value, it gets store as a series of bits. Decimal 10 is 1010 in binary. So

$$1 / 1010_2 = .0001100110011...$$

Since it repeats, it means that it may not convert back perfectly. For example, if you round to four binary digits, it is .0001 or .0010 (depending on whether you round down or up). The first of those is one sixteenth and the latter is one eighth. If you round to five binary digits, it is .00011, which is exactly 3/32.

Anyway, take .1 and .2, convert to binary rounded to four digits (round up if the next digit is a 1), and then add them. So .0010 + .0011 = .0101, which is 5/16 or .3125. You're already off by an eightieth. You could be off less with more digits, but you'll still be off by some small amount.

When you do all your roundabout math operations, these errors are building (and occasionally dropping). I would be concerned that you will occasionally get the wrong answer if you pick a value where it builds errors more than it drops them.

\$\endgroup\$
  • \$\begingroup\$ Every neat explanation ? but i cannot use int (example you have used it for minutes), since i need decimal values \$\endgroup\$ – VIckyb Jul 21 '15 at 10:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.