Search code examples
javacalendarpersiantime4j

Define a date range in Time4j (Persian calendar)


I want to define a date range like this:

  • start from "1396/8/9"
  • end to "1396/9/2"

And get the days date between these two dates.

I searched through the Internet and I found a DateInterval function in Time4j.

Is it possible to use this method for defining optional date range in Persian calendar? If yes, how can I use this method for my purpose?


Solution

  • Using conversions

    The class DateInterval is designed for ISO-8601 only that is for the proleptic gregorian calendar. So you would be responsible yourself for conversions between the gregorian type PlainDate and the target type PersianCalendar:

    DateInterval interval =
      DateInterval.between(
        PersianCalendar.of(1396, 8, 9).transform(PlainDate.axis()),
        PersianCalendar.of(1396, 9, 2)).transform(PlainDate.axis());
    

    And for the reverse conversion:

    PersianCalendar start = interval.getStartAsCalendarDate().transform(PersianCalendar.axis());
    

    Of course, you can also loop daily through your interval:

    interval
      .streamDaily()
      .map(d -> d.transform(PersianCalendar.axis()))
      .forEach(System.out::println);
    

    Side note:

    DateInterval is closed by default (as required in most business date-related applications) but can be configured as half-open by calling interval.withOpenEnd().

    Alternative:

    There is even an approach to store instances of PersianCalendar directly in an interval, namely SimpleInterval (attention: always half-open before release v4.31!). Example:

    SimpleInterval<PersianCalendar> pInterval =
      SimpleInterval.onTimeLine(PersianCalendar.axis()).between(
        PersianCalendar.of(1396, 8, 9),
        PersianCalendar.of(1396, 9, 2).plus(CalendarDays.ONE)
      );
    

    Daily looping via an ordinary for-loop:

    for (
      PersianCalendar pcal = pInterval.getStart().getTemporal(); 
      pcal.isBefore(pInterval.getEnd().getTemporal(); 
      pcal = pcal.plus(CalendarDays.ONE)
    ) {
      System.out.println(pcal);
    }
    

    However, I don't really recommend to use SimpleInterval because a) it has less features than DateInterval and b) is mainly designed for non-calendrical types like java.util.Date (since such intervals are always half-open while calendrical intervals should better be closed in most business applications).

    Optional ranges

    About your term "optional date range", I am not clear about the meaning. Could you clarify? Maybe you are talking about infinite or half-infinite interval boundaries. This is possible. Just construct your intervals not by between(...) but by since(...) or until(...). But this has impact on your looping code (you cannot loop from or until infinity.).

    Update (2017-10-21)

    Starting with release v4.31, the class SimpleInterval has been enhanced such that it can process calendrical types in a better way, namely always closed instead of half-open (but instant-like intervals will still be half-open, of course). Some new methods were introduced for this purpose, for example on(TimeAxis). The javadoc behind the link shows an example:

     PersianCalendar start = PersianCalendar.of(1392, PersianMonth.ESFAND, 27);
     PersianCalendar end = PersianCalendar.of(1393, PersianMonth.FARVARDIN, 6);
    
     SimpleInterval<PersianCalendar> i1 =
       SimpleInterval.on(PersianCalendar.axis()).between(start, end);
     SimpleInterval<PersianCalendar> i2 =
       SimpleInterval.on(PersianCalendar.axis()).between(
         end.minus(CalendarDays.ONE),
         end.plus(CalendarDays.ONE));
    
     System.out.println(
       interval.findIntersection(
         SimpleInterval.on(PersianCalendar.axis()).between(
           end.minus(CalendarDays.ONE), end.plus(CalendarDays.ONE))).get());
     // [AP-1393-01-05/AP-1393-01-06]
    

    Such a calendar interval can be processed within IntervalCollection or IntervalTree as usual.