Getting JODA Duration to work - java

The following code does not print the days correctly, I need to print out days and minutes as well.
Duration duration = new Duration(1328223198241L - 1326308781131L);
Period p2 = new Period(duration.getMillis());
System.out.println(p2.getDays()); // prints 0, should print 22 days
System.out.println(p2.getHours()); // prints 531 which is expected.

According to the javadoc, "Only precise fields in the period type will be used. For the standard period type this is the time fields only. Thus the year, month, week and day fields will not be populated." thus you are getting zero.
Consider this alternative.
Duration duration = new Duration(1328223198241L - 1326308781131L);
Period p2 = new Period(duration.getMillis());
System.out.println(p2.getHours()); // prints 531 which is expected.
System.out.println(p2.toStandardDays().getDays()); // prints 22 days

This behavior is explained in the javadocs: "duration is larger than one day then all the remaining duration will be stored in the largest available precise field, hours in this case."
If you explain what you're trying to do, as opposed to how you're trying to do it, then I'm sure we'll be able to help out. For example, to find what I think you're trying to get from p2.getDays():
Days.daysBetween(dateTime1, dateTime2).getDays()

Does the following not suffice: System.out.println(duration.getStandardDays());

Related

How many days from now is the Duration?

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)

How to manipulate Ranges of Time of a Day

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.

java.time.Year#range does not return a concrete week of year (Java 1.8)

I'm wondering why a concrete instance of java.time.Year#range fails to return a concrete ValueRange of possible week numbers.
I know that the following code fails, because IsoFields.WEEK_OF_WEEK_BASED_YEAR is not supported:
ValueRange range = Year.of(2018).range(IsoFields.WEEK_OF_WEEK_BASED_YEAR)
Nevertheless, I am wondering why the following code returns a ValueRange of 0/1 - 52/54:
ValueRange range = Year.of(2018).range(WeekFields.ISO.weekOfWeekBasedYear())
I know that this is true, but it is a generic ValueRange true of any Year.
From what I gather, given a concrete instance of a year, you are able to find out the actual range of numbers of weeks that fit into this particular year (in this example Year.of(2018) contains weeks 1 - 52 (since 2018-12-31 actually belongs to 2019-W01.
Same rant basically applies to YearMonth.
The only way I know to get the proper range is with the following:
LocalDate date = Year.of(2021).atDay(4)
ValueRange range2 = date.range(WeekFields.ISO.weekOfWeekBasedYear()) // 1-52
since --01-04 is the one that always belongs to --W01, so you have to beware that the following will actually yield different result:
LocalDate date = Year.of(2021).atDay(1)
ValueRange range = date.range(WeekFields.ISO.weekOfWeekBasedYear()) // 1-53 (incorrect!)
Still, it seems somehow wrong to create some arbitrary date just for the reason of getting the value range of possible week numbers in a given year.
So, basically, what I would like to ask is what is the reason for this and if you know any better ways to do this?
Also, I haven't had a change to try this in Java versions other than 1.8; perhaps this was improved in JDK9/10/11?

is com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl#newDurationDayTime broken?

This:
com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl sun = new com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl();
final long aTime = TimeUnit.DAYS.toMillis(32)
System.err.println(sun.newDurationDayTime(aTime));
will print P1DT0H0M0.000S, or 0 months and 1 day if I use getMonths and getDays respectively.
I realize the months should not be set given what's said in the javadocs
whose lexical representation contains only day, hour, minute, and second components.
So months being 0 seems fine, but the number of days in that case should be 32.
If I use org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl I get the expected results, 32 days or P32DT0H0M0.000S.
Is the com.sum version broken, or is there something in the contract that is not mention that the plain xerces implementation is not strict about where that method cannot be used for durations >= one month?
Edit:
I can see in DurationDayTimeImpl w/in com.sum the constructor used in this case seems to get the duration correct. After the call to super the months are 1 and the days are 1. But then after canonicalizing the time component, the years and months are wiped out:
this.years = null;
this.months = null;
This is how it ends up with a value of a single day. I can see why it clears those values given what the javadocs say, but that seems like a bug to not down convert those into days.
The plain xerces impl simply divides off the individual components; the com.sun version uses XMLGregorianCalendar.

"office hours" with jodatime, how store in database

I'm looking into storing a set of "working/office hours" for each day of the week, i.e.
mon - 9 to 5
tue - 8.30 to 16.30
...
sat 10.00 to 14.00
etc.
Using Jodatime in the service layer, I'm going to use these values to calculate which parts of certain Intervals that fall within and without these office hours.
The best way to calculate that seems to me to be by using the interval's Overlap-function, as this speedily put together example shows:
LocalTime startOfficeHours = new LocalTime(9, 30);
LocalTime endOfficeHours = new LocalTime(17, 30);
Interval officeHoursToday = new Interval(startOfficeHours.toDateTimeToday(), endOfficeHours.toDateTimeToday());
DateTime start = new DateTime().withHourOfDay(8).withMinuteOfHour(30);
DateTime end = start.plusHours(7).plusMinutes(45);
Interval workShift = new Interval(start, end);
Period withinOfficeHours = officeHoursToday.overlap(workShift).toPeriod();
assertEquals(withinOfficeHours.getHours(), 6);
assertEquals(withinOfficeHours.getMinutes(), 45);
Now, i need to find a way to read and write those office hours intervals to and from the database in a speedy fashion...
If i just use integers, that'd be 28 columns - but that might be quicker than writing and reading PersistentLocalTimes that might contain a lot of redundant info. But in both those cases i'd end up having to create intervals manually all the time.
Is there a way to store a "date-agnostic" interval in the database that i then can change the date for when i make my calculations as per above?
if anybody's been through this and has any pointers, i'd be much obliged.
You could save the two values workShift.getStartMillis() and workShift.getEndillis() (which return long in Java) to your database as two fields of type big int.
You could save the two values as a ; separated text field if you want to separate them in your code later.
You could also store workShift.toDurationMillis() in the database as big int if you want to apply the addition of that number of milliseconds yourself later rather than creating a Joda Time Interval (since there is no constructor that takes the duration as long).

Categories