mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 10:12:04 +02:00
18.0 vanilla
This commit is contained in:
parent
d72e748793
commit
0a7ae8db93
337 changed files with 399651 additions and 232598 deletions
|
|
@ -1,18 +1,27 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import math
|
||||
import calendar
|
||||
import math
|
||||
from datetime import date, datetime, time
|
||||
from typing import Tuple
|
||||
from typing import Tuple, TypeVar, Literal, Iterator, Type
|
||||
|
||||
import babel
|
||||
import pytz
|
||||
from dateutil.relativedelta import relativedelta, weekdays
|
||||
|
||||
from .func import lazy
|
||||
from odoo.loglevels import ustr
|
||||
|
||||
D = TypeVar('D', date, datetime)
|
||||
|
||||
def date_type(value):
|
||||
__all__ = [
|
||||
'date_range',
|
||||
'get_fiscal_year',
|
||||
'get_month',
|
||||
'get_quarter',
|
||||
'get_quarter_number',
|
||||
'get_timedelta',
|
||||
]
|
||||
|
||||
def date_type(value: D) -> Type[D]:
|
||||
''' Return either the datetime.datetime class or datetime.date type whether `value` is a datetime or a date.
|
||||
|
||||
:param value: A datetime.datetime or datetime.date object.
|
||||
|
|
@ -21,49 +30,39 @@ def date_type(value):
|
|||
return datetime if isinstance(value, datetime) else date
|
||||
|
||||
|
||||
def get_month(date):
|
||||
def get_month(date: D) -> Tuple[D, D]:
|
||||
''' Compute the month dates range on which the 'date' parameter belongs to.
|
||||
|
||||
:param date: A datetime.datetime or datetime.date object.
|
||||
:return: A tuple (date_from, date_to) having the same object type as the 'date' parameter.
|
||||
'''
|
||||
date_from = date_type(date)(date.year, date.month, 1)
|
||||
date_to = date_type(date)(date.year, date.month, calendar.monthrange(date.year, date.month)[1])
|
||||
return date_from, date_to
|
||||
return date.replace(day=1), date.replace(day=calendar.monthrange(date.year, date.month)[1])
|
||||
|
||||
|
||||
def get_quarter_number(date):
|
||||
def get_quarter_number(date: date) -> int:
|
||||
''' Get the number of the quarter on which the 'date' parameter belongs to.
|
||||
|
||||
:param date: A datetime.datetime or datetime.date object.
|
||||
:return: A [1-4] integer.
|
||||
'''
|
||||
return math.ceil(date.month / 3)
|
||||
|
||||
|
||||
def get_quarter(date):
|
||||
def get_quarter(date: D) -> Tuple[D, D]:
|
||||
''' Compute the quarter dates range on which the 'date' parameter belongs to.
|
||||
|
||||
:param date: A datetime.datetime or datetime.date object.
|
||||
:return: A tuple (date_from, date_to) having the same object type as the 'date' parameter.
|
||||
'''
|
||||
quarter_number = get_quarter_number(date)
|
||||
month_from = ((quarter_number - 1) * 3) + 1
|
||||
date_from = date_type(date)(date.year, month_from, 1)
|
||||
date_to = (date_from + relativedelta(months=2))
|
||||
date_from = date.replace(month=month_from, day=1)
|
||||
date_to = date_from + relativedelta(months=2)
|
||||
date_to = date_to.replace(day=calendar.monthrange(date_to.year, date_to.month)[1])
|
||||
return date_from, date_to
|
||||
|
||||
|
||||
def get_fiscal_year(date, day=31, month=12):
|
||||
def get_fiscal_year(date: D, day: int = 31, month: int = 12) -> Tuple[D, D]:
|
||||
''' Compute the fiscal year dates range on which the 'date' parameter belongs to.
|
||||
A fiscal year is the period used by governments for accounting purposes and vary between countries.
|
||||
By default, calling this method with only one parameter gives the calendar year because the ending date of the
|
||||
fiscal year is set to the YYYY-12-31.
|
||||
:param date: A datetime.datetime or datetime.date object.
|
||||
|
||||
:param date: A date belonging to the fiscal year
|
||||
:param day: The day of month the fiscal year ends.
|
||||
:param month: The month of year the fiscal year ends.
|
||||
:return: A tuple (date_from, date_to) having the same object type as the 'date' parameter.
|
||||
:return: The start and end dates of the fiscal year.
|
||||
'''
|
||||
|
||||
def fix_day(year, month, day):
|
||||
|
|
@ -72,28 +71,23 @@ def get_fiscal_year(date, day=31, month=12):
|
|||
return max_day
|
||||
return min(day, max_day)
|
||||
|
||||
day = fix_day(date.year, month, day)
|
||||
date_to = date_type(date)(date.year, month, day)
|
||||
date_to = date.replace(month=month, day=fix_day(date.year, month, day))
|
||||
|
||||
if date <= date_to:
|
||||
date_from = date_to - relativedelta(years=1)
|
||||
day = fix_day(date_from.year, date_from.month, date_from.day)
|
||||
date_from = date_type(date)(date_from.year, date_from.month, day)
|
||||
date_from = date_from.replace(day=day)
|
||||
date_from += relativedelta(days=1)
|
||||
else:
|
||||
date_from = date_to + relativedelta(days=1)
|
||||
date_to = date_to + relativedelta(years=1)
|
||||
day = fix_day(date_to.year, date_to.month, date_to.day)
|
||||
date_to = date_type(date)(date_to.year, date_to.month, day)
|
||||
date_to = date_to.replace(day=day)
|
||||
return date_from, date_to
|
||||
|
||||
|
||||
def get_timedelta(qty, granularity):
|
||||
"""
|
||||
Helper to get a `relativedelta` object for the given quantity and interval unit.
|
||||
:param qty: the number of unit to apply on the timedelta to return
|
||||
:param granularity: Type of period in string, can be year, quarter, month, week, day or hour.
|
||||
|
||||
def get_timedelta(qty: int, granularity: Literal['hour', 'day', 'week', 'month', 'year']):
|
||||
""" Helper to get a `relativedelta` object for the given quantity and interval unit.
|
||||
"""
|
||||
switch = {
|
||||
'hour': relativedelta(hours=qty),
|
||||
|
|
@ -105,7 +99,10 @@ def get_timedelta(qty, granularity):
|
|||
return switch[granularity]
|
||||
|
||||
|
||||
def start_of(value, granularity):
|
||||
Granularity = Literal['year', 'quarter', 'month', 'week', 'day', 'hour']
|
||||
|
||||
|
||||
def start_of(value: D, granularity: Granularity) -> D:
|
||||
"""
|
||||
Get start of a time period from a date or a datetime.
|
||||
|
||||
|
|
@ -144,7 +141,7 @@ def start_of(value, granularity):
|
|||
return datetime.combine(result, time.min) if is_datetime else result
|
||||
|
||||
|
||||
def end_of(value, granularity):
|
||||
def end_of(value: D, granularity: Granularity) -> D:
|
||||
"""
|
||||
Get end of a time period from a date or a datetime.
|
||||
|
||||
|
|
@ -183,7 +180,7 @@ def end_of(value, granularity):
|
|||
return datetime.combine(result, time.max) if is_datetime else result
|
||||
|
||||
|
||||
def add(value, *args, **kwargs):
|
||||
def add(value: D, *args, **kwargs) -> D:
|
||||
"""
|
||||
Return the sum of ``value`` and a :class:`relativedelta`.
|
||||
|
||||
|
|
@ -195,7 +192,7 @@ def add(value, *args, **kwargs):
|
|||
return value + relativedelta(*args, **kwargs)
|
||||
|
||||
|
||||
def subtract(value, *args, **kwargs):
|
||||
def subtract(value: D, *args, **kwargs) -> D:
|
||||
"""
|
||||
Return the difference between ``value`` and a :class:`relativedelta`.
|
||||
|
||||
|
|
@ -206,29 +203,16 @@ def subtract(value, *args, **kwargs):
|
|||
"""
|
||||
return value - relativedelta(*args, **kwargs)
|
||||
|
||||
def json_default(obj):
|
||||
"""
|
||||
Properly serializes date and datetime objects.
|
||||
"""
|
||||
from odoo import fields
|
||||
if isinstance(obj, datetime):
|
||||
return fields.Datetime.to_string(obj)
|
||||
if isinstance(obj, date):
|
||||
return fields.Date.to_string(obj)
|
||||
if isinstance(obj, lazy):
|
||||
return obj._value
|
||||
return ustr(obj)
|
||||
|
||||
|
||||
def date_range(start, end, step=relativedelta(months=1)):
|
||||
def date_range(start: D, end: D, step: relativedelta = relativedelta(months=1)) -> Iterator[datetime]:
|
||||
"""Date range generator with a step interval.
|
||||
|
||||
:param date | datetime start: beginning date of the range.
|
||||
:param date | datetime end: ending date of the range.
|
||||
:param relativedelta step: interval of the range.
|
||||
:param start: beginning date of the range.
|
||||
:param end: ending date of the range.
|
||||
:param step: interval of the range.
|
||||
:return: a range of datetime from start to end.
|
||||
:rtype: Iterator[datetime]
|
||||
"""
|
||||
|
||||
if isinstance(start, datetime) and isinstance(end, datetime):
|
||||
are_naive = start.tzinfo is None and end.tzinfo is None
|
||||
are_utc = start.tzinfo == pytz.utc and end.tzinfo == pytz.utc
|
||||
|
|
@ -247,6 +231,9 @@ def date_range(start, end, step=relativedelta(months=1)):
|
|||
post_process = start.tzinfo.localize if start.tzinfo else lambda dt: dt
|
||||
|
||||
elif isinstance(start, date) and isinstance(end, date):
|
||||
# FIXME: not correctly typed, and will break if the step is a fractional
|
||||
# day: `relativedelta` will return a datetime, which can't be
|
||||
# compared with a `date`
|
||||
dt, end_dt = start, end
|
||||
post_process = lambda dt: dt
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue