How to convert HH:mm:ss.SSS to milliseconds?
Asked Answered
R

6

25

I have a String 00:01:30.500 which is equivalent to 90500 milliseconds. I tried using SimpleDateFormat which give milliseconds including current date. I just need that String representation to milliseconds. Do I have to write custom method, which will split and calculate milliseconds? or Is there any other way to do this? Thanks.

I have tried as follows:

        String startAfter = "00:01:30.555";
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
        Date date = dateFormat.parse(startAfter);
        System.out.println(date.getTime());
Retrogression answered 11/1, 2012 at 20:39 Comment(1)
I take 00:01:30.500 to mean a duration, an amount of time, 1 minute 30.5 seconds. If so Date and SimpleDateFormat are the wrong classes to use. A duration is not a date. Also I recommend you never use those classes. They are poorly designed and long outdated, the latter in particular notoriously troublesome. Use java.time, the modern Java date and time API.Omor
K
41

You can use SimpleDateFormat to do it. You just have to know 2 things.

  1. All dates are internally represented in UTC
  2. .getTime() returns the number of milliseconds since 1970-01-01 00:00:00 UTC.
package se.wederbrand.milliseconds;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class Main {        
    public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

        String inputString = "00:01:30.500";

        Date date = sdf.parse("1970-01-01 " + inputString);
        System.out.println("in milliseconds: " + date.getTime());        
    }
}
Kola answered 11/1, 2012 at 20:49 Comment(4)
No need to prepend yyyy-MM-dd and 1970-01-01. Parsing as the OP did works if the timezone is set to GMT.Protraction
@JBNizet true and a good point. I deliberately included the extra date information because I think it better shows what's really going on.Kola
@AndreasWederbrand: The "trick" that makes it work here is to set the day to 1st January 1970. But Suppose If I don't give this day, then will it work?Katekatee
@bks, if you don't set 1970-01-01 you'll end up with way too many milliseconds for this question.Kola
T
6

If you want to parse the format yourself you could do it easily with a regex such as

private static Pattern pattern = Pattern.compile("(\\d{2}):(\\d{2}):(\\d{2}).(\\d{3})");

public static long dateParseRegExp(String period) {
    Matcher matcher = pattern.matcher(period);
    if (matcher.matches()) {
        return Long.parseLong(matcher.group(1)) * 3600000L 
            + Long.parseLong(matcher.group(2)) * 60000 
            + Long.parseLong(matcher.group(3)) * 1000 
            + Long.parseLong(matcher.group(4)); 
    } else {
        throw new IllegalArgumentException("Invalid format " + period);
    }
}

However, this parsing is quite lenient and would accept 99:99:99.999 and just let the values overflow. This could be a drawback or a feature.

Twoway answered 11/1, 2012 at 21:18 Comment(5)
Hey I have used your approach but it gives the result with an added - character between, is this due to L you have used with 3600000Miler
Probably not, the L is to make it a long. Which part gives the result with a - ? The dateParseRegExp returns a long which does not have a - (unless it is negative). Is it one of the matching groups that has the - ?Falconiform
OOPS not because of L my bad.Miler
"(\\d{2}):(\\d{2}):(\\d{2}).(\\d[0-9])" this won't fail if last number is 2 digitsClericals
@Clericals But it will give wrong answer for the digits. What are you trying to accomplish? If you need a solution for variable numbers of decimals, please post as a new question.Falconiform
S
3

Using JODA:

PeriodFormatter periodFormat = new PeriodFormatterBuilder()
  .minimumParsedDigits(2)
  .appendHour() // 2 digits minimum
  .appendSeparator(":")
  .minimumParsedDigits(2)
  .appendMinute() // 2 digits minimum
  .appendSeparator(":")
  .minimumParsedDigits(2)
  .appendSecond()
  .appendSeparator(".")
  .appendMillis3Digit()
  .toFormatter();
Period result = Period.parse(string, periodFormat);
return result.toStandardDuration().getMillis();
Sacrilege answered 11/1, 2012 at 20:50 Comment(0)
O
2

If you want to use SimpleDateFormat, you could write:

private final SimpleDateFormat sdf =
    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    { sdf.setTimeZone(TimeZone.getTimeZone("GMT")); }

private long parseTimeToMillis(final String time) throws ParseException
    { return sdf.parse("1970-01-01 " + time).getTime(); }

But a custom method would be much more efficient. SimpleDateFormat, because of all its calendar support, time-zone support, daylight-savings-time support, and so on, is pretty slow. The slowness is worth it if you actually need some of those features, but since you don't, it might not be. (It depends how often you're calling this method, and whether efficiency is a concern for your application.)

Also, SimpleDateFormat is non-thread-safe, which is sometimes a pain. (Without knowing anything about your application, I can't guess whether that matters.)

Personally, I'd probably write a custom method.

Ouphe answered 11/1, 2012 at 20:49 Comment(2)
No need to prepend yyyy-MM-dd and 1970-01-01. Parsing as the OP did works if the timezone is set to GMT.Protraction
@JBNizet: On my system that's true, but the OP wrote that his/her approach gave "milliseconds including current date", so I was assuming that his/her JDK's implementation of SimpleDateFormat must not be calling calendar.clear(). I don't see anything in the Javadoc that indicates that SimpleDateFormat must default to 1970-01-01. Do you know if that's guaranteed?Ouphe
O
1

I am presenting two options:

  1. Time4J, an advanced external date, time and time interval library.
  2. java.time, the built-in modern Java date and time API.

SimpleDateFormat and Date are the wrong classes to use, both because a duration of 1 minute 30.5 seoncds is not a date and because those classes have long gone out of any reasonable use.

Time4J

This is the elegant solution. We first declare a formatter:

private static final Duration.Formatter<ClockUnit> DURATION_FORMAT 
        = Duration.formatter(ClockUnit.class, "hh:mm:ss.fff");

Then parse and convert to milliseconds like this:

    String startAfter = "00:01:30.555";
    
    Duration<ClockUnit> dur = DURATION_FORMAT.parse(startAfter);
    long milliseconds = dur.with(ClockUnit.MILLIS.only())
            .getPartialAmount(ClockUnit.MILLIS);
    
    System.out.format("%d milliseconds%n", milliseconds);

Output is:

90555 milliseconds

java.time

The java.time.Duration class can only parse ISO 8601 format. So I am first converting your string to that format. It goes like PT00H01M30.555S (the leading zeroes are not required, but why should I bother removing them?)

    String startAfter = "00:01:30.555";
    
    String iso = startAfter.replaceFirst(
            "^(\\d{2}):(\\d{2}):(\\d{2}\\.\\d{3})$", "PT$1H$2M$3S");
    Duration dur = Duration.parse(iso);
    long milliseconds = dur.toMillis();
    
    System.out.format("%d milliseconds%n", milliseconds);

Output is the same as before:

90555 milliseconds

Another difference from Time4J is that the Java Duration can be directly converted to milliseconds without being converted to a Duration of only milliseconds first.

Links

Omor answered 4/12, 2021 at 9:3 Comment(0)
R
0

For .NET / C# if timeString = "00:00:00:014" Assume we have a GetNumeric() function to handle the String to Int gracefully.

public static int GetTimeSpanInMs(string timeString)
{
    int returnValue = 0;
    try
    {
        var parts = timeString.Split(":");
        returnValue = GetNumeric(parts[0]) * 1000 * 60 * 60 + GetNumeric(parts[1]) * 1000 * 60 + GetNumeric(parts[2]) * 1000 + GetNumeric(parts[3]);
    }
    catch 
    {
        //handle
    }

    return returnValue;
}
Rasmussen answered 30/11, 2023 at 23:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.