Parse to LocalTime pattern mm:ss.S - java

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

Related

How to convert String time to Long milliseconds in Java

I'm trying to convert a time string to milliseconds using the code below. Because I will use the time as countdown timer.
The problem is that the time is from database and it is on varchar type. I tried this code and it's not giving me the correct output.
String timeDuration = "10:00"; //for example only
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
Date time = sdf.parse(timeDuration);
long millis = time.getTime(); //The output must be 600000
I'm getting the wrong "millis" using this code.
I'm using Android Studio.
Duration is not the same as Date-Time
Duration is not the same as Date-Time and therefore you should not parse your string into a Date-Time type. The Date-Time type (e.g. java.util.Date) represents a point in time whereas a duration represents a length of time. You can understand it from the following examples:
I have been reading this book for 10 minutes.
I have been reading this book since 4:00 pm.
The first example has a duration whereas the second example has a Date-Time (implicitly today).
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Solution using java.time, the modern Date-Time API:
You can convert your string into ISO 8601 format for a Duration and then parse the resulting string into Duration which you can be converted into milliseconds.
Demo:
import java.time.Duration;
public class Main {
public static void main(String[] args) {
System.out.println(durationToMillis("10:00"));
}
static long durationToMillis(String strDuration) {
String[] arr = strDuration.split(":");
Duration duration = Duration.ZERO;
if (arr.length == 2) {
strDuration = "PT" + arr[0] + "M" + arr[1] + "S";
duration = Duration.parse(strDuration);
}
return duration.toMillis();
}
}
Output:
600000
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
Using classes SimpleDateFormat and Date is VERY STRONGLY discouraged. These classes are deprecated and very problematic. Use package java.time instead. for parser use DateTimeFormatter class. Also, in your case you may use class Duration. Alternatively, you can use Open Source MgntUtils library That has class TimeInterval and method TextUtils.parseStringToTimeInterval(java.lang.String valueStr). In this case your code could be very simple:
long milliseconds = TextUtils.parseStringToTimeInterval("10m").toMillis();
And this will give you 600000 as a result. The library is available as Maven artifact here, and on the github (including source code and Javadoc) here. Here is a link to Javadoc. The article about the library is here (See paragraph "Parsing String to Time Interval")
Disclaimer: The library is written by me
I think you can cut substring first. Then convert the substring to integer. Then multiple with 60000.
Integer.parseInt(timeDuration.substring(0, timeDuration.indexOf(":"))) * 60 * 1000
HTH
Cheers,
Hook

Unable to parse optional microseconds in localTime

I am receiving timestamp in format : HHmmss followed by milleseconds and microseconds.Microseconds after the '.' are optional
For example: "timestamp ":"152656375.489991" is 15:26:56:375.489991.
Below code is throwing exceptions:
final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
.appendPattern("HHmmssSSS")
.appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true)
.toFormatter();
LocalTime.parse(dateTime,FORMATTER);
Can someone please help me with DateTimeformatter to get LocalTime in java.
Here is the stacktrace from the exception from the code above:
java.time.format.DateTimeParseException: Text '152656375.489991' could not be parsed: Conflict found: NanoOfSecond 375000000 differs from NanoOfSecond 489991000 while resolving MicroOfSecond
at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1959)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1894)
at java.base/java.time.LocalTime.parse(LocalTime.java:463)
at com.ajax.so.Test.main(Test.java:31)
Caused by: java.time.DateTimeException: Conflict found: NanoOfSecond 375000000 differs from NanoOfSecond 489991000 while resolving MicroOfSecond
at java.base/java.time.format.Parsed.updateCheckConflict(Parsed.java:329)
at java.base/java.time.format.Parsed.resolveTimeFields(Parsed.java:462)
at java.base/java.time.format.Parsed.resolveFields(Parsed.java:267)
at java.base/java.time.format.Parsed.resolve(Parsed.java:253)
at java.base/java.time.format.DateTimeParseContext.toResolved(DateTimeParseContext.java:331)
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1994)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1890)
... 3 more
There are many options, depending on the possible variations in the strings you need to parse.
1. Modify the string so you need no formatter
String timestampString = "152656375.489991";
timestampString = timestampString.replaceFirst(
"^(\\d{2})(\\d{2})(\\d{2})(\\d{3})(?:\\.(\\d*))?$", "$1:$2:$3.$4$5");
System.out.println(timestampString);
LocalTime time = LocalTime.parse(timestampString);
System.out.println(time);
The output from this snippet is:
15:26:56.375489991
The replaceFirst() call modifies your string into 15:26:56.375489991, the default format for LocalTime (ISO 8601) so it can be parsed without any explicit formatter. For this I am using a regular expression that may not be too readable. (…) enclose groups that I use as $1, $2, etc., in the replacement string. (?:…) denotes a non-capturing group, that is, cannot be used in the replacement string. I put a ? after it to specify that this group is optional in the original string.
This solution accepts from 1 through 6 decimals after the point and also no fractional part at all.
2. Use a simpler string modification and a formatter
I want to modify the string so I can use this formatter:
private static DateTimeFormatter fullParser
= DateTimeFormatter.ofPattern("HHmmss.[SSSSSSSSS][SSS]");
This requires the point to be after the seconds rather than after the milliseoncds. So move it three places to the left:
timestampString = timestampString.replaceFirst("(\\d{3})(?:\\.|$)", ".$1");
LocalTime time = LocalTime.parse(timestampString, fullParser);
15:26:56.375489991
Again I am using a non-capturing group, this time to say that after the (captured) group of three digits must come either a dot or the end of the string.
3. The same with a more flexible parser
The formatter above specifies that there must be either 9 or 3 digits after the decimal point, which may be too rigid. If you want to accept something in between too, a builder can build a more flexible formatter:
private static DateTimeFormatter fullParser = new DateTimeFormatterBuilder()
.appendPattern("HHmmss")
.appendFraction(ChronoField.NANO_OF_SECOND, 3, 9, true)
.toFormatter();
I think that this would be my favourite approach, again depending on the exact requirements.
4. Parse only a part of the string
There is no problem so big and awful that it cannot simply be run away
from (Linus in Peanuts, from memory)
If you can live without the microseconds, ignore them:
private static DateTimeFormatter partialParser
= DateTimeFormatter.ofPattern("HHmmssSSS");
To parse only a the part of the string up to the point using this formatter:
TemporalAccessor parsed
= partialParser.parse(timestampString, new ParsePosition(0));
LocalTime time = LocalTime.from(parsed);
15:26:56.375
As you can see it has ignored the part from the decimal point, which I wouldn’t find too satisfactory.
What went wrong in your code?
Your 6 digits after the decimal point denote nanoseconds. Microseconds would have been only 3 decimals after the milliseconds. To use appendFraction() to parse these you would have needed a TemporalUnit of nano of millisecond. The ChronoUnit enum offers nano of day and nano of second, but not nano of milli. TemporalUnit is an interface, so in theory we could develop our own nano of milli class for the purpose. I tried to develop a class implementing TemporalUnit once, but gave up, I couldn’t get it to work.
Links
Wikipedia article: ISO 8601
Regular expressions in Java - Tutorial

Which date pattern in java can produce this date result? (seven digits after seconds)

I need a date pattern to generate date string like this :
2019-12-18T17:11:24.2646051+03:30
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.?XXX").format(new Date())
What should I write instead of '?' ?
First point, get rid of SimpleDateFormat. That class is notoriously troublesome and long outdated. Also there is no way that SimpleDateFormat can produce 7 decimals on the seconds.
Second point, you probably don’t need to print 7 decimals on the seconds. Your format exemplified through 2019-12-18T17:11:24.2646051+03:30 is ISO 8601. In ISO 8601 the number of decimals on the seconds is free, so everyone should accept if you give them a string with 3 decimals, 9 decimals or no decimals at all (in the last case leave out the decimal point too).
java.time
So the easy solution is:
String desiredString
= OffsetDateTime.now(ZoneId.systemDefault()).toString();
System.out.println(desiredString);
Output when I ran the code just now:
2019-12-28T11:46:07.308+01:00
I am using java.time, the modern Java date and time API. And I am exploiting the fact that the toString methods of the date and time classes of java.time print ISO 8601 format. So we need no explicit formatter so far.
If you do want or need 7 decimals, do use a DateTimeFormatter:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendPattern("HH:mm:ss.SSSSSSSXXX")
.toFormatter();
String desiredString = OffsetDateTime.now(ZoneId.systemDefault())
.format(formatter);
2019-12-28T11:48:52.3840000+01:00
It is possible to specify the formatter using a format pattern string only, but I prefer to reuse the built-in ISO_LOCAL_DATE formatter for the date part also when the code gets a few lines longer.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Try this:
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZ").format(new Date())
If you want more accuracy than milliseconds please look at this link
You can use S for the milliseconds. See this documentation page. You cannot use more than 3 though; there seems to be no support for microseconds or more fine-grained options.
import java.text.SimpleDateFormat;
import java.util.Date;
class Main {
public static void main(String[] args) {
String s = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").format(new Date());
System.out.println(s);
}
}
Example output:
2019-12-28T10:28:02.933Z
If you do need more granularity, using the Instant.now() function might be an option, although the following code will only produce 6 digits (microseconds, no nanoseconds):
import java.time.Instant;
class Main {
public static void main(String[] args) {
System.out.println(Instant.now().toString());
}
}
Example output:
2019-12-28T10:31:56.477551Z
See this answer for more info.

Java : Custom Timestamp Format : verify format to microsec precision

My Objective is
to create a java class that can handle the below two requirements
(A) 1. Verify if the format of a timestamp matches with expected format.
CCYY-MM-DD'T'hh:mm:ss'.0000000000+'uh:um"
Ex: the expected format is not static.
It may be either of these
"2013-09-10T18:30:20.123456+10:00" or
"2013-09-10T18:30:20.123+10:00".
I am not bothered about the
precision and value. Only the format matters.
(B) 2. Verify if the timestamp is in a certain range.
Ex: Verify if the timestamp is in
between "2013-09-10 18:27" and "2013-09-10 18:33". (verification is only upto minute level precision) (may be a delta of + or - 2min)
As suggested by one of the member, I have edited the post to target at
One specific question.
The QUESTION :
How to validate the custom timestamp upto microsec precision using JAVA class ?
The two arguments for this class will be
1) Expected FORMAT as a String
2) timestamp value as a String
Based on analysis from various search results, below is my understanding :
Java (by default) does not parse/format Timestamp at microsecond level( I used SimpleDateFormat)
If 6 digits are given in milliseconds place, it will re-calculate the value into seconds and the dateformat will be updated and the new dateformat will have 3 digits in milliseconds precision.
I have also seen a thread which suggests to use java.sql.Timestamp.
Tried this approach but not working.
I was not able to convert my strTimestamp 2013-09-10T18:30:20.123456+10:00 into Timestamp object.
Timestamp ts = Timestamp.valueOf(strTimestamp);
java.lang.IllegalArgumentException:
Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
I was not able convert my input format into Timestamp object.
I have a workaround to validate using regular expression :
2013-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9]).[0-9][0-9][0-9][0-9][0-9][0-9]\+10:00
The problem with this reg ex is that, my expected timestamp format is not static. So i have to use a regex for every pattern.
So I am trying to figure out if there is any robust solution in java, which can be self sufficient even if the expected format changes.
java.time in Java 8
JSR 310 defined a new java.time package in Java 8. Its date-time class resolves to nanoseconds. That gives you 9 digits after the decimal point.
The java.time package is inspired by Joda-Time but entirely re-architected. Concepts are similar.
Like Joda-Time, the java.time package uses ISO 8601 formats as its defaults for parsing and formatting. So you can input or output strings such as 2013-09-10T18:30:20.123456789+10:00.
An early release of Java 8 is available now. Official release should be this month.
A project to backport this package to earlier versions of Java was underway. I do not know of its current status or success. The backport project is independent of Oracle and the OpenJDK project.
Milliseconds
The old bundled classes, java.util.Date & .Calendar, use a precision of milliseconds.
Ditto for the excellent Joda-Time library, milliseconds precision.
So not enough digits in the fractional seconds to meet your needs.
A java.sql.Timestamp is not going to help you, because that is a java.util.Date.
The code is fairly simple, if you use the right format String with SimpleDateFormat, which you let do the heavy lifting. Here's an entire working solution:
public static boolean isNear(String timestamp, int microPlaces, Date near, int minutes) {
if (!timestamp.matches(".*\\.\\d{" + microPlaces + "}\\D.*") {
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ");
try {
Date date = sdf.parse(timestamp.replaceAll(":(?=\\d\\d$)", ""));
return Math.abs(date.getTime() - near.getTime()) <= minutes * 60000;
} catch (ParseException ignore) {
return false; // string was not of correct format
}
}
This may not be exactly what you had in mind - if not, you should be able to use it as a basis for what you want. The key points are:
The S format string means "microseconds", and it doesn't require all the digits - so your timestamp can have any number
Java 6 needs the colon removed from the timezone. Java 7 doesn't need this - use the X format string instead of Z
A failure to parse a date from the input throws a ParseException - do what you want with this event
I chose to make the API give central date for the range and a +/- minute value. You may need to pass two dates - up to you. Use Date.before() and Date.after() to compare if you do that.
Here's some test code testing your examples and a couple of edge cases:
public static void main(String[] args) throws Exception {
Date near = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm").parse("2013-09-10T18:32");
System.out.println(isNear("2013-09-10T18:30:20.123456+10:00", near, 2));
System.out.println(isNear("2013-09-10T18:30:20.123+10:00", near, 2));
System.out.println(isNear("2013-09-10T18:10:20.123+10:00", near, 1));
System.out.println(isNear("XXXX-09-10T18:10:20.123+10:00", near, 1));
}
Output:
true
true
false
false
Really I`m also trying to find answer to this problem. As I have no ability to add comment to the Bohemian answer. I want to mention that 'S' pattern in SimpleDateFormat is used not for microseconds but for milliseconds. It means that for pattern "yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ" provided microsecond digits in string would be parsed as milliseconds.
So the first three digits would be passed as XXX seconds and their value value would be added to date. So we can receive mistake about 16 minutes.

ISO 8601 Time Interval Parsing in Java

ISO 8601 defines a syntax for representing a time interval.
There are four ways to express a time interval:
Start and end, such as "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"
Start and duration, such as "2007-03-01T13:00:00Z/P1Y2M10DT2H30M"
Duration and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
Duration only, such as "P1Y2M10DT2H30M", with additional context information
If any elements are missing from the end value, they are assumed to be the same as for the start value including the time zone. This feature of the standard allows for concise representations of time intervals. For example, the date of a two-hour meeting including the start and finish times could be simply shown as "2007-12-14T13:30/15:30", where "/15:30" implies "/2007-12-14T15:30" (the same date as the start), or the beginning and end dates of a monthly billing period as "2008-02-15/03-14", where "/03-14" implies "/2008-03-14" (the same year as the start).
In addition, repeating intervals are formed by adding "R[n]/" to the beginning of an interval expression, where R is used as the letter itself and [n] is replaced by the number of repetitions. Leaving out the value for [n] means an unbounded number of repetitions. So, to repeat the interval of "P1Y2M10DT2H30M" five times starting at "2008-03-01T13:00:00Z", use "R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M".
I am looking for a good Java parser (if possible compatible with the Joda-Time library) to parse this syntax. Any pointers to a good library ?
java.time
The java.time framework built into Java 8 and later has a Duration.parse method for parsing an ISO 8601 formatted duration:
java.time.Duration d = java.time.Duration.parse("PT1H2M34S");
System.out.println("Duration in seconds: " + d.get(java.time.temporal.ChronoUnit.SECONDS));
Prints Duration in seconds: 3754
For anyone on a project that might be restricted from using 3rd party libraries (licensing reasons, or whatever), Java itself provides at least a portion of this capability, since Java 1.6 (or earlier?), using the javax.xml.datatype.DatatypeFactory.newDuration(String) method and Duration class. The DatatypeFactory.newDuration(String) method will parse a string in "PnYnMnDTnHnMnS" format. These classes are intended for XML manipulation, but since XML uses ISO 8601 time notation, they also serve as convenient duration parsing utilities.
Example:
import javax.xml.datatype.*;
Duration dur = DatatypeFactory.newInstance().newDuration("PT5H12M36S");
int hours = dur.getHours(); // Should return 5
I haven't personally used any duration format except the 4th one you list, so I can't vouch for whether it successfully parses them or not.
I take it you have already tried Joda-Time? Feeding the example strings from your question through Interval.parse(Object) reveals that it can handle "start and end", "start and duration" and "duration and end", but not implied fields nor repetition.
2007-03-01T13:00:00Z/2008-05-11T15:30:00Z => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
2007-03-01T13:00:00Z/P1Y2M10DT2H30M => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
P1Y2M10DT2H30M/2008-05-11T15:30:00Z => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
2007-12-14T13:30/15:30 => java.lang.IllegalArgumentException: Invalid format: "15:30" is malformed at ":30"
R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M => java.lang.IllegalArgumentException: Invalid format: "R5"
The only other comprehensive date/time library that I know of is JSR-310, which does not appear to handle intervals like these.
At this point, building your own improvements on top of Joda-Time is probably your best choice, sorry. Are there any specific ISO interval formats that you need to handle beyond those already supported by Joda-Time?
The only library which is capable to model all the features of interval parsing you want is actually my library Time4J (range-module). Examples:
// case 1 (start/end)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/2014-06-20T16:00Z"));
// output: [2012-01-01T14:15:00Z/2014-06-20T16:00:00Z)
// case 1 (with some elements missing at end component and different offset)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/08-11T16:00+00:01"));
// output: [2012-01-01T14:15:00Z/2012-08-11T15:59:00Z)
// case 1 (with missing date and offset at end component)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/16:00"));
// output: [2012-01-01T14:15:00Z/2012-01-01T16:00:00Z)
// case 2 (start/duration)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/P2DT1H45M"));
// output: [2012-01-01T14:15:00Z/2012-01-03T16:00:00Z)
// case 3 (duration/end)
System.out.println(MomentInterval.parseISO("P2DT1H45M/2012-01-01T14:15Z"));
// output: [2011-12-30T12:30:00Z/2012-01-01T14:15:00Z)
// case 4 (duration only, in standard ISO-format)
Duration<IsoUnit> isoDuration = Duration.parsePeriod("P2DT1H45M");
// case 4 (duration only, in alternative representation)
Duration<IsoUnit> isoDuration = Duration.parsePeriod("P0000-01-01T15:00");
System.out.println(isoDuration); // output: P1M1DT15H
Some remarks:
Other interval classes exist with similar parsing capabilities, for example DateInterval or TimestampInterval in the package net.time4j.range.
For handling durations only (which can span both calendar and clock units as well), see also the javadoc. There are also formatting features, see nested class Duration.Formatter or the localized version net.time4j.PrettyTime (actually in 86 languages).
Interoperability is offered with Java-8 (java.time-package) but not with Joda-Time. For example: The start or end component of a MomentInterval can easily be queried by getStartAsInstant() or getEndAsInstant().
Repeating intervals are supported by the class IsoRecurrence. Example:
IsoRecurrence<MomentInterval> ir =
IsoRecurrence.parseMomentIntervals("R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M");
ir.intervalStream().forEach(System.out::println);
Output:
[2008-03-01T13:00:00Z/2009-05-11T15:30:00Z)
[2009-05-11T15:30:00Z/2010-07-21T18:00:00Z)
[2010-07-21T18:00:00Z/2011-10-01T20:30:00Z)
[2011-10-01T20:30:00Z/2012-12-11T23:00:00Z)
[2012-12-11T23:00:00Z/2014-02-22T01:30:00Z)

Categories