Part of my current project is to convert mm:ss to seconds...but the user has the option to enter x:xx or xx:xx.
For example, if someone wanted to enter one minute and thirty seconds into the program, they have the option to write it as either "01:30" or "1:30". And the output of both would be 90 seconds.
This is my current code.
System.out.print("Time (mm:ss): ")
String time = scan.nextLine();
int min = Integer.parseInt(time.substring(0, time.indexOf(':'))) * 60;
int sec = Integer.parseInt(time.substring(3, time.length()));
int duration = (min + sec);
System.out.println("Seconds: " + duration)
It works whenever I enter xx:xx, but fails when I enter x:xx.
I am not sure how to only read the characters after ":" . If I start the substring at ":" (I have it at 3 now), it can't convert to int because it reads the ":".
I have looked all over Google and my textbook, but have not found anything. I assume I am just using the wrong technique. The code needs to stay within the parameters of basic beginner String methods. Thank you!
This answer probably does not stay within the parameters of basic beginner String methods as requested. I think it will be useful for other readers of your question who don’t have the same limitation.
java.time.Duration
The Duration class is the class to use for an amount of time like 1 minute 30 seconds. Unfortunately, the Duration class can only parse strings in ISO 8601 format (link below), but the string conversion isn’t hard with a regular expression. And fortunately in ISO 8601 format leading zeroes don’t matter. The Duration class is part of java.time, the modern Java date and time API.
ISO 8601 format for a duration is like PT01M30S. Think of it as a period of time of 01 minute 30 seconds. If the format feels unusual at first, it is straightforward. So let’s convert to it. The following method accepts your user’s format, converts and returns a Duration.
public static Duration parseDuration(String durStr) {
String isoString = durStr.replaceFirst("^(\\d{1,2}):(\\d{2})$", "PT$1M$2S");
return Duration.parse(isoString);
}
Duration has a toSeconds method for converting to seconds. So let’s try the whole thing out:
System.out.println(parseDuration("01:30").toSeconds());
System.out.println(parseDuration("1:30").toSeconds());
Output is the expected:
90
90
Consider whether you need to convert to seconds at all, though. Keeping the Duration objects as they are will probably make your code more self-explanatory.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
int sec = Integer.parseInt(time.substring(time.indexOf(':') + 1), time.length()));
Thanks guys!
Here I have simplified your code.
String time = "1:30";
String[] timeUnit = time.split(":");
int totalSeconds = 60 * Integer.parseInt(timeUnit[0]) + Integer.parseInt(timeUnit[1]);
System.out.println("Seconds "+totalSeconds);
Related
How can parse LocalTime from String e.g. "10:38.0" in mm:ss.S format? I struggle to change the format.
public static LocalTime parseTime(String time) {
return localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern("mm:ss.S"));
}
Getting error
ISO of type java.time.format.Parsed
java.time.format.DateTimeParseException: Text '10:38.2' could not be parsed: Unable to obtain LocalTime from TemporalAccessor: {MinuteOfHour=10, MicroOfSecond=200000, MilliOfSecond=200, NanoOfSecond=200000000, SecondOfMinute=38},
java.time.Duration.parse()
As several others have correctly and wisely stated, your example string of 10:38.0 looks more like an amount of time, a duration. Not like a time of day a little more than 10 minutes after midnight. So LocalTime is not the correct class to use here. Use Duration. And parse the string into a Duration object.
The Duration class only supports parsing of ISO 8601 format, though. ISO 8601 format goes like PT10M38.0S for a period of time of 10 minutes 38.0 seconds (or for example PT10M38S or PT10M38.00000S, they work too). There are more ways to overcome this limitation. Arvind Kumar Avinash already shows one in his answer. My way would be to convert the string before parsing it:
public static Duration parseTime(String time) {
String iso = time.replaceFirst("^(\\d+):(\\d+(?:\\.\\d*)?)$", "PT$1M$2S");
return Duration.parse(iso);
}
Let’s try it out:
Duration dur = parseTime("10:38.0");
System.out.println(dur);
Output is:
PT10M38S
You see that the Duration prints back in ISO 8601 format too.
Depending on what further processing you want your duration for you are likely to find many useful methods in the documentation of that class; link below.
How time.replaceFirst("^(\\d+):(\\d+(?:\\.\\d*)?)$", "PT$1M$2S") works: I am using a regular expression to match your string:
^: Match the beginning of your string.
(\\d+): A capturing group matching one or more digits. Round brackets denote capturing groups. I will need this feature in the replacement below.
:: A colon (indeed).
(\\d+(?:\\.\\d*)?): A capturing group of digits optionally followed by a dot and zero or more further digits. (?: denotes the beginning of a non-capturing group that I use since I don’t need it separately in the replacement. ? after the non-capturing group denotes that it is optional (so 38 with no fraction would work for the seconds too).
$: match the end of your string
In my replacement string, PT$1M$2S, $1 and $2 denotes whatever was marched by the first and second capturing groups, which is what inserts 10 and 38.0 into the resulting string to obtain PT10M38.0S.
Nicer solution with an external library: Time4J
Using the non-trivial regular expression above to make your string and Duration.parse() meet isn’t the perfectly beautiful solution. Pattern-based parsing of a duration is supported by the Time4J library. So if you can tolerate an external dependency, consider using it. See the details in the answer by Meno Hochshield, the author of Time4J.
Links
Wikipedia article: ISO 8601
Documentation of Java regular expressions
Documentation of Duration
Answer by Meno Hochschild to this question
DateTimeFormatterBuilder#parseDefaulting
You can use DateTimeFormatterBuilder#parseDefaulting to default the hour of the day to zero.
However, in common sense, 10:38.0 represents a duration. You can obtain a Duration object by finding the duration between the parsed LocalTime and LocalTime.MIN.
Demo:
import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String str = "10:38.0";
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.appendPattern("mm:ss.S")
.toFormatter(Locale.ENGLISH);
LocalTime time = LocalTime.parse(str, dtf);
System.out.println(time);
Duration duration = Duration.between(LocalTime.MIN, time);
System.out.println(duration);
}
}
Output:
00:10:38
PT10M38S
ONLINE DEMO
Learn more about the modern Date-Time API* from Trail: Date Time.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.
The problem is, mm:ss isn't really a local time. It's more like a sprint race time. The error occurs because the translation demands a value for # of hours passed, and none are available ('no hours' is interpreted here as: They weren't in the pattern, not: "They were missing, therefore lets assume there are 0 hours").
One hacky way to fix that is to change your pattern to [HH:]mm:ss - the [] indicates optional input. Now the meaning changes: just 10:20 is interpreted as (via the optional input aspect) a shorthand for 00:10:20 and that is parsable into a LocalTime.
But, perhaps LocalTime isn't quite what you're looking for here; if indeed this describes the time passed to measure an event, you're looking for Duration, not LocalTime. Unfortunately, parsing 10:20 into a duration of 10 minutes and 20 seconds is non-trivial, the API just doesn't support it (only way to get there from a DTFormatter object is via LocalTime, crazily enough).
a) Parsing an expression like mm:ss.S without hours is not possible with the class DateTimeFormatter because such a parser tries to interprete it as point in time, not as duration. The missing hour is a fixed requirement for resolving the result to an instance of LocalTime.
b) You probably want a duration, not a LocalTime. Well, java.time has indeed a class named java.time.Duration but it can only format and parse a subset of ISO-8601-like expressions, for example: PT10M38.2S The pattern you want is not supported. Sorry.
c) Some people suggest a compromise by saying: Interprete LocalTime as kind of duration (not really true!) then parse the expression with a default hour value and finally evaluate the minute-of-hour and second-of-minute and so on. However, such a hacky workaround will only work if you never get time component values greater than 59 minutes or 59 seconds.
d) My external library Time4J supports pattern-based printing and parsing of durations. Example using the class net.time4j.Duration.Formatter:
#Test
public void example() throws ParseException {
TemporalAmount ta =
Duration.formatter(ClockUnit.class, "mm:ss.f")
.parse("10:38.2")
.toTemporalAmount();
System.out.println(LocalTime.of(5, 0).plus(ta)); // 05:10:38.200
}
The example also demonstrates a bridge to Java-8-classes like LocalTime via the conversion method toTemporalAmount(). If you use net.time4j.PlainTime instead then the bridge is of course not necessary.
Furthermore, one of many features of the time4j-duration-class is controlled normalizing when an expression contains a time component which does not fit into a standard clock scheme like 10 minutes and 68 seconds (= 11min + 8 sec).
#Test
public void example2() throws ParseException {
net.time4j.Duration dur =
Duration.formatter(ClockUnit.class, "mm:ss.f")
.parse("10:68.2")
.with(Duration.STD_CLOCK_PERIOD); // normalizing
System.out.println(PlainTime.of(5, 0).plus(dur)); // 05:11:08.200
}
I believe you want: .ofPattern("H:mm.s")
public static LocalTime parseTime(String time) {
return LocalTime.parse(time, DateTimeFormatter.ofPattern("H:mm.s"));
}
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
I'm using this library I just discovered which is supposedly less heavier than Joda time for android and I said what the heck, let's use it. But now I'm struggling to find any good examples on the web about how to use it, besides these two methods I have:
// ZonedDateTime contains timezone information at the end
// For example, 2011-12-03T10:15:30+01:00[Europe/Paris]
public static ZonedDateTime getDate(String dateString) {
return ZonedDateTime.parse(dateString).withZoneSameInstant(ZoneId.of("UTC"));
}
public static String formatDate(String format, String dateString) {
return DateTimeFormatter.ofPattern(format).format(getDate(dateString));
}
So how can I get the difference between two dates with this library?
There are several options depending on what you require from the difference you obtain.
It’s easiest to find the difference measured in some time unit. Use ChronoUnit.between. For example:
ZonedDateTime zdt1 = getDate("2011-12-03T10:15:30+01:00[Europe/Paris]");
ZonedDateTime zdt2 = getDate("2017-11-23T23:43:45-05:00[America/New_York]");
long diffYears = ChronoUnit.YEARS.between(zdt1, zdt2);
System.out.println("Difference is " + diffYears + " years");
long diffMilliseconds = ChronoUnit.MILLIS.between(zdt1, zdt2);
System.out.println("Difference is " + diffMilliseconds + " ms");
This prints:
Difference is 5 years
Difference is 188594895000 ms
I am using your getDate method, so the format required is that of ZonedDateTime (modified from ISO 8601), for example 2011-12-03T10:15:30+01:00[Europe/Paris]. Seconds and fraction of second are optional, as is time zone ID in square brackets.
BTW you don’t need to convert to UTC before finding the difference. You will get the same result even if you leave out that conversion.
You may also get the difference in years, months and days. The Period class can give you this, but it cannot handle time of day, so convert to LocalDate first:
Period diff = Period.between(zdt1.toLocalDate(), zdt2.toLocalDate());
System.out.println("Difference is " + diff);
Difference is P5Y11M21D
The output means a period of 5 years 11 months 21 days. The syntax may feel a little strange at first, but is straightforward. It is defined by the ISO 8601 standard. In this case the time zone matters since it is never the same date in all time zones.
To get the difference in hours, minutes and seconds use the Duration class (I am introducing a new time since using Duration for nearly 6 years would be too atypical (though possible)).
ZonedDateTime zdt3 = getDate("2017-11-24T18:45:00+01:00[Europe/Copenhagen]");
Duration diff = Duration.between(zdt2, zdt3);
System.out.println("Difference is " + diff);
Difference is PT13H1M15S
A period of 13 hours 1 minute 15 seconds. The T that you already know from 2011-12-03T10:15:30+01:00[Europe/Paris] here too separates the date part from the time part so you know that in this case 1M means 1 minute, not 1 month.
I am working on a program that reads gps data.
A NMEA string returns time like this: 34658.00.
My parser treats that as a double
InputLine = "\\$GPGGA,34658.00,5106.9792434234,N,11402.3003,W,2,09,1.0,1048.47,M,-16.27,M,08,AAAA*60";
//NMEA string
//inputLine=input.readLine();
if(inputLine.contains("$GPGGA")) {
String gpsgga = inputLine.replace("\\", "");
String[] gga = gpsgga.split(",");
String utc_time = gga[1];
if (!gga[1].isEmpty()) {
Double satTime = Double.parseDouble(utc_time);
gpsData.setgpsTime(satTime);
}
How would I go about formatting 34658.00 as 03:46:58?
DateTimeFormatter nmeaTimeFormatter = DateTimeFormatter.ofPattern("Hmmss.SS");
String utcTime = "34658.00";
System.out.println(LocalTime.parse(utcTime, nmeaTimeFormatter));
This prints
03:46:58
Parsing into a double first is the detour, it just gives you more complicated code than necessary, so avoid that. Just parse the time as you would parse a time in any other format. Does the above also work for times after 10 AM where the hours are two digits? It does. Input:
String utcTime = "123519.00";
Output:
12:35:19
It does require exactly two decimals, though (and will render them back if they are non-zero). If the number of decimals may vary, there are at least two options
Use a DateTimeFormatterBuilder to specify a fractional part (even an optional fractional part) with anything between 0 and 9 decimals.
The hack: use String.replaceFirst with a regular expression to remove the fractional part, and then also remove it from the format pattern string.
It also requires at least 5 digits before the decimal point, so times in the first hour of day need to have leading zeroes, for example 00145.00 for 00:01:45.
Since the time is always in UTC, you may want to use atOffset to convert the LocalTime into an OffsetTime with ZoneOffset.UTC. If you know the date too, an OffsetDateTime or an Instant would be appropriate, but I haven’t delved enough into the documentation to find out whether you know the date.
Links
Oracle tutorial: Date Time explaining how to use java.time.
NMEA data
int hours = (int) Math.floor(satTime / 10000);
int minutes = (int) Math.floor((satTime - hours * 10000) / 100);
double seconds = satTime % 100;
Then zero-pad and add in ':'s in between. You could truncate or round to whole seconds too, if you wish, but then be careful about seconds rounding up to 60, in which case you have to zero the seconds, add 1 to the minutes, and then possible do it over if you get 60 minutes and round up the hours.
I'm trying to add a string representation of time in minutes (4:30) to another string (10:00:00) like you would say 'ten o'clock plus 4 minutes, 30 seconds.
If I sound a bit verbose, it's because I've spent 7 hrs searching the web for an answer and keep getting how to convert a fixed string of even minutes to date/time.
I tried to use joda time but can't figure out how to make 4:30 into an integer (I can make it work with'04'). These times are strings in code from variables, not something the user enters at the command line.
I'm using JDK 1.7 and netbeans 8.
Java 8 Time API
You could take advantage of Java 8's new Time API, which is similar to JodaTime
The first thing you need is a Duration, something like...
Duration d = Duration.parse("PT0H4M30S");
or
Duration d = Duration.ofMinutes(4).plusSeconds(30);
Next, you need to generate a LocalTime value
LocalTime time = LocalTime.parse("10:00:00", DateTimeFormatter.ofPattern("HH:mm:ss"));
and then you can simply add the Duration to it
time = time.plus(d);
which will result in a value of
10:04:30
The difficult part is getting the values of the duration from the String, but if you can guarantee the format, you could simply use String#split
JodaTime
JodaTime would make it slightly easier, for example, you can parse 4:30 into a Period using something like...
PeriodFormatter formatter = new PeriodFormatterBuilder()
.appendMinutes().appendSuffix(":")
.appendSeconds()
.toFormatter();
Period p = formatter.parsePeriod("4:30");
Then you could simply parse 10:00:00 into a LocalTime and add the Period
LocalTime lt = LocalTime.parse("10:00:00", DateTimeFormat.forPattern("HH:mm:ss"));
lt = lt.plus(p);
System.out.println(lt);
which outputs...
10:04:30.000
We have intervals (elapsed time between two oracle timestamps) stored in our database as seconds and we format them at front end with Java.
What we would to achieve on the reports is a format of the form "HH:MM" or "HH:MM:SS", with the time separator ":" localized as it happens with dates and time information, i.e '.' for Italian and ':' for English.
Unfortunately the date-related formatting classes, like SimpleDateFormat, do not work** because we can expect durations above the 24 hours.
We don't want to employ 3rdy party library as well.
Do you know how we can solve this problem?
TIA
If you want hours of more than 24 you can print this separately.
int hour = time / 3600000;
String duration = time + new SimpleDateFormat(":mm:ss").format(new Date(time));
To support other locales you could do this more complicated example.
int hour = time / 3600000;
String duration = hour
+ DateFormat.getDateInstance(DateFormat.MEDIUM, locale)
.format(new Date(time % 3600000).substring(1);
What this will do is use the locale specific format for the last digit of the hour + mins + secs and prepend the additional digits of the hours. Note: this will not work for negative times.
You can construct a date object, say "2000-01-01", then add your seconds to it. Then you can use this to extract the localized version of the time:
DateFormat format = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
String formatted_time_part = format.format(some_date_object);
Finally you still need to add elapsed days! I'm affraid that localization of long intervals (days, months, years, centuries) has no corresponding API in Java but I might be wrong. So you will have to figure out that for yourself.