Today it‘s recommended that you use java.time, the modern Java date and time API, for your time work. Avoid Date
and SimpleDateFormat
since they had bad design problems (of which you have only seen a little bit) and have been outdated since Java 8, which came out more than 10 years ago.
The correct way to do your conversion depends on whether your string in mm ss.S
format denotes an amount of time or a time of day since these concepts are represented by different java.time classes. You would confuse your reader if you used the wrong one.
Since your string does not include any hours, I considered an amount of time up to one hour more likely. For that we need the Duration
class. Duration
isn‘t very well suited for parsing, so we have a little hand work to do:
String amountOfTimeString = " 14 37 485";
String[] parts = amountOfTimeString.trim().split(" ");
Duration duration = Duration.ofMinutes(Integer.parseInt(parts[0]))
.plusSeconds(Integer.parseInt(parts[1]))
.plusMillis(Integer.parseInt(parts[2]));
System.out.println("Duration: " + duration);
double seconds = duration.toSeconds() + duration.getNano() / NANOS_PER_SECOND;
System.out.println("Seconds: " + seconds);
Output from this snippet is:
Duration: PT14M37.485S
Seconds: 877.485
So 877.485 seconds, a positive number as expected. If you like, you may use the first line of output to verify that parsing is correct: PT14M37.485S
means 14 minutes 37.485 seconds, which agrees with the input string of 14 37 485
.
For a time of day we need the LocalTime
class. It can parse your string with the help of a DateTimeFormatter
. So declare:
private static final DateTimeFormatter TIME_PARSER
= new DateTimeFormatterBuilder()
.appendPattern(" mm ss ")
.appendValue(ChronoField.MILLI_OF_SECOND)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter(Locale.ROOT);
public static final double NANOS_PER_SECOND = Duration.ofSeconds(1).toNanos();
Then do:
String timeOfDayString = " 14 37 485";
LocalTime time = LocalTime.parse(timeOfDayString, TIME_PARSER);
System.out.println("Time of day: " + time);
int wholeSeconds = time.get(ChronoField.SECOND_OF_DAY);
int nanoOfSecond = time.getNano();
double secondOfDay = wholeSeconds + (nanoOfSecond / NANOS_PER_SECOND);
System.out.println("secondOfDay: " + secondOfDay);
Output agrees with the output from before:
Time of day: 00:14:37.485
secondOfDay: 877.485
Why is it negative?
Is there something wrong with the code?
We don‘t really need to know since we are not using Date
and SimpleDateFormat
any more -- fortunately! But out of curiosity.
First, the Date
class that you tried to use never was able to represent an amount of time. And it could represent a time of day only if you assumed a date and a time zone.
To give a full account of what I think happened in your code I have made some assumptions, but even if the concrete assumptions are not to the point, I still believe that the idea in my explanation is. I assume that you were running your code in a Central European time zone such as Europe/Oslo or Europe/Rome or some other time zone that used UTC offset +01:00 in February 2013. Next I assume you ran your code at 13:14:37.485 before posting your question at 13:44 in your time zone. Or at some other hour where the minutes and seconds were 14:37.485.
Then this happens: new Date()
produces a Date
object representing the current point in time. This is formatted into a String
of 14 37 485
. This string is parsed back into a Date
of Thu Jan 01 00:14:37 CET 1970 because defaults of 1970-01-01 and your default time zone are confusingly applied. The getTime
method returns the number of milliseconds since the start of 1070-01-01 in UTC, the so-called epoch. Because of your UTC offset of +01:00, in UTC the date and time mentioned are equal to 1969-12-31T23:14:37.485Z, so before the epoch. This explains why a negative number comes out. Your observed rsult of -2722.515 corresponds to 45 minutes 22.515 seconds before the epoch, so the time I just mentioned.
Oracle Tutorial trail: Date Time explaining how to use java.time.