I am trying to find notation for periodic event in ISO8601 format.
Can it be done as one expression?
For example, event starting at March 31 2017 at 17:25 with periodicity 1 hour:
2017-03-31T17:25:00Z/PT1H
Trying to parse this expression to Duration in Java:
java.time.Duration d = java.time.Duration.parse("2017-03-31T17:25:00Z/PT1H");
results in exception.
In Iso-8601, the expression "2017-03-31T17:25:00Z/PT1H" is not a recurrent interval but just a normal instant/moment-interval. If you look for recurrent intervals then you have to prefix it with "Rn/" where "n" is a positive integer (optional). Standard Java has no support for intervals so you have to either code your own workaround with string processing and partial parsing or you use a 3rd-party library like my one (Time4J):
MomentInterval interval = MomentInterval.parseISO("2017-03-31T17:25:00Z/PT1H");
System.out.println(interval); // [2017-03-31T17:25:00Z/2017-03-31T18:25:00Z)
See also the Javadoc for moment intervals. For recurrent intervals please have a look at this class IsoRecurrence.
Interoperability note: You can easily convert the Moment-components to java.time.Instant via the method toTemporalAccessor().
Related
I have a Duration, like P3M (3 months). How can I get number of days it is from now?
All I have now is this:
Duration.parseWeekBasedPeriod("P3M")
I know the period parameter will never be shorter than 1 week, so parseWeekBasedPeriod() should be ok. But I'm reading JavaDoc, and I can't figure out how to get those days.
I understand, the problem is that months can has 31, 30, 29 and 28 days.
Using parseWeekBasedPeriod(...) is certainly wrong if you want to apply durations measured in months. This very special method handles week based years which can last either 364 or 371 days (52 or 53 weeks). So I suggest just to use the standard parsing method for calendar-based durations. The following code also strongly simplifies the evaluation of days between two dates (see last line).
Duration<CalendarUnit> duration = Duration.parseCalendarPeriod("P3M");
PlainDate today = PlainDate.nowInSystemTime();
PlainDate later = today.plus(duration);
long days = CalendarUnit.DAYS.between(today, later);
By the way, I have tested the method for weekbased durations once again. It will usually throw an exception if it tries to parse months. You didn't seem to have seen any exception so I assume that the fact that you use untyped constructs like "val" has shadowed the necessary type information in processing the duration string (and Time4J is a strongly typed library). Hence - if technically possible for you -, I strongly recommend to use type-safe code as shown in my solution.
Finaly figured it out:
val period = Duration.parseWeekBasedPeriod("P3M")
val start = PlainDate.nowInSystemTime()
val end = start.plus(period)
val days: Long = Duration.`in`(CalendarUnit.DAYS).between(start, end).getPartialAmount(CalendarUnit.DAYS)
I do want to check if an Instant is between two other instants:
Currently I use:
import java.time.format.DateTimeFormatter;
import java.time.Instant;
Instant start = Instant.from(DateTimeFormatter.ISO_DATE_TIME.parse("2016-10-25T12:31:39.084726218Z"));
Instant end = Instant.from(DateTimeFormatter.ISO_DATE_TIME.parse("2016-10-25T13:31:39.084726218Z"));
// for exclusive range
Instant testSubject1 = Instant.from(DateTimeFormatter.ISO_DATE_TIME.parse("2016-10-25T12:31:40Z"));
boolean isInRange1 = testSubject1.isAfter(start) && testSubject1.isBefore(end); // this works as exclusive range
//for inclusive range
Instant testSubject2 = Instant.from(DateTimeFormatter.ISO_DATE_TIME.parse("2016-10-25T12:31:39.084726218Z"));
boolean isInRange2 = (testSubject2.equals(start) || testSubject2.isAfter(start)) && (testSubject2.equals(end) || testSubject2.isBefore(end)); // inclusive range
Is there any other utility function is the standard library or elsewhere that allows for this kind of range check is a simplified way?
I'm looking for something like:
new InstantRange(start,end).checkInstantWithin(testSubject1);
// or
InstantUtils.inRangeExclusive(start,end, testSubject1);
InstantUtils.inRangeInclusivestart,end, testSubject1);
You can use Interval in ThreeTen-Extra for a task like this. (Assuming you are willing o pull in a library)
My lib Time4J offers MomentInterval which is interoperable with the type Instant and also allows either half-open (default) or closed intervals. Example:
Instant start = ...;
Instant end = ...;
MomentInterval interval = MomentInterval.between(start, end); // half-open (end exclusive)
MomentInterval closed = interval.withClosedEnd(); // (inclusive)
Testing if an interval contains a test instant is easy, for example:
boolean isInRange = interval.contains(Moment.from(testInstant));
I agree that using an extra lib is probably overkill if you only want to do this simple in-range-test, but if you like to manipulate intervals or to do complex queries in interval trees then my lib might be interesting enough for you, see the other classes in the range-package.
Not natively. See https://stackoverflow.com/a/35300229/628318
which gives the "inclusive" example of:
containsNow = !now.isBefore( start ) && now.isBefore( stop );
I realized thanks to #kumesana comment that the the range check can be simplified into a more readable form by taking advantage that testSubject2.equals(start) || testSubject2.isAfter(start) can be effectively replaced with !testSubject2.isBefore(start) so the inclusive range check can be implemented as:
private boolean timestampInRange(Instant start, Instant end, Instant subject) {
return !subject.isBefore(start) && !subject.isAfter(end);
}
Alternatively, I found that the Joda Time library has org.joda.time.Interval that allows for range checks via .contains() but that requires converting my java.time.Instants to org.joda.time.Instant, so the ThreeTen-Extra library from the other answer seems more appropriate since it works with regular java.time.Instant.
In JDK 8, no there is no such class. There are 2 classes that may provide partial support for your needs. It is Period and Duration
I'm working with an agenda in Java. I have stored in my database the day of the week, the start and end time of some labs availability.
Now I need to provide a service for a schedule system by showing only the unavailable times of the day. For example, if day one has start time 13:00 and end time 19:00, I need to return a range just like this:
[00:00 - 13:00, 19:00 - 23:59] . Remembering that a day can have more than a range available.
Is there any Java Class or API that could help me on subtracting these ranges?
My lib Time4J offers following solution for the subtraction problem:
ClockInterval fullDay = ClockInterval.between(PlainTime.of(0), PlainTime.of(24));
ClockInterval slot = ClockInterval.between(PlainTime.of(13, 0), PlainTime.of(19, 0));
IntervalCollection<PlainTime> icoll = IntervalCollection.onClockAxis().plus(fullDay);
List<ChronoInterval<PlainTime>> result = icoll.minus(slot).getIntervals();
The resulting list of half-open intervals (with open end) can be easily iterated through and gives the expected result {[T00:00/T13:00), [T19:00/T24:00)}. Every result interval can be converted to a standard ClockInterval, too. There are also various methods to print such intervals in a localized way. Furthermore, you might find the class DayPartitionBuilder interesting which allows to connect weekdays and time schedules in streaming, see the example given in the documentation.
About compatibility with java.time:
The between()-methods of ClockInterval also accept instances of java.time.LocalTime.
Every instance of PlainTime can be converted back to LocalTime by help of the method toTemporalAccessor() with the exception of the value 24:00 which exists in Time4J but not in java.time.LocalTime.
Recently I am being challenged by quite an "easy" problem. Suppose that there is sentences (saved in a String), and I need to find out if there is any date in this String. The challenges is that the date can be in a lot of different formats. Some examples are shown in the list:
June 12, 1956
London, 21st October 2014
13 October 1999
01/11/2003
Worth mentioning that these are contained in one string. So as an example it can be like:
String s = "This event took place on 13 October 1999.";
My question in this case would be how can I detect that there is a date in this string. My first approach was to search for the word "event", and then try to localize the date. But with more and more possible formats of the date this solution is not very beautiful. The second solution that I tried is to create a list for months and search. This had good results but still misses the cases when the date is expressed all in digits.
One solution which I have not tried till now is to design regular expressions and try to find a match in the string. Not sure how much this solution might decrease the performance.
What could be a good solution that I should probably consider? Did anybody face a similar problem before and what solutions did you find?
One thing is for sure that there are no time, so the only interesting part is the date.
Using the natty.joestelmach.com library
Natty is a natural language date parser written in Java. Given a date expression, natty will apply standard language recognition and translation techniques to produce a list of corresponding dates with optional parse and syntax information.
import com.joestelmach.natty.*;
List<Date> dates =new Parser().parse("Start date 11/30/2013 , end date Friday, Sept. 7, 2013").get(0).getDates();
System.out.println(dates.get(0));
System.out.println(dates.get(1));
//output:
//Sat Nov 30 11:14:30 BDT 2013
//Sat Sep 07 11:14:30 BDT 2013
You are after Named Entity Recognition. I'd start with Stanford NLP. The 7 class model includes date, but the online demo struggles and misses the "13". :(
Natty mentioned above gives a better answer.
If it's only one String you could use the Regular Expression as you mentioned. Having to find the different date format expressions. Here are some examples:
Regular Expressions - dates
In case it's a document or a big text, you will need a parser. You could use a Lexical analysis approach.
Depending on the project using an external library as mentioned in some answers might be a good idea. Sometimes it's not an option.
I've done this before with good precision and recall. You'll need GATE and its ANNIE plugin.
Use GATE UI tool to create a .GAPP file that will contain your
processing resources.
Use the .GAPP file to use the extracted Date
annotation set.
Step 2 can be done as follows:
Corpus corpus = Factory.newCorpus("Gate Corpus");
Document gateDoc = Factory.newDocument("This event took place on 13 October 1999.");
corpus.add(gateDoc);
File pluginsHome = Gate.getPluginsHome();
File ANNIEPlugin = new File(pluginsHome, "ANNIE");
File AnnieGapp = new File(ANNIEPlugin, "Test.gapp");
AnnieController =(CorpusController) PersistenceManager.loadObjectFromFile(AnnieGapp);
AnnieController.setCorpus(corpus);
AnnieController.execute();
Later you can see the extracted annotations like this:
AnnotationSetImpl ann = (AnnotationSetImpl) gateDoc.getAnnotations();
System.out.println("Found annotations of the following types: "+ gateDoc.getAnnotations().getAllTypes());
I'm sure you can do it easily with the inbuilt annotation set Date. It is also very enhancable.
To enhance the annotation set Date create a lenient annotation rule in JAPE say 'DateEnhanced' from inbuilt ANNIE annotation Date to include certain kinds of dates like "9/11" and use a Chaining of Java regex on R.H.S. of the 'DateEnhanced' annotations JAPE RULE, to filter some unwanted outputs (if any).
First of all I'm new to java.time package.
I'm writing a webapp that need to work with specific times of the day and with several durations of events.
So I wrote my code using LocalTime and Duration classes of java.time package.
When I need to render their value in JSP it is very simple for LocalTime object (because .toString() returns a human readable vale), so I can just write ${startTime} and everything goes in the right way (e.g. it is rendered as 9:00).
The same approach doesn't work for Duration, because its representation is something like PT20M (in this case for 20 minutes).
Does it exist an elegant way to perform a human-readable conversion in JSP directly by EL?
Yes, I know I can convert the object in a string in my classes (before JSP), but I'm searching for the suggested approach (that I'm not able to find)... another point is that I not see an official "convert()" (or whatever else) method in Duration object... so I'm thinking I'm using the wrong object to map a duration of time (to add or subtract on LocalTimes).
Thank you.
Unfortunately there exists no elegant builtin way to format a Duration in Java 8. The best i have found is to use the method bobince describes in this answer:
Duration duration = Duration.ofHours(1).plusMinutes(20);
long s = duration.getSeconds();
System.out.println(String.format("%d:%02d:%02d", s/3600, (s%3600)/60, (s%60)));
Which prints:
1:20:00
The code will have to be tuned if you need longer durations.
I'm not sure what you mean that you are missing a convert method, but Duration is well suited for adding/subtracting on LocalTime. The methods LocalTime.plus() and LocalTime.minus() accepts Duration as argument.
If you're interested in words, apache commons will do the trick:
DurationFormatUtils.formatDurationWords(System.currentTimeMillis() - start, true, false))
2 days 1 hour 5 minutes 20 seconds
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/time/DurationFormatUtils.html#formatDurationWords-long-boolean-boolean-