Format string with durations

Asked

Viewed 125 times

2

I have a time string with minutes, seconds and milliseconds. Example: 1:25.684

How can I format this String to a full hour format, filling with 0, to look like this:

00:01:25.684
String tempo = "1:25.684";
MaskFormatter f = new MaskFormatter("##:##:##.###");
String s = f.valueToString(tempo);

But the following error occurs:

java.text.Parseexception: Invalid Character

2 answers

1

I’m afraid you’ll have to do the Parsing and the formatting manually. As it was not clear which formats were possible, I tried to do it in a kind of "generic":

// método para separar segundos das frações de segundo
public int[] separarSegundosMs(String s) {
    String[] partes = s.split("\\.");
    if (partes.length == 1) { // não tem milissegundos
        return new int[] { Integer.parseInt(partes[0]), 0 };
    }
    return new int[] { Integer.parseInt(partes[0]), Integer.parseInt(partes[1]) };
}

...
String tempo = "1:25.684";
int horas = 0, minutos = 0, segundos = 0, fracoesSegundo = 0;
String[] partes = tempo.split(":");
int[] secs = null;
switch (partes.length) {
    case 1:// só segundos
        secs = separarSegundosMs(partes[0]);
        break;
    case 2:// só tem minutos e segundos
        minutos = Integer.parseInt(partes[0]);
        secs = separarSegundosMs(partes[1]);
        break;
    case 3:// tem horas, minutos e segundos
        horas = Integer.parseInt(partes[0]);
        minutos = Integer.parseInt(partes[1]);
        secs = separarSegundosMs(partes[2]);
        break;
}
if (secs != null) {
    segundos = secs[0];
    fracoesSegundo = secs[1];
}

String formatado = String.format("%02d:%02d:%02d.%d", horas, minutos, segundos, fracoesSegundo);
System.out.println(formatado); // 00:01:25.684

First I make one split separating the parts by :. Then I see how many parts there are String contains or does not contain hours, minutes, seconds, etc. For seconds and milliseconds, I used another split, separating by . (as split receives a regex and the point has special meaning in regex, I had to make the escape with \).

When formatting, I used the format %02d (uses two digits, filling with zero left if necessary). For the second fractions I only used %d, because if the entrance is 03:02.5, the fraction of a second is 5, then there should be no zeros left (otherwise the output would be 03:02.05, that would be a value different from the original).


Joda-Time

Another option, if you want to use an external library, is to use the Joda-Time. You can create a PeriodFormatter for cases that have hours and other for cases that do not have (and then you try to do the Parsing with one, and if not, try with another):

PeriodFormatter comHoras = new PeriodFormatterBuilder().minimumPrintedDigits(2)
    // horas
    .printZeroAlways().appendHours().appendSeparator(":")
    // minutos
    .appendMinutes().appendSeparator(":")
    // segundos
    .appendSeconds().appendSeparator(".")
    // frações de segundos
    .minimumPrintedDigits(0).appendMillis()
    // create formatter
    .toFormatter();
PeriodFormatter semmHoras = new PeriodFormatterBuilder()
    // minutos
    .appendMinutes().appendSeparator(":")
    // segundos
    .appendSeconds().appendSeparator(".")
    // frações de segundos
    .appendMillis()
    // create formatter
    .toFormatter();
Period period = null;
try {
    period = comHoras.parsePeriod(tempo);
} catch (IllegalArgumentException e) {
    period = semmHoras.parsePeriod(tempo);
}
if (period != null) {
    System.out.println(comHoras.print(period)); // 00:01:25.684
}

To print, I used the formwork with the hours, so all fields are printed even if they are zero. I also used minimumPrintedDigits(2) so that the values have zero left when necessary.

Remember that Joda-Time is a discontinued API (see more details here) and I only suggested because the standard Java API does not support Parsing and custom formatting of durations. By the way, this is the problem of another answer, which is treating the duration as if it were a date.


There is a difference between these two concepts. Consider the sentences below:

  • the movie starts at two o'clock
  • the film lasts two hours

In the first case, I am talking about a time (a specific time of the day). In the second case, I’m talking about a duration (a amount of time), but I’m not necessarily saying when the movie starts (it doesn’t actually say even if it will pass, it just says how long it lasts).

The problem is that we use the same words for both ("hours", "minutes", etc). But in your case, the string represents a duration, not a date or time.

The other answer suggests using Date and SimpleDateFormat, which are classes that serve to work with dates and times, but not with durations. Although sometimes "working", it is a crooked use of these classes and quite prone to errors (and nor did I enter the merit that the code returned there - before it is edited - 12:01:25.684, or that it will fail to last longer than 24 hours).

Native API supports durations from Java 8 in classes Duration and Period, but unfortunately they do not have methods ready to do Parsing and custom formatting, so the best options are still to do manually or use an external library.


Before anyone suggests this gambit, I will put here and alert to the problems:

LocalTime time = LocalTime.parse("00:0" + tempo);
System.out.println(time);

The class LocalTime (available from Java 8) represents a time, not a duration. Although it "works" for this specific case, it is a crooked use of it, for reasons already mentioned above. Also, it only supports values for the time between 0 and 23, so if you have a duration of 24 hours or more, it no longer works.

-1

You can try it like this.

In the output formatting was the fixed time value since you are only treating minutes and seconds so the time will always be 00.

    String input = "1:25.684";

    DateFormat formatacaoEntrada = new SimpleDateFormat("mm:ss.SSS");

    DateFormat formatacaoSaida = new SimpleDateFormat("00:mm:ss.SSS");

    String horaConvertida = null;

    try {

        Date entradaFormatadaComoData = formatacaoEntrada.parse(input);

        horaConvertida = formatacaoSaida.format(entradaFormatadaComoData);

        System.out.println(horaConvertida);

    } catch (ParseException pe) {
        pe.printStackTrace();
    }

Browser other questions tagged

You are not signed in. Login or sign up in order to post.