Months duration are assumed to be 30 days

Issue #29 closed
Former user created an issue

This is related to issue #12 but for non-repeating intervals.

When using the "P1M" syntax in an interval, I expect the month to be incremented but the days not to change, instead the syntax seems to be interpreted as would be "P30D":

>>> aniso8601.parse_interval("2020-01-01/P1M")
(datetime.date(2020, 1, 1), datetime.date(2020, 1, 31))
>>> aniso8601.parse_interval("2020-02-01/P1M")
(datetime.date(2020, 2, 1), datetime.date(2020, 3, 2))

I would expect:

>>> aniso8601.parse_interval("2020-01-01/P1M")
(datetime.date(2020, 1, 1), datetime.date(2020, 2, 1))
>>> aniso8601.parse_interval("2020-02-01/P1M")
(datetime.date(2020, 2, 1), datetime.date(2020, 3, 1))

Now, I might be wrong, maybe the implementation is compliant with the standard, but I think the current behavior is not intuitive.

Comments (1)

  1. Brandon Nielsen repo owner

    The issue here is twofold. First: durations are mapped (by default) onto Python timedelta objects, which do not support years / months / weeks. As such, some value needs to be chosen and 30 days is the number that was chose (365 days for years). I’m pretty sure this used to be documented better, I’ll look into that.

    The second issue is support for fractional months (and years). What does 1.2 months become? With all months fixed to 30 days, that answer is straightforward.

    Luckily, aniso8601 isn’t tied to the Python datetime library. The behavior you want is available using the relativetimebuilder for calendar level accuracy:

    >>> import aniso8601
    >>> from relativetimebuilder import RelativeTimeBuilder
    >>> aniso8601.parse_interval("2020-01-01/P1M", builder=RelativeTimeBuilder)
    (datetime.date(2020, 1, 1), datetime.date(2020, 2, 1))
    >>> aniso8601.parse_interval("2020-02-01/P1M", builder=RelativeTimeBuilder)
    (datetime.date(2020, 2, 1), datetime.date(2020, 3, 1))
    

    Notice that fractional months are explicitly not supported:

    >>> aniso8601.parse_interval("2020-02-01/P1.2M", builder=RelativeTimeBuilder)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/interval.py", line 57, in parse_interval
        return _parse_interval(isointervalstr, builder,
      File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/interval.py", line 141, in _parse_interval
        return builder.build_interval(start=starttuple,
      File "/home/nielsenb/Jetfuse/relativetimebuilder/relativetimebuilder/relativetimebuilder/__init__.py", line 110, in build_interval
        durationobject = cls._build_object(duration)
      File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 344, in _build_object
        return cls.build_duration(PnY=parsetuple.PnY, PnM=parsetuple.PnM,
      File "/home/nielsenb/Jetfuse/relativetimebuilder/relativetimebuilder/relativetimebuilder/__init__.py", line 42, in build_duration
        raise RelativeValueError('Fractional months and years are not '
    relativetimebuilder.RelativeValueError: Fractional months and years are not defined for relative durations.
    
  2. Log in to comment