3

I have the following method to sum time:

public static String sumTime(String date1, String date2) throws ParseException {
    Calendar calendar = Calendar.getInstance();
    SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss.SSS");

    Date d1 = formatter.parse(date1);
    Date d2 = formatter.parse(date2);
    calendar.setTime(d2);

    d1 = DateUtils.addHours(d1, calendar.get(Calendar.HOUR_OF_DAY));
    d1 = DateUtils.addMinutes(d1, calendar.get(Calendar.MINUTE));
    d1 = DateUtils.addSeconds(d1, calendar.get(Calendar.SECOND));
    d1 = DateUtils.addMilliseconds(d1, calendar.get(Calendar.MILLISECOND));

    return formatter.format(d1);
}

DateUtils is from Apache Commons Lang 3
It works quite well for what I want, unless the sum is bigger than 24 hours.


For example:

String time = "00:00:00.000";

try {
    for (int i = 0; i < 24; i++) {
            time = sumTime(time, "01:00:00.123");
    }

    System.out.println(time);
} catch (ParseException e) {
    e.printStackTrace();
}

The result is:

00:00:02.952

But this is what I'd like it to be:

24:00:02.952


Is there any (easy) way to accomplish that?
I don't mind using different libraries/methods, as long as I get the correct result.


Keep in mind that time will always start in 00:00:00.000;

ReX
  • 88
  • 2
  • 7

4 Answers4

1

Have you thought about using days to represent each set of 24 hours? You could add something in your sumTime method, and have it add days. SimpleDateFormater can use days, maybe this will help:

http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html

BlackHatSamurai
  • 23,275
  • 22
  • 95
  • 156
1

java.util.Date is not so strong in this area. See the Joda Time for a library that handles this properly.

I don't have access to an installation just now. The code will be close to this:

DateTimeFormatter dtf = DateTimeFormat.forPattern("HH:mm:ss.SSS");
DateTime start = dtf.parseDateTime(date1);
DateTime end = dtf.parseDateTime(date2);
PeriodFormatter pf = new PeriodFormatterBuilder()
 .printZeroAlways().appendHours().appendSeparator(":")
 .appendMinutes().appendSeparator(":")
 .appendSeconds().appendSeparator(":")
 .appendMillis3Digit().toFormatter();
return pf.print(new Period(start, end, PeriodType.time()));
Gene
  • 46,253
  • 4
  • 58
  • 96
  • Do you know for sure if that can be done using that library? If yes, could you tell me where to start looking? – ReX Jul 06 '12 at 02:18
  • You probably need MutablePeriod - you can start there - http://joda-time.sourceforge.net/api-release/index.html?org/joda/time/MutablePeriod.html – Chip Jul 06 '12 at 03:09
  • I tested this and worked the same way I started with. I may try to tackle this again with Joda some other time, but I'm gonna go with Chip's solution. Thanks for all the help, everyone. – ReX Jul 06 '12 at 04:04
  • Sorry I did not specify that the `Period()` needed to be type `Time`. That's fixed. Without this the hours wrap back to zero and days are incremented. Certainly Chip's solution is fine as long as you can handle the testing costs and you aren't anticipating a more complex requirement in the future. – Gene Jul 06 '12 at 15:25
  • ReX, I absolutely agree with @Gene. I would recommend going with Joda-time over my solution any day. Always use libraries that are used/debugged by thousands of people every day. I mention this in my answer. So, if Gene's code works, you should use that. – Chip Jul 10 '12 at 00:36
0

Date is not the right thing class to use. Date is a instant of time, not a "Date Difference".

The right thing to do will be to use a library like Joda Time as someone has already suggested. If you don't want to do so - here's a possible alternative:

Parse the string into hours, minutes and seconds yourself, and then add it yourself.

I would encourage you to look into a "well accepted" library though. There may be things I'm not thinking of in my solution. Also, you have add all the error checking.

Here's the starter code:

public class TimeInterval {
short milliSeconds;
short seconds;
short minutes;
int hours;

public TimeInterval (String dateString) {
    // HHHHHH:MI:SS.SSS
    Pattern pattern = Pattern.compile("(\\d+):(\\d\\d):(\\d\\d)\\.(\\d\\d\\d)");
    Matcher matcher = pattern.matcher(dateString);
    if ( matcher.find() ) {
        hours = Integer.parseInt(dateString.substring(matcher.start(1), matcher.end(1)));
        minutes = Short.parseShort(dateString.substring(matcher.start(2), matcher.end(2)));
        seconds = Short.parseShort(dateString.substring(matcher.start(3), matcher.end(3)));
        milliSeconds = Short.parseShort(dateString.substring(matcher.start(4), matcher.end(4)));
    }
}

private TimeInterval() {
}

public TimeInterval add(TimeInterval interval) {
    TimeInterval ret = new TimeInterval();
    ret.milliSeconds = (short) ((interval.milliSeconds + milliSeconds)%1000);
    int carry = (interval.milliSeconds + milliSeconds)/1000;
    ret.seconds = (short) ((interval.seconds + seconds)%60 + carry );
    carry =(interval.seconds + seconds)/60;
    ret.minutes = (short) ((interval.minutes + minutes)%60 + carry);
    carry = (interval.minutes + minutes)/60;
    ret.hours = (interval.hours + hours + carry);
    return ret;
}

@Override
public String toString() {
    return String.format("%d:%02d:%02d.%03d", hours, minutes, seconds, milliSeconds);
}
}

Using this class your program will be like :

TimeInterval time = new TimeInterval("00:00:00.000");

try {
    for (int i = 0; i < 24; i++) {
      time = time.add(new TimeInterval("01:00:00.123"));
    }

    System.out.println(time.toString());
} catch (ParseException e) {
    e.printStackTrace();
}
Chip
  • 3,226
  • 23
  • 31
  • Your solution works great, thank you so much. I may try to go with Joda Time next time, but I didn't have any luck with it this time. – ReX Jul 06 '12 at 03:28
0

Have you tried Joda-Time which actually has direct support for this sort of thing?

    PeriodFormatterBuilder builder = new PeriodFormatterBuilder();
    builder.printZeroAlways()
        .minimumPrintedDigits(2)
        .appendHours()
        .appendSeparator(":").appendMinutes()
        .appendSeparator(":").appendSeconds()
        .appendSeparator(".").appendMillis3Digit();

    PeriodFormatter formatter = builder.toFormatter();
    PeriodParser parser = builder.toParser();

    String s1 = "11:00:00.111";
    String s2 = "23:00:00.111";

    MutablePeriod p1 = new MutablePeriod();
    MutablePeriod p2 = new MutablePeriod();
    parser.parseInto(p1, s1, 0, Locale.getDefault());       
    parser.parseInto(p2, s2, 0, Locale.getDefault());       
    p1.add(p2);


    System.out.println(formatter.print(p1));

Prints

34:00:00.222
Matt
  • 11,523
  • 2
  • 23
  • 33
  • There's a problem with this, if I change the values of both milliseconds to 500, "34:00:00.1000" gets printed. – ReX Jul 06 '12 at 03:42