def test_dispatch_scale_type_on_creation(data):
    scale = parameters.Scale("amount_scale", data, "")
    first_jan = periods.Instant((2017, 11, 1))

    result = scale.get_at_instant(first_jan)

    assert isinstance(result, taxscales.SingleAmountTaxScale)
def test_assign_amounts_on_creation(data):
    scale = parameters.Scale("amount_scale", data, "")
    first_jan = periods.Instant((2017, 11, 1))
    scale_at_instant = scale.get_at_instant(first_jan)

    result = scale_at_instant.amounts

    assert result == [6]
예제 #3
0
def instant(instant):
    """Return a new instant, aka a triple of integers (year, month, day).

    >>> instant(2014)
    Instant((2014, 1, 1))
    >>> instant('2014')
    Instant((2014, 1, 1))
    >>> instant('2014-02')
    Instant((2014, 2, 1))
    >>> instant('2014-3-2')
    Instant((2014, 3, 2))
    >>> instant(instant('2014-3-2'))
    Instant((2014, 3, 2))
    >>> instant(period('month', '2014-3-2'))
    Instant((2014, 3, 2))

    >>> instant(None)
    """
    if instant is None:
        return None
    if isinstance(instant, periods.Instant):
        return instant
    if isinstance(instant, str):
        if not config.INSTANT_PATTERN.match(instant):
            raise ValueError("'{}' is not a valid instant. Instants are described using the 'YYYY-MM-DD' format, for instance '2015-06-15'.".format(instant))
        instant = periods.Instant(
            int(fragment)
            for fragment in instant.split('-', 2)[:3]
            )
    elif isinstance(instant, datetime.date):
        instant = periods.Instant((instant.year, instant.month, instant.day))
    elif isinstance(instant, int):
        instant = (instant,)
    elif isinstance(instant, list):
        assert 1 <= len(instant) <= 3
        instant = tuple(instant)
    elif isinstance(instant, periods.Period):
        instant = instant.start
    else:
        assert isinstance(instant, tuple), instant
        assert 1 <= len(instant) <= 3
    if len(instant) == 1:
        return periods.Instant((instant[0], 1, 1))
    if len(instant) == 2:
        return periods.Instant((instant[0], instant[1], 1))
    return periods.Instant(instant)
예제 #4
0
 def parse_simple_period(value):
     """
     Parses simple periods respecting the ISO format, such as 2012 or 2015-03
     """
     try:
         date = datetime.datetime.strptime(value, '%Y')
     except ValueError:
         try:
             date = datetime.datetime.strptime(value, '%Y-%m')
         except ValueError:
             try:
                 date = datetime.datetime.strptime(value, '%Y-%m-%d')
             except ValueError:
                 return None
             else:
                 return periods.Period((config.DAY, periods.Instant((date.year, date.month, date.day)), 1))
         else:
             return periods.Period((config.MONTH, periods.Instant((date.year, date.month, 1)), 1))
     else:
         return periods.Period((config.YEAR, periods.Instant((date.year, date.month, 1)), 1))
예제 #5
0
def str_to_instant(s):
    return periods.Instant(tuple(map(lambda s: int(s), s.split("-"))))
예제 #6
0
def period(value):
    """Return a new period, aka a triple (unit, start_instant, size).

    >>> period('2014')
    Period((YEAR, Instant((2014, 1, 1)), 1))
    >>> period('year:2014')
    Period((YEAR, Instant((2014, 1, 1)), 1))

    >>> period('2014-2')
    Period((MONTH, Instant((2014, 2, 1)), 1))
    >>> period('2014-02')
    Period((MONTH, Instant((2014, 2, 1)), 1))
    >>> period('month:2014-2')
    Period((MONTH, Instant((2014, 2, 1)), 1))

    >>> period('year:2014-2')
    Period((YEAR, Instant((2014, 2, 1)), 1))
    """
    if isinstance(value, periods.Period):
        return value

    if isinstance(value, periods.Instant):
        return periods.Period((config.DAY, value, 1))

    def parse_simple_period(value):
        """
        Parses simple periods respecting the ISO format, such as 2012 or 2015-03
        """
        try:
            date = datetime.datetime.strptime(value, '%Y')
        except ValueError:
            try:
                date = datetime.datetime.strptime(value, '%Y-%m')
            except ValueError:
                try:
                    date = datetime.datetime.strptime(value, '%Y-%m-%d')
                except ValueError:
                    return None
                else:
                    return periods.Period((config.DAY, periods.Instant((date.year, date.month, date.day)), 1))
            else:
                return periods.Period((config.MONTH, periods.Instant((date.year, date.month, 1)), 1))
        else:
            return periods.Period((config.YEAR, periods.Instant((date.year, date.month, 1)), 1))

    def raise_error(value):
        message = os.linesep.join([
            "Expected a period (eg. '2017', '2017-01', '2017-01-01', ...); got: '{}'.".format(value),
            "Learn more about legal period formats in OpenFisca:",
            "<https://openfisca.org/doc/coding-the-legislation/35_periods.html#periods-in-simulations>."
            ])
        raise ValueError(message)

    if value == 'ETERNITY' or value == config.ETERNITY:
        return periods.Period(('eternity', instant(datetime.date.min), float("inf")))

    # check the type
    if isinstance(value, int):
        return periods.Period((config.YEAR, periods.Instant((value, 1, 1)), 1))
    if not isinstance(value, str):
        raise_error(value)

    # try to parse as a simple period
    period = parse_simple_period(value)
    if period is not None:
        return period

    # complex period must have a ':' in their strings
    if ":" not in value:
        raise_error(value)

    components = value.split(':')

    # left-most component must be a valid unit
    unit = components[0]
    if unit not in (config.DAY, config.MONTH, config.YEAR):
        raise_error(value)

    # middle component must be a valid iso period
    base_period = parse_simple_period(components[1])
    if not base_period:
        raise_error(value)

    # period like year:2015-03 have a size of 1
    if len(components) == 2:
        size = 1
    # if provided, make sure the size is an integer
    elif len(components) == 3:
        try:
            size = int(components[2])
        except ValueError:
            raise_error(value)
    # if there is more than 2 ":" in the string, the period is invalid
    else:
        raise_error(value)

    # reject ambiguous period such as month:2014
    if unit_weight(base_period.unit) > unit_weight(unit):
        raise_error(value)

    return periods.Period((unit, base_period.start, size))
예제 #7
0
    def stop(self) -> periods.Instant:
        """
        Return the last day of the period as an Instant instance.

        >>> period('year', 2014).stop
        Instant((2014, 12, 31))
        >>> period('month', 2014).stop
        Instant((2014, 12, 31))
        >>> period('day', 2014).stop
        Instant((2014, 12, 31))

        >>> period('year', '2012-2-29').stop
        Instant((2013, 2, 28))
        >>> period('month', '2012-2-29').stop
        Instant((2012, 3, 28))
        >>> period('day', '2012-2-29').stop
        Instant((2012, 2, 29))

        >>> period('year', '2012-2-29', 2).stop
        Instant((2014, 2, 28))
        >>> period('month', '2012-2-29', 2).stop
        Instant((2012, 4, 28))
        >>> period('day', '2012-2-29', 2).stop
        Instant((2012, 3, 1))
        """
        unit, start_instant, size = self
        year, month, day = start_instant
        if unit == config.ETERNITY:
            return periods.Instant((float("inf"), float("inf"), float("inf")))
        if unit == 'day':
            if size > 1:
                day += size - 1
                month_last_day = calendar.monthrange(year, month)[1]
                while day > month_last_day:
                    month += 1
                    if month == 13:
                        year += 1
                        month = 1
                    day -= month_last_day
                    month_last_day = calendar.monthrange(year, month)[1]
        else:
            if unit == 'month':
                month += size
                while month > 12:
                    year += 1
                    month -= 12
            else:
                assert unit == 'year', 'Invalid unit: {} of type {}'.format(unit, type(unit))
                year += size
            day -= 1
            if day < 1:
                month -= 1
                if month == 0:
                    year -= 1
                    month = 12
                day += calendar.monthrange(year, month)[1]
            else:
                month_last_day = calendar.monthrange(year, month)[1]
                if day > month_last_day:
                    month += 1
                    if month == 13:
                        year += 1
                        month = 1
                    day -= month_last_day
        return periods.Instant((year, month, day))