简体   繁体   中英

Java Time calculation

So my method receives a time in 24 hour format ("HH:MM:SS") and returns a string the difference time. If it's 2:00PM local time I should be able to send it "16:30:00"(4:30PM) and get the output "2 hours, 30 mins". But the code has some problem, and I am just a beginner and I need help to fix it.

The problem is if the time is 4:40PM, and I sent it "17:00:00"(5:00PM) it returns the message: 12 hours, 20 minutes instead of 0 hours, 20 minutes. The other problem is if I sent it the current time, it would return "12 hours" away, and not 24 like it should.

Please keep in mind I am only a beginner at java and math really isn't my thing, so any help is highly appreciated. Thanks.

private static String timeUntil(String distanceTime) {
String returnMsg = null;
try {
    SimpleDateFormat sdfDate = new SimpleDateFormat("hh:mm:ss");
    Date now = new Date();
    java.text.DateFormat df = new java.text.SimpleDateFormat("hh:mm:ss");
    Date date1 = df.parse(sdfDate.format(now));
    Date date2 = df.parse(distanceTime);
    long diff = date2.getTime() - date1.getTime();
    int timeInSeconds = (int) (diff / 1000);
    int hours, minutes;

    hours = timeInSeconds / 3600;
    timeInSeconds = timeInSeconds - (hours * 3600);
    minutes = timeInSeconds / 60;

    if (hours >= 0) {
    returnMsg = hours + " hours" +
            "\n" + minutes + " mins";
    } else {
    returnMsg = minutes + " mins";
    }
} catch (Exception e) {
    e.printStackTrace();
}
return returnMsg;
}

In your date format, hh is used for 12 -hour time. Use HH for 24 -hour time:

new SimpleDateFormat("HH:mm:ss");

Code review comments...

  1. Most of the Date class' methods are deprecated. You should consider using Calendar (GregorianCalendar) instead of Date.

  2. Your variable names often lack meaning - you have to know the purpose of the variable to know its meaning. Your code will be more maintainable if you use better variable names. df could be renamed "format" or "dateFormat".

  3. You create a Date object 'now', then you pass it through your df DateFormat instance, hten through your sdfDate instance, to convert it back to a Date. This is unnecessary. Replace this with Date date1 = new Date(); and remove Date now = new Date(); . Simiilarly, I've seen very difficult to diagnose errors when converting between units, so you should change diff to indicate that it's milliseconds, like diff_ms . And you should keep names consistent - you change from "different" ( diff ) to "timeInSeconds". They should both be "timeInXxx" or "diff_xx". the distanceTime parameter should be renamed something like futureTime

  4. You do a bunch of math to determine the difference between two times, but there are libraries that will do this for you. Google for "java difference between two dates" and find many answers.

  5. Your code always assumes that the second time occurs after "now". This should be included in a comment at the top of your method.

  6. When you instantiate date1 and date2, they're both probably for the same day. Try debugging or at least printing the objects to stdout immediately after they're created to see if this is the case. Is this what you really want?

  7. Your code doesn't handle leap year.

  8. Instead of handling all of the time conversions yourself, why don't you look for a library that does it for you?

This is your fixed code. I have changed the date format to HH:mm:ss and also your calculation logic. Try it and let us know

private static String timeUntil(String distanceTime) {
    String returnMsg = null;
    try {
        SimpleDateFormat sdfDate = new SimpleDateFormat("HH:mm:ss");
        Date now = new Date();
        java.text.DateFormat df = new java.text.SimpleDateFormat("HH:mm:ss");
        Date date1 = df.parse(sdfDate.format(now));
        Date date2 = df.parse(distanceTime);
        long diff = date2.getTime() - date1.getTime();
        int timeInSeconds = (int) (diff / 1000);
        int hours, minutes;

        hours = timeInSeconds / 3600;
        timeInSeconds = timeInSeconds - (hours * 3600);
        minutes = timeInSeconds / 60;

        if (hours != 0) {
        returnMsg = hours + " hours" +
                "\n" + minutes + " mins";
        } else {
        returnMsg = minutes + " mins";
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return returnMsg;
    }

Here is an example which is more robust and uses Java more modern date functions. I could go point by point and point at all the thing you could have done better in your example, but sometimes its easier to give you a good example and let you glean what you can from other people code as far as good style.

import java.util.Calendar;

public class testSpace {
     public static void main (String ... args){
            System.out.println(timeUntil("00:12:12"));      
    }

    private static String timeUntil(String distanceTime){

        String[] times = distanceTime.split(":");

        Calendar now = Calendar.getInstance();

        Calendar then = Calendar.getInstance();
        then.set(Calendar.SECOND, Integer.parseInt(times[2]));
        then.set(Calendar.MINUTE, Integer.parseInt(times[1]));
        then.set(Calendar.HOUR, Integer.parseInt(times[0]) % 12);
        then.set(Calendar.AM_PM,  (Integer.parseInt(times[0]) >= 12 ) ? Calendar.PM : Calendar.AM);

        boolean isFuture = (then.getTimeInMillis() > now.getTimeInMillis());

        long interval = (isFuture)
            ? then.getTimeInMillis() - now.getTimeInMillis()
            : now.getTimeInMillis() - then.getTimeInMillis();

        return ((isFuture) ? "" : "-") + millToTime(interval);
    }

    public static long MILLISECOND_PER_HOUR = 1000*60*60;
    public static long MILLISECOND_PER_MIN = 1000*60;
    public static long MILLISECOND_PER_SECOND = 1000;

    public static String millToTime(long mill){
        long hours  = mill / MILLISECOND_PER_HOUR;
        long mins   = (mill % MILLISECOND_PER_HOUR) / MILLISECOND_PER_MIN;
        long sec    = ((mill % MILLISECOND_PER_HOUR) % MILLISECOND_PER_MIN) / MILLISECOND_PER_SECOND;
        return String.format("%d:%d:%d", hours, mins, sec);
    }
}

tl;dr

For time-of-day only, without a date or time zone.

LocalTime start = LocalTime.of( "16:40" ) ;
LocalTime stop = LocalTime.of( "17:00" ) ;
Duration d = Duration.between( start , stop ) ;

PT20M

Or, for date-time in a zone.

ZoneId z = ZoneId.of( "America/Montreal" )
ZonedDateTime zdtNow = ZonedDateTime.now( z );
ZonedDateTime zdtThen = 
    ZonedDateTime.of(   // Pass a LocalDate, LocalTime, ZoneId.
        zdtNow.toLocalDate() ,  // Same date…
        LocalTime.parse( "16:30:00" ) ,  // … but different time-of-day.
        z  
    ) 
Duration d = Duration.between( zdtNow , zdtThen ) ;

PT2H30M

Details

You are using old date-time classes, now legacy, supplanted by the java.time classes.

LocalTime

You are incorrectly using a date-time class for a time-of-day-only value. Instead use the LocalTime class. And use a span-of-time class when calculating elapsed time.

String input = "16:30:00" ;
LocalTime lt = LocalTime.parse( input );

Instant

The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instant = Instant.now();

ZonedDateTime

Determining a wall-clock time requires a time zone. Specify a proper time zone name in the format of continent/region , such as America/Montreal , Africa/Casablanca , or Pacific/Auckland . Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtNow = instant.atZone( z );  // Adjusted into your time zone.

Now construct a ZonedDateTime for your given input time-of-day.

ZonedDateTime zdtTarget = ZonedDateTime.of( zdtNow.toLocalDate() , lt , z );

Duration

Use Duration to represent elapsed time not attached to the timeline.

Duration d = Duration.between( zdtNow , zdtTarget );

Note that the duration will be a negative amount if the specified time-of-day is earlier than the current time-of-day.

ISO 8601 string for duration

To get a String describing the hours, minutes, etc. of that span of time, simply call toString to generate a String in standard ISO 8601 format of PnYnMnDTnHnMnS where P marks the beginning and T separates the years-month-days from hours-minutes-seconds.

If the current time were 14:00:00 in the same zone, the output would be:

PT2H30M

Getter methods

Oddly, in Java 8 this class Duration lacks any getter methods for the parts such as 2 for hours and 30 for minutes. Remedied in Java 9 with methods such as toHoursPart and toMinutesPart .


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat .

The Joda-Time project, now in maintenance mode , advises migration to java.time.

To learn more, see the Oracle Tutorial . And search Stack Overflow for many examples and explanations. Specification is JSR 310 .

Where to obtain the java.time classes?

  • Java SE 8 and SE 9 and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport .
  • Android

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM