diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-12-17 14:36:26 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-12-17 14:36:33 +0000 |
commit | 665666d6f4213da8db57ebb480947b7caf1fe382 (patch) | |
tree | 0cac5d322dfe861a6de62b04fb916cef6dbe4510 /pendulum | |
parent | Releasing debian version 3.0.0~a1-2. (diff) | |
download | pendulum-665666d6f4213da8db57ebb480947b7caf1fe382.tar.xz pendulum-665666d6f4213da8db57ebb480947b7caf1fe382.zip |
Merging upstream version 3.0.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pendulum')
107 files changed, 0 insertions, 14138 deletions
diff --git a/pendulum/__init__.py b/pendulum/__init__.py deleted file mode 100644 index 4491244..0000000 --- a/pendulum/__init__.py +++ /dev/null @@ -1,363 +0,0 @@ -from __future__ import annotations - -import datetime as _datetime - -from typing import Union -from typing import cast - -from pendulum.__version__ import __version__ -from pendulum.constants import DAYS_PER_WEEK -from pendulum.constants import FRIDAY -from pendulum.constants import HOURS_PER_DAY -from pendulum.constants import MINUTES_PER_HOUR -from pendulum.constants import MONDAY -from pendulum.constants import MONTHS_PER_YEAR -from pendulum.constants import SATURDAY -from pendulum.constants import SECONDS_PER_DAY -from pendulum.constants import SECONDS_PER_HOUR -from pendulum.constants import SECONDS_PER_MINUTE -from pendulum.constants import SUNDAY -from pendulum.constants import THURSDAY -from pendulum.constants import TUESDAY -from pendulum.constants import WEDNESDAY -from pendulum.constants import WEEKS_PER_YEAR -from pendulum.constants import YEARS_PER_CENTURY -from pendulum.constants import YEARS_PER_DECADE -from pendulum.date import Date -from pendulum.datetime import DateTime -from pendulum.duration import Duration -from pendulum.formatting import Formatter -from pendulum.helpers import format_diff -from pendulum.helpers import get_locale -from pendulum.helpers import locale -from pendulum.helpers import set_locale -from pendulum.helpers import week_ends_at -from pendulum.helpers import week_starts_at -from pendulum.interval import Interval -from pendulum.parser import parse -from pendulum.testing.traveller import Traveller -from pendulum.time import Time -from pendulum.tz import UTC -from pendulum.tz import local_timezone -from pendulum.tz import set_local_timezone -from pendulum.tz import test_local_timezone -from pendulum.tz import timezone -from pendulum.tz import timezones -from pendulum.tz.timezone import FixedTimezone -from pendulum.tz.timezone import Timezone - -_TEST_NOW: DateTime | None = None -_LOCALE = "en" -_WEEK_STARTS_AT = MONDAY -_WEEK_ENDS_AT = SUNDAY - -_formatter = Formatter() - - -def _safe_timezone( - obj: str | float | _datetime.tzinfo | Timezone | FixedTimezone | None, - dt: _datetime.datetime | None = None, -) -> Timezone | FixedTimezone: - """ - Creates a timezone instance - from a string, Timezone, TimezoneInfo or integer offset. - """ - if isinstance(obj, (Timezone, FixedTimezone)): - return obj - - if obj is None or obj == "local": - return local_timezone() - - if isinstance(obj, (int, float)): - obj = int(obj * 60 * 60) - elif isinstance(obj, _datetime.tzinfo): - # zoneinfo - if hasattr(obj, "key"): - obj = obj.key # type: ignore - # pytz - elif hasattr(obj, "localize"): - obj = obj.zone # type: ignore - elif obj.tzname(None) == "UTC": - return UTC - else: - offset = obj.utcoffset(dt) - - if offset is None: - offset = _datetime.timedelta(0) - - obj = int(offset.total_seconds()) - - obj = cast(Union[str, int], obj) - - return timezone(obj) - - -# Public API -def datetime( - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - second: int = 0, - microsecond: int = 0, - tz: str | float | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC, - fold: int = 1, - raise_on_unknown_times: bool = False, -) -> DateTime: - """ - Creates a new DateTime instance from a specific date and time. - """ - return DateTime.create( - year, - month, - day, - hour=hour, - minute=minute, - second=second, - microsecond=microsecond, - tz=tz, - fold=fold, - raise_on_unknown_times=raise_on_unknown_times, - ) - - -def local( - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - second: int = 0, - microsecond: int = 0, -) -> DateTime: - """ - Return a DateTime in the local timezone. - """ - return datetime( - year, month, day, hour, minute, second, microsecond, tz=local_timezone() - ) - - -def naive( - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - second: int = 0, - microsecond: int = 0, - fold: int = 1, -) -> DateTime: - """ - Return a naive DateTime. - """ - return DateTime(year, month, day, hour, minute, second, microsecond, fold=fold) - - -def date(year: int, month: int, day: int) -> Date: - """ - Create a new Date instance. - """ - return Date(year, month, day) - - -def time(hour: int, minute: int = 0, second: int = 0, microsecond: int = 0) -> Time: - """ - Create a new Time instance. - """ - return Time(hour, minute, second, microsecond) - - -def instance( - dt: _datetime.datetime, - tz: str | Timezone | FixedTimezone | _datetime.tzinfo | None = UTC, -) -> DateTime: - """ - Create a DateTime instance from a datetime one. - """ - if not isinstance(dt, _datetime.datetime): - raise ValueError("instance() only accepts datetime objects.") - - if isinstance(dt, DateTime): - return dt - - tz = dt.tzinfo or tz - - if tz is not None: - tz = _safe_timezone(tz, dt=dt) - - return datetime( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tz=cast(Union[str, int, Timezone, FixedTimezone, None], tz), - ) - - -def now(tz: str | Timezone | None = None) -> DateTime: - """ - Get a DateTime instance for the current date and time. - """ - return DateTime.now(tz) - - -def today(tz: str | Timezone = "local") -> DateTime: - """ - Create a DateTime instance for today. - """ - return now(tz).start_of("day") - - -def tomorrow(tz: str | Timezone = "local") -> DateTime: - """ - Create a DateTime instance for today. - """ - return today(tz).add(days=1) - - -def yesterday(tz: str | Timezone = "local") -> DateTime: - """ - Create a DateTime instance for today. - """ - return today(tz).subtract(days=1) - - -def from_format( - string: str, - fmt: str, - tz: str | Timezone = UTC, - locale: str | None = None, -) -> DateTime: - """ - Creates a DateTime instance from a specific format. - """ - parts = _formatter.parse(string, fmt, now(tz=tz), locale=locale) - if parts["tz"] is None: - parts["tz"] = tz - - return datetime(**parts) - - -def from_timestamp(timestamp: int | float, tz: str | Timezone = UTC) -> DateTime: - """ - Create a DateTime instance from a timestamp. - """ - dt = _datetime.datetime.utcfromtimestamp(timestamp) - - dt = datetime( - dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond - ) - - if tz is not UTC or tz != "UTC": - dt = dt.in_timezone(tz) - - return dt - - -def duration( - days: float = 0, - seconds: float = 0, - microseconds: float = 0, - milliseconds: float = 0, - minutes: float = 0, - hours: float = 0, - weeks: float = 0, - years: float = 0, - months: float = 0, -) -> Duration: - """ - Create a Duration instance. - """ - return Duration( - days=days, - seconds=seconds, - microseconds=microseconds, - milliseconds=milliseconds, - minutes=minutes, - hours=hours, - weeks=weeks, - years=years, - months=months, - ) - - -def interval(start: DateTime, end: DateTime, absolute: bool = False) -> Interval: - """ - Create an Interval instance. - """ - return Interval(start, end, absolute=absolute) - - -# Testing - -_traveller = Traveller(DateTime) - -freeze = _traveller.freeze -travel = _traveller.travel -travel_to = _traveller.travel_to -travel_back = _traveller.travel_back - -__all__ = [ - "__version__", - "DAYS_PER_WEEK", - "FRIDAY", - "HOURS_PER_DAY", - "MINUTES_PER_HOUR", - "MONDAY", - "MONTHS_PER_YEAR", - "SATURDAY", - "SECONDS_PER_DAY", - "SECONDS_PER_HOUR", - "SECONDS_PER_MINUTE", - "SUNDAY", - "THURSDAY", - "TUESDAY", - "WEDNESDAY", - "WEEKS_PER_YEAR", - "YEARS_PER_CENTURY", - "YEARS_PER_DECADE", - "Date", - "DateTime", - "Duration", - "Formatter", - "date", - "datetime", - "duration", - "format_diff", - "freeze", - "from_format", - "from_timestamp", - "get_locale", - "instance", - "interval", - "local", - "locale", - "naive", - "now", - "set_locale", - "week_ends_at", - "week_starts_at", - "parse", - "Interval", - "Time", - "UTC", - "local_timezone", - "set_local_timezone", - "test_local_timezone", - "time", - "timezone", - "timezones", - "today", - "tomorrow", - "travel", - "travel_back", - "travel_to", - "FixedTimezone", - "Timezone", - "yesterday", -] diff --git a/pendulum/__version__.py b/pendulum/__version__.py deleted file mode 100644 index 29e86a8..0000000 --- a/pendulum/__version__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "3.0.0a" diff --git a/pendulum/_extensions/__init__.py b/pendulum/_extensions/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/_extensions/__init__.py +++ /dev/null diff --git a/pendulum/_extensions/_helpers.c b/pendulum/_extensions/_helpers.c deleted file mode 100644 index a3114d9..0000000 --- a/pendulum/_extensions/_helpers.c +++ /dev/null @@ -1,931 +0,0 @@ -/* ------------------------------------------------------------------------- */ - -#include <Python.h> -#include <datetime.h> -#include <structmember.h> -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -/* ------------------------------------------------------------------------- */ - -#define EPOCH_YEAR 1970 - -#define DAYS_PER_N_YEAR 365 -#define DAYS_PER_L_YEAR 366 - -#define USECS_PER_SEC 1000000 - -#define SECS_PER_MIN 60 -#define SECS_PER_HOUR (60 * SECS_PER_MIN) -#define SECS_PER_DAY (SECS_PER_HOUR * 24) - -// 400-year chunks always have 146097 days (20871 weeks). -#define DAYS_PER_400_YEARS 146097L -#define SECS_PER_400_YEARS ((int64_t)DAYS_PER_400_YEARS * (int64_t)SECS_PER_DAY) - -// The number of seconds in an aligned 100-year chunk, for those that -// do not begin with a leap year and those that do respectively. -const int64_t SECS_PER_100_YEARS[2] = { - (uint64_t)(76L * DAYS_PER_N_YEAR + 24L * DAYS_PER_L_YEAR) * SECS_PER_DAY, - (uint64_t)(75L * DAYS_PER_N_YEAR + 25L * DAYS_PER_L_YEAR) * SECS_PER_DAY}; - -// The number of seconds in an aligned 4-year chunk, for those that -// do not begin with a leap year and those that do respectively. -const int32_t SECS_PER_4_YEARS[2] = { - (4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY, - (3 * DAYS_PER_N_YEAR + 1 * DAYS_PER_L_YEAR) * SECS_PER_DAY}; - -// The number of seconds in non-leap and leap years respectively. -const int32_t SECS_PER_YEAR[2] = { - DAYS_PER_N_YEAR * SECS_PER_DAY, - DAYS_PER_L_YEAR *SECS_PER_DAY}; - -#define MONTHS_PER_YEAR 12 - -// The month lengths in non-leap and leap years respectively. -const int32_t DAYS_PER_MONTHS[2][13] = { - {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; - -// The day offsets of the beginning of each (1-based) month in non-leap -// and leap years respectively. -// For example, in a leap year there are 335 days before December. -const int32_t MONTHS_OFFSETS[2][14] = { - {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, - {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}}; - -const int DAY_OF_WEEK_TABLE[12] = { - 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -/* ------------------------------------------------------------------------- */ - -int _p(int y) -{ - return y + y / 4 - y / 100 + y / 400; -} - -int _is_leap(int year) -{ - return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); -} - -int _is_long_year(int year) -{ - return (_p(year) % 7 == 4) || (_p(year - 1) % 7 == 3); -} - -int _week_day(int year, int month, int day) -{ - int y; - int w; - - y = year - (month < 3); - - w = (_p(y) + DAY_OF_WEEK_TABLE[month - 1] + day) % 7; - - if (!w) - { - w = 7; - } - - return w; -} - -int _days_in_year(int year) -{ - if (_is_leap(year)) - { - return DAYS_PER_L_YEAR; - } - - return DAYS_PER_N_YEAR; -} - -int _day_number(int year, int month, int day) -{ - month = (month + 9) % 12; - year = year - month / 10; - - return ( - 365 * year + year / 4 - year / 100 + year / 400 + (month * 306 + 5) / 10 + (day - 1)); -} - -int _get_offset(PyObject *dt) -{ - PyObject *tzinfo; - PyObject *offset; - - tzinfo = ((PyDateTime_DateTime *)(dt))->tzinfo; - - if (tzinfo != Py_None) - { - offset = PyObject_CallMethod(tzinfo, "utcoffset", "O", dt); - - return PyDateTime_DELTA_GET_DAYS(offset) * SECS_PER_DAY + PyDateTime_DELTA_GET_SECONDS(offset); - } - - return 0; -} - -int _has_tzinfo(PyObject *dt) -{ - return ((_PyDateTime_BaseTZInfo *)(dt))->hastzinfo; -} - -char *_get_tz_name(PyObject *dt) -{ - PyObject *tzinfo; - char *tz = ""; - - tzinfo = ((PyDateTime_DateTime *)(dt))->tzinfo; - - if (tzinfo != Py_None) - { - if (PyObject_HasAttrString(tzinfo, "key")) - { - // zoneinfo timezone - tz = (char *)PyUnicode_AsUTF8( - PyObject_GetAttrString(tzinfo, "name")); - } - else if (PyObject_HasAttrString(tzinfo, "name")) - { - // Pendulum timezone - tz = (char *)PyUnicode_AsUTF8( - PyObject_GetAttrString(tzinfo, "name")); - } - else if (PyObject_HasAttrString(tzinfo, "zone")) - { - // pytz timezone - tz = (char *)PyUnicode_AsUTF8( - PyObject_GetAttrString(tzinfo, "zone")); - } - } - - return tz; -} - -/* ------------------------ Custom Types ------------------------------- */ - -/* - * class Diff(): - */ -typedef struct -{ - PyObject_HEAD int years; - int months; - int days; - int hours; - int minutes; - int seconds; - int microseconds; - int total_days; -} Diff; - -/* - * def __init__(self, years, months, days, hours, minutes, seconds, microseconds, total_days): - * self.years = years - * # ... -*/ -static int Diff_init(Diff *self, PyObject *args, PyObject *kwargs) -{ - int years; - int months; - int days; - int hours; - int minutes; - int seconds; - int microseconds; - int total_days; - - if (!PyArg_ParseTuple(args, "iiiiiii", &years, &months, &days, &hours, &minutes, &seconds, µseconds, &total_days)) - return -1; - - self->years = years; - self->months = months; - self->days = days; - self->hours = hours; - self->minutes = minutes; - self->seconds = seconds; - self->microseconds = microseconds; - self->total_days = total_days; - - return 0; -} - -/* - * def __repr__(self): - * return '{} years {} months {} days {} hours {} minutes {} seconds {} microseconds'.format( - * self.years, self.months, self.days, self.minutes, self.hours, self.seconds, self.microseconds - * ) - */ -static PyObject *Diff_repr(Diff *self) -{ - return PyUnicode_FromFormat( - "%d years %d months %d days %d hours %d minutes %d seconds %d microseconds", - self->years, - self->months, - self->days, - self->hours, - self->minutes, - self->seconds, - self->microseconds); -} - -/* - * Instantiate new Diff_type object - * Skip overhead of calling PyObject_New and PyObject_Init. - * Directly allocate object. - */ -static PyObject *new_diff_ex(int years, int months, int days, int hours, int minutes, int seconds, int microseconds, int total_days, PyTypeObject *type) -{ - Diff *self = (Diff *)(type->tp_alloc(type, 0)); - - if (self != NULL) - { - self->years = years; - self->months = months; - self->days = days; - self->hours = hours; - self->minutes = minutes; - self->seconds = seconds; - self->microseconds = microseconds; - self->total_days = total_days; - } - - return (PyObject *)self; -} - -/* - * Class member / class attributes - */ -static PyMemberDef Diff_members[] = { - {"years", T_INT, offsetof(Diff, years), 0, "years in diff"}, - {"months", T_INT, offsetof(Diff, months), 0, "months in diff"}, - {"days", T_INT, offsetof(Diff, days), 0, "days in diff"}, - {"hours", T_INT, offsetof(Diff, hours), 0, "hours in diff"}, - {"minutes", T_INT, offsetof(Diff, minutes), 0, "minutes in diff"}, - {"seconds", T_INT, offsetof(Diff, seconds), 0, "seconds in diff"}, - {"microseconds", T_INT, offsetof(Diff, microseconds), 0, "microseconds in diff"}, - {"total_days", T_INT, offsetof(Diff, total_days), 0, "total days in diff"}, - {NULL}}; - -static PyTypeObject Diff_type = { - PyVarObject_HEAD_INIT(NULL, 0) "PreciseDiff", /* tp_name */ - sizeof(Diff), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)Diff_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)Diff_repr, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Precise difference between two datetime objects", /* tp_doc */ -}; - -#define new_diff(years, months, days, hours, minutes, seconds, microseconds, total_days) new_diff_ex(years, months, days, hours, minutes, seconds, microseconds, total_days, &Diff_type) - -/* -------------------------- Functions --------------------------*/ - -PyObject *is_leap(PyObject *self, PyObject *args) -{ - PyObject *leap; - int year; - - if (!PyArg_ParseTuple(args, "i", &year)) - { - PyErr_SetString( - PyExc_ValueError, "Invalid parameters"); - return NULL; - } - - leap = PyBool_FromLong(_is_leap(year)); - - return leap; -} - -PyObject *is_long_year(PyObject *self, PyObject *args) -{ - PyObject *is_long; - int year; - - if (!PyArg_ParseTuple(args, "i", &year)) - { - PyErr_SetString( - PyExc_ValueError, "Invalid parameters"); - return NULL; - } - - is_long = PyBool_FromLong(_is_long_year(year)); - - return is_long; -} - -PyObject *week_day(PyObject *self, PyObject *args) -{ - PyObject *wd; - int year; - int month; - int day; - - if (!PyArg_ParseTuple(args, "iii", &year, &month, &day)) - { - PyErr_SetString( - PyExc_ValueError, "Invalid parameters"); - return NULL; - } - - wd = PyLong_FromLong(_week_day(year, month, day)); - - return wd; -} - -PyObject *days_in_year(PyObject *self, PyObject *args) -{ - PyObject *ndays; - int year; - - if (!PyArg_ParseTuple(args, "i", &year)) - { - PyErr_SetString( - PyExc_ValueError, "Invalid parameters"); - return NULL; - } - - ndays = PyLong_FromLong(_days_in_year(year)); - - return ndays; -} - -PyObject *timestamp(PyObject *self, PyObject *args) -{ - int64_t result; - PyObject *dt; - - if (!PyArg_ParseTuple(args, "O", &dt)) - { - PyErr_SetString( - PyExc_ValueError, "Invalid parameters"); - return NULL; - } - - int year = (double)PyDateTime_GET_YEAR(dt); - int month = PyDateTime_GET_MONTH(dt); - int day = PyDateTime_GET_DAY(dt); - int hour = PyDateTime_DATE_GET_HOUR(dt); - int minute = PyDateTime_DATE_GET_MINUTE(dt); - int second = PyDateTime_DATE_GET_SECOND(dt); - - result = (year - 1970) * 365 + MONTHS_OFFSETS[0][month]; - result += (int)floor((double)(year - 1968) / 4); - result -= (year - 1900) / 100; - result += (year - 1600) / 400; - - if (_is_leap(year) && month < 3) - { - result -= 1; - } - - result += day - 1; - result *= 24; - result += hour; - result *= 60; - result += minute; - result *= 60; - result += second; - - return PyLong_FromSsize_t(result); -} - -PyObject *local_time(PyObject *self, PyObject *args) -{ - double unix_time; - int32_t utc_offset; - int32_t year; - int32_t microsecond; - int64_t seconds; - int32_t leap_year; - int64_t sec_per_100years; - int64_t sec_per_4years; - int32_t sec_per_year; - int32_t month; - int32_t day; - int32_t month_offset; - int32_t hour; - int32_t minute; - int32_t second; - - if (!PyArg_ParseTuple(args, "dii", &unix_time, &utc_offset, µsecond)) - { - PyErr_SetString( - PyExc_ValueError, "Invalid parameters"); - return NULL; - } - - year = EPOCH_YEAR; - seconds = (int64_t)floor(unix_time); - - // Shift to a base year that is 400-year aligned. - if (seconds >= 0) - { - seconds -= 10957L * SECS_PER_DAY; - year += 30; // == 2000; - } - else - { - seconds += (int64_t)(146097L - 10957L) * SECS_PER_DAY; - year -= 370; // == 1600; - } - - seconds += utc_offset; - - // Handle years in chunks of 400/100/4/1 - year += 400 * (seconds / SECS_PER_400_YEARS); - seconds %= SECS_PER_400_YEARS; - if (seconds < 0) - { - seconds += SECS_PER_400_YEARS; - year -= 400; - } - - leap_year = 1; // 4-century aligned - - sec_per_100years = SECS_PER_100_YEARS[leap_year]; - - while (seconds >= sec_per_100years) - { - seconds -= sec_per_100years; - year += 100; - leap_year = 0; // 1-century, non 4-century aligned - sec_per_100years = SECS_PER_100_YEARS[leap_year]; - } - - sec_per_4years = SECS_PER_4_YEARS[leap_year]; - while (seconds >= sec_per_4years) - { - seconds -= sec_per_4years; - year += 4; - leap_year = 1; // 4-year, non century aligned - sec_per_4years = SECS_PER_4_YEARS[leap_year]; - } - - sec_per_year = SECS_PER_YEAR[leap_year]; - while (seconds >= sec_per_year) - { - seconds -= sec_per_year; - year += 1; - leap_year = 0; // non 4-year aligned - sec_per_year = SECS_PER_YEAR[leap_year]; - } - - // Handle months and days - month = TM_DECEMBER + 1; - day = seconds / SECS_PER_DAY + 1; - seconds %= SECS_PER_DAY; - while (month != TM_JANUARY + 1) - { - month_offset = MONTHS_OFFSETS[leap_year][month]; - if (day > month_offset) - { - day -= month_offset; - break; - } - - month -= 1; - } - - // Handle hours, minutes and seconds - hour = seconds / SECS_PER_HOUR; - seconds %= SECS_PER_HOUR; - minute = seconds / SECS_PER_MIN; - second = seconds % SECS_PER_MIN; - - return Py_BuildValue("NNNNNNN", - PyLong_FromLong(year), - PyLong_FromLong(month), - PyLong_FromLong(day), - PyLong_FromLong(hour), - PyLong_FromLong(minute), - PyLong_FromLong(second), - PyLong_FromLong(microsecond)); -} - -// Calculate a precise difference between two datetimes. -PyObject *precise_diff(PyObject *self, PyObject *args) -{ - PyObject *dt1; - PyObject *dt2; - - if (!PyArg_ParseTuple(args, "OO", &dt1, &dt2)) - { - PyErr_SetString( - PyExc_ValueError, "Invalid parameters"); - return NULL; - } - - int year_diff = 0; - int month_diff = 0; - int day_diff = 0; - int hour_diff = 0; - int minute_diff = 0; - int second_diff = 0; - int microsecond_diff = 0; - int sign = 1; - int year; - int month; - int leap; - int days_in_last_month; - int days_in_month; - int dt1_year = PyDateTime_GET_YEAR(dt1); - int dt2_year = PyDateTime_GET_YEAR(dt2); - int dt1_month = PyDateTime_GET_MONTH(dt1); - int dt2_month = PyDateTime_GET_MONTH(dt2); - int dt1_day = PyDateTime_GET_DAY(dt1); - int dt2_day = PyDateTime_GET_DAY(dt2); - int dt1_hour = 0; - int dt2_hour = 0; - int dt1_minute = 0; - int dt2_minute = 0; - int dt1_second = 0; - int dt2_second = 0; - int dt1_microsecond = 0; - int dt2_microsecond = 0; - int dt1_total_seconds = 0; - int dt2_total_seconds = 0; - int dt1_offset = 0; - int dt2_offset = 0; - int dt1_is_datetime = PyDateTime_Check(dt1); - int dt2_is_datetime = PyDateTime_Check(dt2); - char *tz1 = ""; - char *tz2 = ""; - int in_same_tz = 0; - int total_days = (_day_number(dt2_year, dt2_month, dt2_day) - _day_number(dt1_year, dt1_month, dt1_day)); - - // If both dates are datetimes, we check - // If we are in the same timezone - if (dt1_is_datetime && dt2_is_datetime) - { - if (_has_tzinfo(dt1)) - { - tz1 = _get_tz_name(dt1); - dt1_offset = _get_offset(dt1); - } - - if (_has_tzinfo(dt2)) - { - tz2 = _get_tz_name(dt2); - dt2_offset = _get_offset(dt2); - } - - in_same_tz = tz1 == tz2 && strncmp(tz1, "", 1); - } - - // If we have datetimes (and not only dates) - // we get the information we need - if (dt1_is_datetime) - { - dt1_hour = PyDateTime_DATE_GET_HOUR(dt1); - dt1_minute = PyDateTime_DATE_GET_MINUTE(dt1); - dt1_second = PyDateTime_DATE_GET_SECOND(dt1); - dt1_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt1); - - if ((!in_same_tz && dt1_offset != 0) || total_days == 0) - { - dt1_hour -= dt1_offset / SECS_PER_HOUR; - dt1_offset %= SECS_PER_HOUR; - dt1_minute -= dt1_offset / SECS_PER_MIN; - dt1_offset %= SECS_PER_MIN; - dt1_second -= dt1_offset; - - if (dt1_second < 0) - { - dt1_second += 60; - dt1_minute -= 1; - } - else if (dt1_second > 60) - { - dt1_second -= 60; - dt1_minute += 1; - } - - if (dt1_minute < 0) - { - dt1_minute += 60; - dt1_hour -= 1; - } - else if (dt1_minute > 60) - { - dt1_minute -= 60; - dt1_hour += 1; - } - - if (dt1_hour < 0) - { - dt1_hour += 24; - dt1_day -= 1; - } - else if (dt1_hour > 24) - { - dt1_hour -= 24; - dt1_day += 1; - } - } - - dt1_total_seconds = (dt1_hour * SECS_PER_HOUR + dt1_minute * SECS_PER_MIN + dt1_second); - } - - if (dt2_is_datetime) - { - dt2_hour = PyDateTime_DATE_GET_HOUR(dt2); - dt2_minute = PyDateTime_DATE_GET_MINUTE(dt2); - dt2_second = PyDateTime_DATE_GET_SECOND(dt2); - dt2_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt2); - - if ((!in_same_tz && dt2_offset != 0) || total_days == 0) - { - dt2_hour -= dt2_offset / SECS_PER_HOUR; - dt2_offset %= SECS_PER_HOUR; - dt2_minute -= dt2_offset / SECS_PER_MIN; - dt2_offset %= SECS_PER_MIN; - dt2_second -= dt2_offset; - - if (dt2_second < 0) - { - dt2_second += 60; - dt2_minute -= 1; - } - else if (dt2_second > 60) - { - dt2_second -= 60; - dt2_minute += 1; - } - - if (dt2_minute < 0) - { - dt2_minute += 60; - dt2_hour -= 1; - } - else if (dt2_minute > 60) - { - dt2_minute -= 60; - dt2_hour += 1; - } - - if (dt2_hour < 0) - { - dt2_hour += 24; - dt2_day -= 1; - } - else if (dt2_hour > 24) - { - dt2_hour -= 24; - dt2_day += 1; - } - } - - dt2_total_seconds = (dt2_hour * SECS_PER_HOUR + dt2_minute * SECS_PER_MIN + dt2_second); - } - - // Direct comparison between two datetimes does not work - // so we need to check by properties - int dt1_gt_dt2 = (dt1_year > dt2_year || (dt1_year == dt2_year && dt1_month > dt2_month) || (dt1_year == dt2_year && dt1_month == dt2_month && dt1_day > dt2_day) || (dt1_year == dt2_year && dt1_month == dt2_month && dt1_day == dt2_day && dt1_total_seconds > dt2_total_seconds) || (dt1_year == dt2_year && dt1_month == dt2_month && dt1_day == dt2_day && dt1_total_seconds == dt2_total_seconds && dt1_microsecond > dt2_microsecond)); - - if (dt1_gt_dt2) - { - PyObject *temp; - temp = dt1; - dt1 = dt2; - dt2 = temp; - sign = -1; - - // Retrieving properties - dt1_year = PyDateTime_GET_YEAR(dt1); - dt2_year = PyDateTime_GET_YEAR(dt2); - dt1_month = PyDateTime_GET_MONTH(dt1); - dt2_month = PyDateTime_GET_MONTH(dt2); - dt1_day = PyDateTime_GET_DAY(dt1); - dt2_day = PyDateTime_GET_DAY(dt2); - - if (dt2_is_datetime) - { - dt1_hour = PyDateTime_DATE_GET_HOUR(dt1); - dt1_minute = PyDateTime_DATE_GET_MINUTE(dt1); - dt1_second = PyDateTime_DATE_GET_SECOND(dt1); - dt1_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt1); - } - - if (dt1_is_datetime) - { - dt2_hour = PyDateTime_DATE_GET_HOUR(dt2); - dt2_minute = PyDateTime_DATE_GET_MINUTE(dt2); - dt2_second = PyDateTime_DATE_GET_SECOND(dt2); - dt2_microsecond = PyDateTime_DATE_GET_MICROSECOND(dt2); - } - - total_days = (_day_number(dt2_year, dt2_month, dt2_day) - _day_number(dt1_year, dt1_month, dt1_day)); - } - - year_diff = dt2_year - dt1_year; - month_diff = dt2_month - dt1_month; - day_diff = dt2_day - dt1_day; - hour_diff = dt2_hour - dt1_hour; - minute_diff = dt2_minute - dt1_minute; - second_diff = dt2_second - dt1_second; - microsecond_diff = dt2_microsecond - dt1_microsecond; - - if (microsecond_diff < 0) - { - microsecond_diff += 1e6; - second_diff -= 1; - } - - if (second_diff < 0) - { - second_diff += 60; - minute_diff -= 1; - } - - if (minute_diff < 0) - { - minute_diff += 60; - hour_diff -= 1; - } - - if (hour_diff < 0) - { - hour_diff += 24; - day_diff -= 1; - } - - if (day_diff < 0) - { - // If we have a difference in days, - // we have to check if they represent months - year = dt2_year; - month = dt2_month; - - if (month == 1) - { - month = 12; - year -= 1; - } - else - { - month -= 1; - } - - leap = _is_leap(year); - - days_in_last_month = DAYS_PER_MONTHS[leap][month]; - days_in_month = DAYS_PER_MONTHS[_is_leap(dt2_year)][dt2_month]; - - if (day_diff < days_in_month - days_in_last_month) - { - // We don't have a full month, we calculate days - if (days_in_last_month < dt1_day) - { - day_diff += dt1_day; - } - else - { - day_diff += days_in_last_month; - } - } - else if (day_diff == days_in_month - days_in_last_month) - { - // We have exactly a full month - // We remove the days difference - // and add one to the months difference - day_diff = 0; - month_diff += 1; - } - else - { - // We have a full month - day_diff += days_in_last_month; - } - - month_diff -= 1; - } - - if (month_diff < 0) - { - month_diff += 12; - year_diff -= 1; - } - - return new_diff( - year_diff * sign, - month_diff * sign, - day_diff * sign, - hour_diff * sign, - minute_diff * sign, - second_diff * sign, - microsecond_diff * sign, - total_days * sign); -} - -/* ------------------------------------------------------------------------- */ - -static PyMethodDef helpers_methods[] = { - {"is_leap", - (PyCFunction)is_leap, - METH_VARARGS, - PyDoc_STR("Checks if a year is a leap year.")}, - {"is_long_year", - (PyCFunction)is_long_year, - METH_VARARGS, - PyDoc_STR("Checks if a year is a long year.")}, - {"week_day", - (PyCFunction)week_day, - METH_VARARGS, - PyDoc_STR("Returns the weekday number.")}, - {"days_in_year", - (PyCFunction)days_in_year, - METH_VARARGS, - PyDoc_STR("Returns the number of days in the given year.")}, - {"timestamp", - (PyCFunction)timestamp, - METH_VARARGS, - PyDoc_STR("Returns the timestamp of the given datetime.")}, - {"local_time", - (PyCFunction)local_time, - METH_VARARGS, - PyDoc_STR("Returns a UNIX time as a broken down time for a particular transition type.")}, - {"precise_diff", - (PyCFunction)precise_diff, - METH_VARARGS, - PyDoc_STR("Calculate a precise difference between two datetimes.")}, - {NULL}}; - -/* ------------------------------------------------------------------------- */ - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_helpers", - NULL, - -1, - helpers_methods, - NULL, - NULL, - NULL, - NULL, -}; - -PyMODINIT_FUNC -PyInit__helpers(void) -{ - PyObject *module; - - PyDateTime_IMPORT; - - module = PyModule_Create(&moduledef); - - if (module == NULL) - return NULL; - - // Diff declaration - Diff_type.tp_new = PyType_GenericNew; - Diff_type.tp_members = Diff_members; - Diff_type.tp_init = (initproc)Diff_init; - - if (PyType_Ready(&Diff_type) < 0) - return NULL; - - PyModule_AddObject(module, "PreciseDiff", (PyObject *)&Diff_type); - - return module; -} diff --git a/pendulum/_extensions/_helpers.pyi b/pendulum/_extensions/_helpers.pyi deleted file mode 100644 index 99a5397..0000000 --- a/pendulum/_extensions/_helpers.pyi +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations - -from collections import namedtuple -from datetime import date -from datetime import datetime - -def days_in_year(year: int) -> int: ... -def is_leap(year: int) -> bool: ... -def is_long_year(year: int) -> bool: ... -def local_time( - unix_time: int, utc_offset: int, microseconds: int -) -> tuple[int, int, int, int, int, int, int]: ... - -class PreciseDiff( - namedtuple( - "PreciseDiff", - "years months days " "hours minutes seconds microseconds " "total_days", - ) -): - years: int - months: int - days: int - hours: int - minutes: int - seconds: int - microseconds: int - total_days: int - -def precise_diff(d1: datetime | date, d2: datetime | date) -> PreciseDiff: ... -def timestamp(dt: datetime) -> int: ... -def week_day(year: int, month: int, day: int) -> int: ... diff --git a/pendulum/_extensions/helpers.py b/pendulum/_extensions/helpers.py deleted file mode 100644 index 01066a3..0000000 --- a/pendulum/_extensions/helpers.py +++ /dev/null @@ -1,364 +0,0 @@ -from __future__ import annotations - -import datetime -import math - -from collections import namedtuple -from typing import cast - -from pendulum.constants import DAY_OF_WEEK_TABLE -from pendulum.constants import DAYS_PER_L_YEAR -from pendulum.constants import DAYS_PER_MONTHS -from pendulum.constants import DAYS_PER_N_YEAR -from pendulum.constants import EPOCH_YEAR -from pendulum.constants import MONTHS_OFFSETS -from pendulum.constants import SECS_PER_4_YEARS -from pendulum.constants import SECS_PER_100_YEARS -from pendulum.constants import SECS_PER_400_YEARS -from pendulum.constants import SECS_PER_DAY -from pendulum.constants import SECS_PER_HOUR -from pendulum.constants import SECS_PER_MIN -from pendulum.constants import SECS_PER_YEAR -from pendulum.constants import TM_DECEMBER -from pendulum.constants import TM_JANUARY -from pendulum.tz.timezone import Timezone -from pendulum.utils._compat import zoneinfo - - -class PreciseDiff( - namedtuple( - "PreciseDiff", - "years months days " "hours minutes seconds microseconds " "total_days", - ) -): - years: int - months: int - days: int - hours: int - minutes: int - seconds: int - microseconds: int - total_days: int - - def __repr__(self) -> str: - return ( - f"{self.years} years " - f"{self.months} months " - f"{self.days} days " - f"{self.hours} hours " - f"{self.minutes} minutes " - f"{self.seconds} seconds " - f"{self.microseconds} microseconds" - ) - - -def is_leap(year: int) -> bool: - return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) - - -def is_long_year(year: int) -> bool: - def p(y: int) -> int: - return y + y // 4 - y // 100 + y // 400 - - return p(year) % 7 == 4 or p(year - 1) % 7 == 3 - - -def week_day(year: int, month: int, day: int) -> int: - if month < 3: - year -= 1 - - w = ( - year - + year // 4 - - year // 100 - + year // 400 - + DAY_OF_WEEK_TABLE[month - 1] - + day - ) % 7 - - if not w: - w = 7 - - return w - - -def days_in_year(year: int) -> int: - if is_leap(year): - return DAYS_PER_L_YEAR - - return DAYS_PER_N_YEAR - - -def timestamp(dt: datetime.datetime) -> int: - year = dt.year - - result = (year - 1970) * 365 + MONTHS_OFFSETS[0][dt.month] - result += (year - 1968) // 4 - result -= (year - 1900) // 100 - result += (year - 1600) // 400 - - if is_leap(year) and dt.month < 3: - result -= 1 - - result += dt.day - 1 - result *= 24 - result += dt.hour - result *= 60 - result += dt.minute - result *= 60 - result += dt.second - - return result - - -def local_time( - unix_time: int, utc_offset: int, microseconds: int -) -> tuple[int, int, int, int, int, int, int]: - """ - Returns a UNIX time as a broken-down time - for a particular transition type. - """ - year = EPOCH_YEAR - seconds = int(math.floor(unix_time)) - - # Shift to a base year that is 400-year aligned. - if seconds >= 0: - seconds -= 10957 * SECS_PER_DAY - year += 30 # == 2000 - else: - seconds += (146097 - 10957) * SECS_PER_DAY - year -= 370 # == 1600 - - seconds += utc_offset - - # Handle years in chunks of 400/100/4/1 - year += 400 * (seconds // SECS_PER_400_YEARS) - seconds %= SECS_PER_400_YEARS - if seconds < 0: - seconds += SECS_PER_400_YEARS - year -= 400 - - leap_year = 1 # 4-century aligned - - sec_per_100years = SECS_PER_100_YEARS[leap_year] - while seconds >= sec_per_100years: - seconds -= sec_per_100years - year += 100 - leap_year = 0 # 1-century, non 4-century aligned - sec_per_100years = SECS_PER_100_YEARS[leap_year] - - sec_per_4years = SECS_PER_4_YEARS[leap_year] - while seconds >= sec_per_4years: - seconds -= sec_per_4years - year += 4 - leap_year = 1 # 4-year, non century aligned - sec_per_4years = SECS_PER_4_YEARS[leap_year] - - sec_per_year = SECS_PER_YEAR[leap_year] - while seconds >= sec_per_year: - seconds -= sec_per_year - year += 1 - leap_year = 0 # non 4-year aligned - sec_per_year = SECS_PER_YEAR[leap_year] - - # Handle months and days - month = TM_DECEMBER + 1 - day = seconds // SECS_PER_DAY + 1 - seconds %= SECS_PER_DAY - while month != TM_JANUARY + 1: - month_offset = MONTHS_OFFSETS[leap_year][month] - if day > month_offset: - day -= month_offset - break - - month -= 1 - - # Handle hours, minutes, seconds and microseconds - hour = seconds // SECS_PER_HOUR - seconds %= SECS_PER_HOUR - minute = seconds // SECS_PER_MIN - second = seconds % SECS_PER_MIN - - return year, month, day, hour, minute, second, microseconds - - -def precise_diff( - d1: datetime.datetime | datetime.date, d2: datetime.datetime | datetime.date -) -> PreciseDiff: - """ - Calculate a precise difference between two datetimes. - - :param d1: The first datetime - :param d2: The second datetime - """ - sign = 1 - - if d1 == d2: - return PreciseDiff(0, 0, 0, 0, 0, 0, 0, 0) - - tzinfo1: datetime.tzinfo | None = ( - d1.tzinfo if isinstance(d1, datetime.datetime) else None - ) - tzinfo2: datetime.tzinfo | None = ( - d2.tzinfo if isinstance(d2, datetime.datetime) else None - ) - - if ( - tzinfo1 is None - and tzinfo2 is not None - or tzinfo2 is None - and tzinfo1 is not None - ): - raise ValueError( - "Comparison between naive and aware datetimes is not supported" - ) - - if d1 > d2: - d1, d2 = d2, d1 - sign = -1 - - d_diff = 0 - hour_diff = 0 - min_diff = 0 - sec_diff = 0 - mic_diff = 0 - total_days = _day_number(d2.year, d2.month, d2.day) - _day_number( - d1.year, d1.month, d1.day - ) - in_same_tz = False - tz1 = None - tz2 = None - - # Trying to figure out the timezone names - # If we can't find them, we assume different timezones - if tzinfo1 and tzinfo2: - tz1 = _get_tzinfo_name(tzinfo1) - tz2 = _get_tzinfo_name(tzinfo2) - - in_same_tz = tz1 == tz2 and tz1 is not None - - if isinstance(d2, datetime.datetime): - if isinstance(d1, datetime.datetime): - # If we are not in the same timezone - # we need to adjust - # - # We also need to adjust if we do not - # have variable-length units - if not in_same_tz or total_days == 0: - offset1 = d1.utcoffset() - offset2 = d2.utcoffset() - - if offset1: - d1 = d1 - offset1 - - if offset2: - d2 = d2 - offset2 - - hour_diff = d2.hour - d1.hour - min_diff = d2.minute - d1.minute - sec_diff = d2.second - d1.second - mic_diff = d2.microsecond - d1.microsecond - else: - hour_diff = d2.hour - min_diff = d2.minute - sec_diff = d2.second - mic_diff = d2.microsecond - - if mic_diff < 0: - mic_diff += 1000000 - sec_diff -= 1 - - if sec_diff < 0: - sec_diff += 60 - min_diff -= 1 - - if min_diff < 0: - min_diff += 60 - hour_diff -= 1 - - if hour_diff < 0: - hour_diff += 24 - d_diff -= 1 - - y_diff = d2.year - d1.year - m_diff = d2.month - d1.month - d_diff += d2.day - d1.day - - if d_diff < 0: - year = d2.year - month = d2.month - - if month == 1: - month = 12 - year -= 1 - else: - month -= 1 - - leap = int(is_leap(year)) - - days_in_last_month = DAYS_PER_MONTHS[leap][month] - days_in_month = DAYS_PER_MONTHS[int(is_leap(d2.year))][d2.month] - - if d_diff < days_in_month - days_in_last_month: - # We don't have a full month, we calculate days - if days_in_last_month < d1.day: - d_diff += d1.day - else: - d_diff += days_in_last_month - elif d_diff == days_in_month - days_in_last_month: - # We have exactly a full month - # We remove the days difference - # and add one to the months difference - d_diff = 0 - m_diff += 1 - else: - # We have a full month - d_diff += days_in_last_month - - m_diff -= 1 - - if m_diff < 0: - m_diff += 12 - y_diff -= 1 - - return PreciseDiff( - sign * y_diff, - sign * m_diff, - sign * d_diff, - sign * hour_diff, - sign * min_diff, - sign * sec_diff, - sign * mic_diff, - sign * total_days, - ) - - -def _day_number(year: int, month: int, day: int) -> int: - month = (month + 9) % 12 - year = year - month // 10 - - return ( - 365 * year - + year // 4 - - year // 100 - + year // 400 - + (month * 306 + 5) // 10 - + (day - 1) - ) - - -def _get_tzinfo_name(tzinfo: datetime.tzinfo | None) -> str | None: - if tzinfo is None: - return None - - if hasattr(tzinfo, "key"): - # zoneinfo timezone - return cast(str, cast(zoneinfo.ZoneInfo, tzinfo).key) - elif hasattr(tzinfo, "name"): - # Pendulum timezone - return cast(Timezone, tzinfo).name - elif hasattr(tzinfo, "zone"): - # pytz timezone - return tzinfo.zone # type: ignore - - return None diff --git a/pendulum/constants.py b/pendulum/constants.py deleted file mode 100644 index a3d2a18..0000000 --- a/pendulum/constants.py +++ /dev/null @@ -1,109 +0,0 @@ -# The day constants -from __future__ import annotations - -SUNDAY = 0 -MONDAY = 1 -TUESDAY = 2 -WEDNESDAY = 3 -THURSDAY = 4 -FRIDAY = 5 -SATURDAY = 6 - -# Number of X in Y. -YEARS_PER_CENTURY = 100 -YEARS_PER_DECADE = 10 -MONTHS_PER_YEAR = 12 -WEEKS_PER_YEAR = 52 -DAYS_PER_WEEK = 7 -HOURS_PER_DAY = 24 -MINUTES_PER_HOUR = 60 -SECONDS_PER_MINUTE = 60 -SECONDS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE -SECONDS_PER_DAY = HOURS_PER_DAY * SECONDS_PER_HOUR -US_PER_SECOND = 1000000 - -# Formats -ATOM = "YYYY-MM-DDTHH:mm:ssZ" -COOKIE = "dddd, DD-MMM-YYYY HH:mm:ss zz" -ISO8601 = "YYYY-MM-DDTHH:mm:ssZ" -ISO8601_EXTENDED = "YYYY-MM-DDTHH:mm:ss.SSSSSSZ" -RFC822 = "ddd, DD MMM YY HH:mm:ss ZZ" -RFC850 = "dddd, DD-MMM-YY HH:mm:ss zz" -RFC1036 = "ddd, DD MMM YY HH:mm:ss ZZ" -RFC1123 = "ddd, DD MMM YYYY HH:mm:ss ZZ" -RFC2822 = "ddd, DD MMM YYYY HH:mm:ss ZZ" -RFC3339 = ISO8601 -RFC3339_EXTENDED = ISO8601_EXTENDED -RSS = "ddd, DD MMM YYYY HH:mm:ss ZZ" -W3C = ISO8601 - - -EPOCH_YEAR = 1970 - -DAYS_PER_N_YEAR = 365 -DAYS_PER_L_YEAR = 366 - -USECS_PER_SEC = 1000000 - -SECS_PER_MIN = 60 -SECS_PER_HOUR = 60 * SECS_PER_MIN -SECS_PER_DAY = SECS_PER_HOUR * 24 - -# 400-year chunks always have 146097 days (20871 weeks). -SECS_PER_400_YEARS = 146097 * SECS_PER_DAY - -# The number of seconds in an aligned 100-year chunk, for those that -# do not begin with a leap year and those that do respectively. -SECS_PER_100_YEARS = ( - (76 * DAYS_PER_N_YEAR + 24 * DAYS_PER_L_YEAR) * SECS_PER_DAY, - (75 * DAYS_PER_N_YEAR + 25 * DAYS_PER_L_YEAR) * SECS_PER_DAY, -) - -# The number of seconds in an aligned 4-year chunk, for those that -# do not begin with a leap year and those that do respectively. -SECS_PER_4_YEARS = ( - (4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY, - (3 * DAYS_PER_N_YEAR + 1 * DAYS_PER_L_YEAR) * SECS_PER_DAY, -) - -# The number of seconds in non-leap and leap years respectively. -SECS_PER_YEAR = (DAYS_PER_N_YEAR * SECS_PER_DAY, DAYS_PER_L_YEAR * SECS_PER_DAY) - -DAYS_PER_YEAR = (DAYS_PER_N_YEAR, DAYS_PER_L_YEAR) - -# The month lengths in non-leap and leap years respectively. -DAYS_PER_MONTHS = ( - (-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), - (-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), -) - -# The day offsets of the beginning of each (1-based) month in non-leap -# and leap years respectively. -# For example, in a leap year there are 335 days before December. -MONTHS_OFFSETS = ( - (-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365), - (-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366), -) - -DAY_OF_WEEK_TABLE = (0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4) - -TM_SUNDAY = 0 -TM_MONDAY = 1 -TM_TUESDAY = 2 -TM_WEDNESDAY = 3 -TM_THURSDAY = 4 -TM_FRIDAY = 5 -TM_SATURDAY = 6 - -TM_JANUARY = 0 -TM_FEBRUARY = 1 -TM_MARCH = 2 -TM_APRIL = 3 -TM_MAY = 4 -TM_JUNE = 5 -TM_JULY = 6 -TM_AUGUST = 7 -TM_SEPTEMBER = 8 -TM_OCTOBER = 9 -TM_NOVEMBER = 10 -TM_DECEMBER = 11 diff --git a/pendulum/date.py b/pendulum/date.py deleted file mode 100644 index 4e2655b..0000000 --- a/pendulum/date.py +++ /dev/null @@ -1,760 +0,0 @@ -from __future__ import annotations - -import calendar -import math - -from datetime import date -from datetime import datetime -from datetime import timedelta -from typing import NoReturn -from typing import cast -from typing import overload - -import pendulum - -from pendulum.constants import FRIDAY -from pendulum.constants import MONDAY -from pendulum.constants import MONTHS_PER_YEAR -from pendulum.constants import SATURDAY -from pendulum.constants import SUNDAY -from pendulum.constants import THURSDAY -from pendulum.constants import TUESDAY -from pendulum.constants import WEDNESDAY -from pendulum.constants import YEARS_PER_CENTURY -from pendulum.constants import YEARS_PER_DECADE -from pendulum.exceptions import PendulumException -from pendulum.helpers import add_duration -from pendulum.interval import Interval -from pendulum.mixins.default import FormattableMixin - - -class Date(FormattableMixin, date): - # Names of days of the week - _days = { - SUNDAY: "Sunday", - MONDAY: "Monday", - TUESDAY: "Tuesday", - WEDNESDAY: "Wednesday", - THURSDAY: "Thursday", - FRIDAY: "Friday", - SATURDAY: "Saturday", - } - - _MODIFIERS_VALID_UNITS = ["day", "week", "month", "year", "decade", "century"] - - # Getters/Setters - - def set( - self, year: int | None = None, month: int | None = None, day: int | None = None - ) -> Date: - return self.replace(year=year, month=month, day=day) - - @property - def day_of_week(self) -> int: - """ - Returns the day of the week (0-6). - """ - return self.isoweekday() % 7 - - @property - def day_of_year(self) -> int: - """ - Returns the day of the year (1-366). - """ - k = 1 if self.is_leap_year() else 2 - - return (275 * self.month) // 9 - k * ((self.month + 9) // 12) + self.day - 30 - - @property - def week_of_year(self) -> int: - return self.isocalendar()[1] - - @property - def days_in_month(self) -> int: - return calendar.monthrange(self.year, self.month)[1] - - @property - def week_of_month(self) -> int: - first_day_of_month = self.replace(day=1) - - return self.week_of_year - first_day_of_month.week_of_year + 1 - - @property - def age(self) -> int: - return self.diff(abs=False).in_years() - - @property - def quarter(self) -> int: - return int(math.ceil(self.month / 3)) - - # String Formatting - - def to_date_string(self) -> str: - """ - Format the instance as date. - - :rtype: str - """ - return self.strftime("%Y-%m-%d") - - def to_formatted_date_string(self) -> str: - """ - Format the instance as a readable date. - - :rtype: str - """ - return self.strftime("%b %d, %Y") - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.year}, {self.month}, {self.day})" - - # COMPARISONS - - def closest(self, dt1: date, dt2: date) -> Date: - """ - Get the closest date from the instance. - """ - dt1 = self.__class__(dt1.year, dt1.month, dt1.day) - dt2 = self.__class__(dt2.year, dt2.month, dt2.day) - - if self.diff(dt1).in_seconds() < self.diff(dt2).in_seconds(): - return dt1 - - return dt2 - - def farthest(self, dt1: date, dt2: date) -> Date: - """ - Get the farthest date from the instance. - """ - dt1 = self.__class__(dt1.year, dt1.month, dt1.day) - dt2 = self.__class__(dt2.year, dt2.month, dt2.day) - - if self.diff(dt1).in_seconds() > self.diff(dt2).in_seconds(): - return dt1 - - return dt2 - - def is_future(self) -> bool: - """ - Determines if the instance is in the future, ie. greater than now. - """ - return self > self.today() - - def is_past(self) -> bool: - """ - Determines if the instance is in the past, ie. less than now. - """ - return self < self.today() - - def is_leap_year(self) -> bool: - """ - Determines if the instance is a leap year. - """ - return calendar.isleap(self.year) - - def is_long_year(self) -> bool: - """ - Determines if the instance is a long year - - See link `<https://en.wikipedia.org/wiki/ISO_8601#Week_dates>`_ - """ - return Date(self.year, 12, 28).isocalendar()[1] == 53 - - def is_same_day(self, dt: date) -> bool: - """ - Checks if the passed in date is the same day as the instance current day. - """ - return self == dt - - def is_anniversary(self, dt: date | None = None) -> bool: - """ - Check if it's the anniversary. - - Compares the date/month values of the two dates. - """ - if dt is None: - dt = Date.today() - - instance = self.__class__(dt.year, dt.month, dt.day) - - return (self.month, self.day) == (instance.month, instance.day) - - # the additional method for checking if today is the anniversary day - # the alias is provided to start using a new name and keep the backward compatibility - # the old name can be completely replaced with the new in one of the future versions - is_birthday = is_anniversary - - # ADDITIONS AND SUBTRACTIONS - - def add( - self, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0 - ) -> Date: - """ - Add duration to the instance. - - :param years: The number of years - :param months: The number of months - :param weeks: The number of weeks - :param days: The number of days - """ - dt = add_duration( - date(self.year, self.month, self.day), - years=years, - months=months, - weeks=weeks, - days=days, - ) - - return self.__class__(dt.year, dt.month, dt.day) - - def subtract( - self, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0 - ) -> Date: - """ - Remove duration from the instance. - - :param years: The number of years - :param months: The number of months - :param weeks: The number of weeks - :param days: The number of days - """ - return self.add(years=-years, months=-months, weeks=-weeks, days=-days) - - def _add_timedelta(self, delta: timedelta) -> Date: - """ - Add timedelta duration to the instance. - - :param delta: The timedelta instance - """ - if isinstance(delta, pendulum.Duration): - return self.add( - years=delta.years, - months=delta.months, - weeks=delta.weeks, - days=delta.remaining_days, - ) - - return self.add(days=delta.days) - - def _subtract_timedelta(self, delta: timedelta) -> Date: - """ - Remove timedelta duration from the instance. - - :param delta: The timedelta instance - """ - if isinstance(delta, pendulum.Duration): - return self.subtract( - years=delta.years, - months=delta.months, - weeks=delta.weeks, - days=delta.remaining_days, - ) - - return self.subtract(days=delta.days) - - def __add__(self, other: timedelta) -> Date: - if not isinstance(other, timedelta): - return NotImplemented - - return self._add_timedelta(other) - - @overload - def __sub__(self, delta: timedelta) -> Date: - ... - - @overload - def __sub__(self, dt: datetime) -> NoReturn: - ... - - @overload - def __sub__(self, dt: Date) -> Interval: - ... - - def __sub__(self, other: timedelta | date) -> Date | Interval: - if isinstance(other, timedelta): - return self._subtract_timedelta(other) - - if not isinstance(other, date): - return NotImplemented - - dt = self.__class__(other.year, other.month, other.day) - - return dt.diff(self, False) - - # DIFFERENCES - - def diff(self, dt: date | None = None, abs: bool = True) -> Interval: - """ - Returns the difference between two Date objects as an Interval. - - :param dt: The date to compare to (defaults to today) - :param abs: Whether to return an absolute interval or not - """ - if dt is None: - dt = self.today() - - return Interval(self, Date(dt.year, dt.month, dt.day), absolute=abs) - - def diff_for_humans( - self, - other: date | None = None, - absolute: bool = False, - locale: str | None = None, - ) -> str: - """ - Get the difference in a human readable format in the current locale. - - When comparing a value in the past to default now: - 1 day ago - 5 months ago - - When comparing a value in the future to default now: - 1 day from now - 5 months from now - - When comparing a value in the past to another value: - 1 day before - 5 months before - - When comparing a value in the future to another value: - 1 day after - 5 months after - - :param other: The date to compare to (defaults to today) - :param absolute: removes time difference modifiers ago, after, etc - :param locale: The locale to use for localization - """ - is_now = other is None - - if is_now: - other = self.today() - - diff = self.diff(other) - - return pendulum.format_diff(diff, is_now, absolute, locale) - - # MODIFIERS - - def start_of(self, unit: str) -> Date: - """ - Returns a copy of the instance with the time reset - with the following rules: - - * day: time to 00:00:00 - * week: date to first day of the week and time to 00:00:00 - * month: date to first day of the month and time to 00:00:00 - * year: date to first day of the year and time to 00:00:00 - * decade: date to first day of the decade and time to 00:00:00 - * century: date to first day of century and time to 00:00:00 - - :param unit: The unit to reset to - """ - if unit not in self._MODIFIERS_VALID_UNITS: - raise ValueError(f'Invalid unit "{unit}" for start_of()') - - return cast(Date, getattr(self, f"_start_of_{unit}")()) - - def end_of(self, unit: str) -> Date: - """ - Returns a copy of the instance with the time reset - with the following rules: - - * week: date to last day of the week - * month: date to last day of the month - * year: date to last day of the year - * decade: date to last day of the decade - * century: date to last day of century - - :param unit: The unit to reset to - """ - if unit not in self._MODIFIERS_VALID_UNITS: - raise ValueError(f'Invalid unit "{unit}" for end_of()') - - return cast(Date, getattr(self, f"_end_of_{unit}")()) - - def _start_of_day(self) -> Date: - """ - Compatibility method. - """ - return self - - def _end_of_day(self) -> Date: - """ - Compatibility method - """ - return self - - def _start_of_month(self) -> Date: - """ - Reset the date to the first day of the month. - """ - return self.set(self.year, self.month, 1) - - def _end_of_month(self) -> Date: - """ - Reset the date to the last day of the month. - """ - return self.set(self.year, self.month, self.days_in_month) - - def _start_of_year(self) -> Date: - """ - Reset the date to the first day of the year. - """ - return self.set(self.year, 1, 1) - - def _end_of_year(self) -> Date: - """ - Reset the date to the last day of the year. - """ - return self.set(self.year, 12, 31) - - def _start_of_decade(self) -> Date: - """ - Reset the date to the first day of the decade. - """ - year = self.year - self.year % YEARS_PER_DECADE - - return self.set(year, 1, 1) - - def _end_of_decade(self) -> Date: - """ - Reset the date to the last day of the decade. - """ - year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1 - - return self.set(year, 12, 31) - - def _start_of_century(self) -> Date: - """ - Reset the date to the first day of the century. - """ - year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1 - - return self.set(year, 1, 1) - - def _end_of_century(self) -> Date: - """ - Reset the date to the last day of the century. - """ - year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY - - return self.set(year, 12, 31) - - def _start_of_week(self) -> Date: - """ - Reset the date to the first day of the week. - """ - dt = self - - if self.day_of_week != pendulum._WEEK_STARTS_AT: - dt = self.previous(pendulum._WEEK_STARTS_AT) - - return dt.start_of("day") - - def _end_of_week(self) -> Date: - """ - Reset the date to the last day of the week. - """ - dt = self - - if self.day_of_week != pendulum._WEEK_ENDS_AT: - dt = self.next(pendulum._WEEK_ENDS_AT) - - return dt.end_of("day") - - def next(self, day_of_week: int | None = None) -> Date: - """ - Modify to the next occurrence of a given day of the week. - If no day_of_week is provided, modify to the next occurrence - of the current day of the week. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - - :param day_of_week: The next day of week to reset to. - """ - if day_of_week is None: - day_of_week = self.day_of_week - - if day_of_week < SUNDAY or day_of_week > SATURDAY: - raise ValueError("Invalid day of week") - - dt = self.add(days=1) - while dt.day_of_week != day_of_week: - dt = dt.add(days=1) - - return dt - - def previous(self, day_of_week: int | None = None) -> Date: - """ - Modify to the previous occurrence of a given day of the week. - If no day_of_week is provided, modify to the previous occurrence - of the current day of the week. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - - :param day_of_week: The previous day of week to reset to. - """ - if day_of_week is None: - day_of_week = self.day_of_week - - if day_of_week < SUNDAY or day_of_week > SATURDAY: - raise ValueError("Invalid day of week") - - dt = self.subtract(days=1) - while dt.day_of_week != day_of_week: - dt = dt.subtract(days=1) - - return dt - - def first_of(self, unit: str, day_of_week: int | None = None) -> Date: - """ - Returns an instance set to the first occurrence - of a given day of the week in the current unit. - If no day_of_week is provided, modify to the first day of the unit. - Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. - - Supported units are month, quarter and year. - - :param unit: The unit to use - :param day_of_week: The day of week to reset to. - """ - if unit not in ["month", "quarter", "year"]: - raise ValueError(f'Invalid unit "{unit}" for first_of()') - - return cast(Date, getattr(self, f"_first_of_{unit}")(day_of_week)) - - def last_of(self, unit: str, day_of_week: int | None = None) -> Date: - """ - Returns an instance set to the last occurrence - of a given day of the week in the current unit. - If no day_of_week is provided, modify to the last day of the unit. - Use the supplied consts to indicate the desired day_of_week, ex. pendulum.MONDAY. - - Supported units are month, quarter and year. - - :param unit: The unit to use - :param day_of_week: The day of week to reset to. - """ - if unit not in ["month", "quarter", "year"]: - raise ValueError(f'Invalid unit "{unit}" for first_of()') - - return cast(Date, getattr(self, f"_last_of_{unit}")(day_of_week)) - - def nth_of(self, unit: str, nth: int, day_of_week: int) -> Date: - """ - Returns a new instance set to the given occurrence - of a given day of the week in the current unit. - If the calculated occurrence is outside the scope of the current unit, - then raise an error. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - - Supported units are month, quarter and year. - - :param unit: The unit to use - :param nth: The occurrence to use - :param day_of_week: The day of week to set to. - """ - if unit not in ["month", "quarter", "year"]: - raise ValueError(f'Invalid unit "{unit}" for first_of()') - - dt = cast(Date, getattr(self, f"_nth_of_{unit}")(nth, day_of_week)) - if not dt: - raise PendulumException( - f"Unable to find occurence {nth}" - f" of {self._days[day_of_week]} in {unit}" - ) - - return dt - - def _first_of_month(self, day_of_week: int) -> Date: - """ - Modify to the first occurrence of a given day of the week - in the current month. If no day_of_week is provided, - modify to the first day of the month. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - - :param day_of_week: The day of week to set to. - """ - dt = self - - if day_of_week is None: - return dt.set(day=1) - - month = calendar.monthcalendar(dt.year, dt.month) - - calendar_day = (day_of_week - 1) % 7 - - if month[0][calendar_day] > 0: - day_of_month = month[0][calendar_day] - else: - day_of_month = month[1][calendar_day] - - return dt.set(day=day_of_month) - - def _last_of_month(self, day_of_week: int | None = None) -> Date: - """ - Modify to the last occurrence of a given day of the week - in the current month. If no day_of_week is provided, - modify to the last day of the month. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - - :param day_of_week: The day of week to set to. - """ - dt = self - - if day_of_week is None: - return dt.set(day=self.days_in_month) - - month = calendar.monthcalendar(dt.year, dt.month) - - calendar_day = (day_of_week - 1) % 7 - - if month[-1][calendar_day] > 0: - day_of_month = month[-1][calendar_day] - else: - day_of_month = month[-2][calendar_day] - - return dt.set(day=day_of_month) - - def _nth_of_month(self, nth: int, day_of_week: int) -> Date | None: - """ - Modify to the given occurrence of a given day of the week - in the current month. If the calculated occurrence is outside, - the scope of the current month, then return False and no - modifications are made. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - """ - if nth == 1: - return self.first_of("month", day_of_week) - - dt = self.first_of("month") - check = dt.format("YYYY-MM") - for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): - dt = dt.next(day_of_week) - - if dt.format("YYYY-MM") == check: - return self.set(day=dt.day) - - return None - - def _first_of_quarter(self, day_of_week: int | None = None) -> Date: - """ - Modify to the first occurrence of a given day of the week - in the current quarter. If no day_of_week is provided, - modify to the first day of the quarter. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - """ - return self.set(self.year, self.quarter * 3 - 2, 1).first_of( - "month", day_of_week - ) - - def _last_of_quarter(self, day_of_week: int | None = None) -> Date: - """ - Modify to the last occurrence of a given day of the week - in the current quarter. If no day_of_week is provided, - modify to the last day of the quarter. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - """ - return self.set(self.year, self.quarter * 3, 1).last_of("month", day_of_week) - - def _nth_of_quarter(self, nth: int, day_of_week: int) -> Date | None: - """ - Modify to the given occurrence of a given day of the week - in the current quarter. If the calculated occurrence is outside, - the scope of the current quarter, then return False and no - modifications are made. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - """ - if nth == 1: - return self.first_of("quarter", day_of_week) - - dt = self.replace(self.year, self.quarter * 3, 1) - last_month = dt.month - year = dt.year - dt = dt.first_of("quarter") - for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): - dt = dt.next(day_of_week) - - if last_month < dt.month or year != dt.year: - return None - - return self.set(self.year, dt.month, dt.day) - - def _first_of_year(self, day_of_week: int | None = None) -> Date: - """ - Modify to the first occurrence of a given day of the week - in the current year. If no day_of_week is provided, - modify to the first day of the year. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - """ - return self.set(month=1).first_of("month", day_of_week) - - def _last_of_year(self, day_of_week: int | None = None) -> Date: - """ - Modify to the last occurrence of a given day of the week - in the current year. If no day_of_week is provided, - modify to the last day of the year. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - """ - return self.set(month=MONTHS_PER_YEAR).last_of("month", day_of_week) - - def _nth_of_year(self, nth: int, day_of_week: int) -> Date | None: - """ - Modify to the given occurrence of a given day of the week - in the current year. If the calculated occurrence is outside, - the scope of the current year, then return False and no - modifications are made. Use the supplied consts - to indicate the desired day_of_week, ex. pendulum.MONDAY. - """ - if nth == 1: - return self.first_of("year", day_of_week) - - dt = self.first_of("year") - year = dt.year - for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): - dt = dt.next(day_of_week) - - if year != dt.year: - return None - - return self.set(self.year, dt.month, dt.day) - - def average(self, dt: date | None = None) -> Date: - """ - Modify the current instance to the average - of a given instance (default now) and the current instance. - """ - if dt is None: - dt = Date.today() - - return self.add(days=int(self.diff(dt, False).in_days() / 2)) - - # Native methods override - - @classmethod - def today(cls) -> Date: - dt = date.today() - - return cls(dt.year, dt.month, dt.day) - - @classmethod - def fromtimestamp(cls, t: float) -> Date: - dt = super().fromtimestamp(t) - - return cls(dt.year, dt.month, dt.day) - - @classmethod - def fromordinal(cls, n: int) -> Date: - dt = super().fromordinal(n) - - return cls(dt.year, dt.month, dt.day) - - def replace( - self, - year: int | None = None, - month: int | None = None, - day: int | None = None, - ) -> Date: - year = year if year is not None else self.year - month = month if month is not None else self.month - day = day if day is not None else self.day - - return self.__class__(year, month, day) diff --git a/pendulum/datetime.py b/pendulum/datetime.py deleted file mode 100644 index 52ad3cc..0000000 --- a/pendulum/datetime.py +++ /dev/null @@ -1,1381 +0,0 @@ -from __future__ import annotations - -import calendar -import datetime - -from typing import TYPE_CHECKING -from typing import Any -from typing import Callable -from typing import Optional -from typing import cast -from typing import overload - -import pendulum - -from pendulum.constants import ATOM -from pendulum.constants import COOKIE -from pendulum.constants import MINUTES_PER_HOUR -from pendulum.constants import MONTHS_PER_YEAR -from pendulum.constants import RFC822 -from pendulum.constants import RFC850 -from pendulum.constants import RFC1036 -from pendulum.constants import RFC1123 -from pendulum.constants import RFC2822 -from pendulum.constants import RSS -from pendulum.constants import SATURDAY -from pendulum.constants import SECONDS_PER_DAY -from pendulum.constants import SECONDS_PER_MINUTE -from pendulum.constants import SUNDAY -from pendulum.constants import W3C -from pendulum.constants import YEARS_PER_CENTURY -from pendulum.constants import YEARS_PER_DECADE -from pendulum.date import Date -from pendulum.exceptions import PendulumException -from pendulum.helpers import add_duration -from pendulum.interval import Interval -from pendulum.time import Time -from pendulum.tz import UTC -from pendulum.tz import local_timezone -from pendulum.tz.timezone import FixedTimezone -from pendulum.tz.timezone import Timezone -from pendulum.utils._compat import PY38 - -if TYPE_CHECKING: - from typing import Literal - - -class DateTime(datetime.datetime, Date): - EPOCH: DateTime - - # Formats - - _FORMATS: dict[str, str | Callable[[datetime.datetime], str]] = { - "atom": ATOM, - "cookie": COOKIE, - "iso8601": lambda dt: dt.isoformat(), - "rfc822": RFC822, - "rfc850": RFC850, - "rfc1036": RFC1036, - "rfc1123": RFC1123, - "rfc2822": RFC2822, - "rfc3339": lambda dt: dt.isoformat(), - "rss": RSS, - "w3c": W3C, - } - - _MODIFIERS_VALID_UNITS: list[str] = [ - "second", - "minute", - "hour", - "day", - "week", - "month", - "year", - "decade", - "century", - ] - - _EPOCH: datetime.datetime = datetime.datetime(1970, 1, 1, tzinfo=UTC) - - @classmethod - def create( - cls, - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - second: int = 0, - microsecond: int = 0, - tz: str | float | Timezone | FixedTimezone | None | datetime.tzinfo = UTC, - fold: int = 1, - raise_on_unknown_times: bool = False, - ) -> DateTime: - """ - Creates a new DateTime instance from a specific date and time. - """ - if tz is not None: - tz = pendulum._safe_timezone(tz) - - dt = datetime.datetime( - year, month, day, hour, minute, second, microsecond, fold=fold - ) - - if tz is not None: - dt = tz.convert(dt, raise_on_unknown_times=raise_on_unknown_times) - - return cls( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tzinfo=dt.tzinfo, - fold=dt.fold, - ) - - @overload - @classmethod - def now(cls, tz: datetime.tzinfo | None = None) -> DateTime: - ... - - @overload - @classmethod - def now(cls, tz: str | Timezone | FixedTimezone | None = None) -> DateTime: - ... - - @classmethod - def now( - cls, tz: str | Timezone | FixedTimezone | datetime.tzinfo | None = None - ) -> DateTime: - """ - Get a DateTime instance for the current date and time. - """ - if tz is None or tz == "local": - dt = datetime.datetime.now(local_timezone()) - elif tz is UTC or tz == "UTC": - dt = datetime.datetime.now(UTC) - else: - dt = datetime.datetime.now(UTC) - tz = pendulum._safe_timezone(tz) - dt = dt.astimezone(tz) - - return cls( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tzinfo=dt.tzinfo, - fold=dt.fold, - ) - - @classmethod - def utcnow(cls) -> DateTime: - """ - Get a DateTime instance for the current date and time in UTC. - """ - return cls.now(UTC) - - @classmethod - def today(cls) -> DateTime: - return cls.now() - - @classmethod - def strptime(cls, time: str, fmt: str) -> DateTime: - return pendulum.instance(datetime.datetime.strptime(time, fmt)) - - # Getters/Setters - - def set( - self, - year: int | None = None, - month: int | None = None, - day: int | None = None, - hour: int | None = None, - minute: int | None = None, - second: int | None = None, - microsecond: int | None = None, - tz: str | float | Timezone | FixedTimezone | datetime.tzinfo | None = None, - ) -> DateTime: - if year is None: - year = self.year - if month is None: - month = self.month - if day is None: - day = self.day - if hour is None: - hour = self.hour - if minute is None: - minute = self.minute - if second is None: - second = self.second - if microsecond is None: - microsecond = self.microsecond - if tz is None: - tz = self.tz - - return DateTime.create( - year, month, day, hour, minute, second, microsecond, tz=tz - ) - - @property - def float_timestamp(self) -> float: - return self.timestamp() - - @property - def int_timestamp(self) -> int: - # Workaround needed to avoid inaccuracy - # for far into the future datetimes - dt = datetime.datetime( - self.year, - self.month, - self.day, - self.hour, - self.minute, - self.second, - self.microsecond, - tzinfo=self.tzinfo, - fold=self.fold, - ) - - delta = dt - self._EPOCH - - return delta.days * SECONDS_PER_DAY + delta.seconds - - @property - def offset(self) -> int | None: - return self.get_offset() - - @property - def offset_hours(self) -> float | None: - offset = self.get_offset() - - if offset is None: - return None - - return offset / SECONDS_PER_MINUTE / MINUTES_PER_HOUR - - @property - def timezone(self) -> Timezone | FixedTimezone | None: - if not isinstance(self.tzinfo, (Timezone, FixedTimezone)): - return None - - return self.tzinfo - - @property - def tz(self) -> Timezone | FixedTimezone | None: - return self.timezone - - @property - def timezone_name(self) -> str | None: - tz = self.timezone - - if tz is None: - return None - - return tz.name - - @property - def age(self) -> int: - return self.date().diff(self.now(self.tz).date(), abs=False).in_years() - - def is_local(self) -> bool: - return self.offset == self.in_timezone(pendulum.local_timezone()).offset - - def is_utc(self) -> bool: - return self.offset == 0 - - def is_dst(self) -> bool: - return self.dst() != datetime.timedelta() - - def get_offset(self) -> int | None: - utcoffset = self.utcoffset() - if utcoffset is None: - return None - - return int(utcoffset.total_seconds()) - - def date(self) -> Date: - return Date(self.year, self.month, self.day) - - def time(self) -> Time: - return Time(self.hour, self.minute, self.second, self.microsecond) - - def naive(self) -> DateTime: - """ - Return the DateTime without timezone information. - """ - return self.__class__( - self.year, - self.month, - self.day, - self.hour, - self.minute, - self.second, - self.microsecond, - ) - - def on(self, year: int, month: int, day: int) -> DateTime: - """ - Returns a new instance with the current date set to a different date. - """ - return self.set(year=int(year), month=int(month), day=int(day)) - - def at( - self, hour: int, minute: int = 0, second: int = 0, microsecond: int = 0 - ) -> DateTime: - """ - Returns a new instance with the current time to a different time. - """ - return self.set( - hour=hour, minute=minute, second=second, microsecond=microsecond - ) - - def in_timezone(self, tz: str | Timezone | FixedTimezone) -> DateTime: - """ - Set the instance's timezone from a string or object. - """ - tz = pendulum._safe_timezone(tz) - - dt = self - if not self.timezone: - dt = dt.replace(fold=1) - - return cast(DateTime, tz.convert(dt)) - - def in_tz(self, tz: str | Timezone | FixedTimezone) -> DateTime: - """ - Set the instance's timezone from a string or object. - """ - return self.in_timezone(tz) - - # STRING FORMATTING - - def to_time_string(self) -> str: - """ - Format the instance as time. - """ - return self.format("HH:mm:ss") - - def to_datetime_string(self) -> str: - """ - Format the instance as date and time. - """ - return self.format("YYYY-MM-DD HH:mm:ss") - - def to_day_datetime_string(self) -> str: - """ - Format the instance as day, date and time (in english). - """ - return self.format("ddd, MMM D, YYYY h:mm A", locale="en") - - def to_atom_string(self) -> str: - """ - Format the instance as ATOM. - """ - return self._to_string("atom") - - def to_cookie_string(self) -> str: - """ - Format the instance as COOKIE. - """ - return self._to_string("cookie", locale="en") - - def to_iso8601_string(self) -> str: - """ - Format the instance as ISO 8601. - """ - string = self._to_string("iso8601") - - if self.tz and self.tz.name == "UTC": - string = string.replace("+00:00", "Z") - - return string - - def to_rfc822_string(self) -> str: - """ - Format the instance as RFC 822. - """ - return self._to_string("rfc822") - - def to_rfc850_string(self) -> str: - """ - Format the instance as RFC 850. - """ - return self._to_string("rfc850") - - def to_rfc1036_string(self) -> str: - """ - Format the instance as RFC 1036. - """ - return self._to_string("rfc1036") - - def to_rfc1123_string(self) -> str: - """ - Format the instance as RFC 1123. - """ - return self._to_string("rfc1123") - - def to_rfc2822_string(self) -> str: - """ - Format the instance as RFC 2822. - """ - return self._to_string("rfc2822") - - def to_rfc3339_string(self) -> str: - """ - Format the instance as RFC 3339. - """ - return self._to_string("rfc3339") - - def to_rss_string(self) -> str: - """ - Format the instance as RSS. - """ - return self._to_string("rss") - - def to_w3c_string(self) -> str: - """ - Format the instance as W3C. - """ - return self._to_string("w3c") - - def _to_string(self, fmt: str, locale: str | None = None) -> str: - """ - Format the instance to a common string format. - """ - if fmt not in self._FORMATS: - raise ValueError(f"Format [{fmt}] is not supported") - - fmt_value = self._FORMATS[fmt] - if callable(fmt_value): - return fmt_value(self) - - return self.format(fmt_value, locale=locale) - - def __str__(self) -> str: - return self.isoformat("T") - - def __repr__(self) -> str: - us = "" - if self.microsecond: - us = f", {self.microsecond}" - - repr_ = "{klass}(" "{year}, {month}, {day}, " "{hour}, {minute}, {second}{us}" - - if self.tzinfo is not None: - repr_ += ", tzinfo={tzinfo}" - - repr_ += ")" - - return repr_.format( - klass=self.__class__.__name__, - year=self.year, - month=self.month, - day=self.day, - hour=self.hour, - minute=self.minute, - second=self.second, - us=us, - tzinfo=repr(self.tzinfo), - ) - - # Comparisons - def closest(self, *dts: datetime.datetime) -> DateTime: # type: ignore[override] - """ - Get the farthest date from the instance. - """ - pdts = [pendulum.instance(x) for x in dts] - - return min((abs(self - dt), dt) for dt in pdts)[1] - - def farthest(self, *dts: datetime.datetime) -> DateTime: # type: ignore[override] - """ - Get the farthest date from the instance. - """ - pdts = [pendulum.instance(x) for x in dts] - - return max((abs(self - dt), dt) for dt in pdts)[1] - - def is_future(self) -> bool: - """ - Determines if the instance is in the future, ie. greater than now. - """ - return self > self.now(self.timezone) - - def is_past(self) -> bool: - """ - Determines if the instance is in the past, ie. less than now. - """ - return self < self.now(self.timezone) - - def is_long_year(self) -> bool: - """ - Determines if the instance is a long year - - See link `https://en.wikipedia.org/wiki/ISO_8601#Week_dates`_ - """ - return ( - DateTime.create(self.year, 12, 28, 0, 0, 0, tz=self.tz).isocalendar()[1] - == 53 - ) - - def is_same_day(self, dt: datetime.datetime) -> bool: # type: ignore[override] - """ - Checks if the passed in date is the same day - as the instance current day. - """ - dt = pendulum.instance(dt) - - return self.to_date_string() == dt.to_date_string() - - def is_anniversary( # type: ignore[override] - self, dt: datetime.datetime | None = None - ) -> bool: - """ - Check if its the anniversary. - Compares the date/month values of the two dates. - """ - if dt is None: - dt = self.now(self.tz) - - instance = pendulum.instance(dt) - - return (self.month, self.day) == (instance.month, instance.day) - - # ADDITIONS AND SUBSTRACTIONS - - def add( - self, - years: int = 0, - months: int = 0, - weeks: int = 0, - days: int = 0, - hours: int = 0, - minutes: int = 0, - seconds: float = 0, - microseconds: int = 0, - ) -> DateTime: - """ - Add a duration to the instance. - - If we're adding units of variable length (i.e., years, months), - move forward from current time, otherwise move forward from utc, for accuracy - when moving across DST boundaries. - """ - units_of_variable_length = any([years, months, weeks, days]) - - current_dt = datetime.datetime( - self.year, - self.month, - self.day, - self.hour, - self.minute, - self.second, - self.microsecond, - ) - if not units_of_variable_length: - offset = self.utcoffset() - if offset: - current_dt = current_dt - offset - - dt = add_duration( - current_dt, - years=years, - months=months, - weeks=weeks, - days=days, - hours=hours, - minutes=minutes, - seconds=seconds, - microseconds=microseconds, - ) - - if units_of_variable_length or self.tz is None: - return DateTime.create( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tz=self.tz, - ) - - dt = datetime.datetime( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tzinfo=UTC, - ) - - dt = self.tz.convert(dt) - - return self.__class__( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tzinfo=self.tz, - fold=dt.fold, - ) - - def subtract( - self, - years: int = 0, - months: int = 0, - weeks: int = 0, - days: int = 0, - hours: int = 0, - minutes: int = 0, - seconds: float = 0, - microseconds: int = 0, - ) -> DateTime: - """ - Remove duration from the instance. - """ - return self.add( - years=-years, - months=-months, - weeks=-weeks, - days=-days, - hours=-hours, - minutes=-minutes, - seconds=-seconds, - microseconds=-microseconds, - ) - - # Adding a final underscore to the method name - # to avoid errors for PyPy which already defines - # a _add_timedelta method - def _add_timedelta_(self, delta: datetime.timedelta) -> DateTime: - """ - Add timedelta duration to the instance. - """ - if isinstance(delta, pendulum.Interval): - return self.add( - years=delta.years, - months=delta.months, - weeks=delta.weeks, - days=delta.remaining_days, - hours=delta.hours, - minutes=delta.minutes, - seconds=delta.remaining_seconds, - microseconds=delta.microseconds, - ) - elif isinstance(delta, pendulum.Duration): - return self.add( - years=delta.years, months=delta.months, seconds=delta._total - ) - - return self.add(seconds=delta.total_seconds()) - - def _subtract_timedelta(self, delta: datetime.timedelta) -> DateTime: - """ - Remove timedelta duration from the instance. - """ - if isinstance(delta, pendulum.Duration): - return self.subtract( - years=delta.years, months=delta.months, seconds=delta._total - ) - - return self.subtract(seconds=delta.total_seconds()) - - # DIFFERENCES - - def diff( # type: ignore[override] - self, dt: datetime.datetime | None = None, abs: bool = True - ) -> Interval: - """ - Returns the difference between two DateTime objects represented as an Interval. - """ - if dt is None: - dt = self.now(self.tz) - - return Interval(self, dt, absolute=abs) - - def diff_for_humans( # type: ignore[override] - self, - other: DateTime | None = None, - absolute: bool = False, - locale: str | None = None, - ) -> str: - """ - Get the difference in a human readable format in the current locale. - - When comparing a value in the past to default now: - 1 day ago - 5 months ago - - When comparing a value in the future to default now: - 1 day from now - 5 months from now - - When comparing a value in the past to another value: - 1 day before - 5 months before - - When comparing a value in the future to another value: - 1 day after - 5 months after - """ - is_now = other is None - - if is_now: - other = self.now() - - diff = self.diff(other) - - return pendulum.format_diff(diff, is_now, absolute, locale) - - # Modifiers - def start_of(self, unit: str) -> DateTime: - """ - Returns a copy of the instance with the time reset - with the following rules: - - * second: microsecond set to 0 - * minute: second and microsecond set to 0 - * hour: minute, second and microsecond set to 0 - * day: time to 00:00:00 - * week: date to first day of the week and time to 00:00:00 - * month: date to first day of the month and time to 00:00:00 - * year: date to first day of the year and time to 00:00:00 - * decade: date to first day of the decade and time to 00:00:00 - * century: date to first day of century and time to 00:00:00 - """ - if unit not in self._MODIFIERS_VALID_UNITS: - raise ValueError(f'Invalid unit "{unit}" for start_of()') - - return cast(DateTime, getattr(self, f"_start_of_{unit}")()) - - def end_of(self, unit: str) -> DateTime: - """ - Returns a copy of the instance with the time reset - with the following rules: - - * second: microsecond set to 999999 - * minute: second set to 59 and microsecond set to 999999 - * hour: minute and second set to 59 and microsecond set to 999999 - * day: time to 23:59:59.999999 - * week: date to last day of the week and time to 23:59:59.999999 - * month: date to last day of the month and time to 23:59:59.999999 - * year: date to last day of the year and time to 23:59:59.999999 - * decade: date to last day of the decade and time to 23:59:59.999999 - * century: date to last day of century and time to 23:59:59.999999 - """ - if unit not in self._MODIFIERS_VALID_UNITS: - raise ValueError(f'Invalid unit "{unit}" for end_of()') - - return cast(DateTime, getattr(self, f"_end_of_{unit}")()) - - def _start_of_second(self) -> DateTime: - """ - Reset microseconds to 0. - """ - return self.set(microsecond=0) - - def _end_of_second(self) -> DateTime: - """ - Set microseconds to 999999. - """ - return self.set(microsecond=999999) - - def _start_of_minute(self) -> DateTime: - """ - Reset seconds and microseconds to 0. - """ - return self.set(second=0, microsecond=0) - - def _end_of_minute(self) -> DateTime: - """ - Set seconds to 59 and microseconds to 999999. - """ - return self.set(second=59, microsecond=999999) - - def _start_of_hour(self) -> DateTime: - """ - Reset minutes, seconds and microseconds to 0. - """ - return self.set(minute=0, second=0, microsecond=0) - - def _end_of_hour(self) -> DateTime: - """ - Set minutes and seconds to 59 and microseconds to 999999. - """ - return self.set(minute=59, second=59, microsecond=999999) - - def _start_of_day(self) -> DateTime: - """ - Reset the time to 00:00:00. - """ - return self.at(0, 0, 0, 0) - - def _end_of_day(self) -> DateTime: - """ - Reset the time to 23:59:59.999999. - """ - return self.at(23, 59, 59, 999999) - - def _start_of_month(self) -> DateTime: - """ - Reset the date to the first day of the month and the time to 00:00:00. - """ - return self.set(self.year, self.month, 1, 0, 0, 0, 0) - - def _end_of_month(self) -> DateTime: - """ - Reset the date to the last day of the month - and the time to 23:59:59.999999. - """ - return self.set(self.year, self.month, self.days_in_month, 23, 59, 59, 999999) - - def _start_of_year(self) -> DateTime: - """ - Reset the date to the first day of the year and the time to 00:00:00. - """ - return self.set(self.year, 1, 1, 0, 0, 0, 0) - - def _end_of_year(self) -> DateTime: - """ - Reset the date to the last day of the year - and the time to 23:59:59.999999. - """ - return self.set(self.year, 12, 31, 23, 59, 59, 999999) - - def _start_of_decade(self) -> DateTime: - """ - Reset the date to the first day of the decade - and the time to 00:00:00. - """ - year = self.year - self.year % YEARS_PER_DECADE - return self.set(year, 1, 1, 0, 0, 0, 0) - - def _end_of_decade(self) -> DateTime: - """ - Reset the date to the last day of the decade - and the time to 23:59:59.999999. - """ - year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1 - - return self.set(year, 12, 31, 23, 59, 59, 999999) - - def _start_of_century(self) -> DateTime: - """ - Reset the date to the first day of the century - and the time to 00:00:00. - """ - year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1 - - return self.set(year, 1, 1, 0, 0, 0, 0) - - def _end_of_century(self) -> DateTime: - """ - Reset the date to the last day of the century - and the time to 23:59:59.999999. - """ - year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY - - return self.set(year, 12, 31, 23, 59, 59, 999999) - - def _start_of_week(self) -> DateTime: - """ - Reset the date to the first day of the week - and the time to 00:00:00. - """ - dt = self - - if self.day_of_week != pendulum._WEEK_STARTS_AT: - dt = self.previous(pendulum._WEEK_STARTS_AT) - - return dt.start_of("day") - - def _end_of_week(self) -> DateTime: - """ - Reset the date to the last day of the week - and the time to 23:59:59. - """ - dt = self - - if self.day_of_week != pendulum._WEEK_ENDS_AT: - dt = self.next(pendulum._WEEK_ENDS_AT) - - return dt.end_of("day") - - def next(self, day_of_week: int | None = None, keep_time: bool = False) -> DateTime: - """ - Modify to the next occurrence of a given day of the week. - If no day_of_week is provided, modify to the next occurrence - of the current day of the week. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - if day_of_week is None: - day_of_week = self.day_of_week - - if day_of_week < SUNDAY or day_of_week > SATURDAY: - raise ValueError("Invalid day of week") - - if keep_time: - dt = self - else: - dt = self.start_of("day") - - dt = dt.add(days=1) - while dt.day_of_week != day_of_week: - dt = dt.add(days=1) - - return dt - - def previous( - self, day_of_week: int | None = None, keep_time: bool = False - ) -> DateTime: - """ - Modify to the previous occurrence of a given day of the week. - If no day_of_week is provided, modify to the previous occurrence - of the current day of the week. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - if day_of_week is None: - day_of_week = self.day_of_week - - if day_of_week < SUNDAY or day_of_week > SATURDAY: - raise ValueError("Invalid day of week") - - if keep_time: - dt = self - else: - dt = self.start_of("day") - - dt = dt.subtract(days=1) - while dt.day_of_week != day_of_week: - dt = dt.subtract(days=1) - - return dt - - def first_of(self, unit: str, day_of_week: int | None = None) -> DateTime: - """ - Returns an instance set to the first occurrence - of a given day of the week in the current unit. - If no day_of_week is provided, modify to the first day of the unit. - Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - Supported units are month, quarter and year. - """ - if unit not in ["month", "quarter", "year"]: - raise ValueError(f'Invalid unit "{unit}" for first_of()') - - return cast(DateTime, getattr(self, f"_first_of_{unit}")(day_of_week)) - - def last_of(self, unit: str, day_of_week: int | None = None) -> DateTime: - """ - Returns an instance set to the last occurrence - of a given day of the week in the current unit. - If no day_of_week is provided, modify to the last day of the unit. - Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - Supported units are month, quarter and year. - """ - if unit not in ["month", "quarter", "year"]: - raise ValueError(f'Invalid unit "{unit}" for first_of()') - - return cast(DateTime, getattr(self, f"_last_of_{unit}")(day_of_week)) - - def nth_of(self, unit: str, nth: int, day_of_week: int) -> DateTime: - """ - Returns a new instance set to the given occurrence - of a given day of the week in the current unit. - If the calculated occurrence is outside the scope of the current unit, - then raise an error. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - - Supported units are month, quarter and year. - """ - if unit not in ["month", "quarter", "year"]: - raise ValueError(f'Invalid unit "{unit}" for first_of()') - - dt = cast( - Optional[DateTime], getattr(self, f"_nth_of_{unit}")(nth, day_of_week) - ) - if not dt: - raise PendulumException( - f"Unable to find occurence {nth}" - f" of {self._days[day_of_week]} in {unit}" - ) - - return dt - - def _first_of_month(self, day_of_week: int | None = None) -> DateTime: - """ - Modify to the first occurrence of a given day of the week - in the current month. If no day_of_week is provided, - modify to the first day of the month. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - dt = self.start_of("day") - - if day_of_week is None: - return dt.set(day=1) - - month = calendar.monthcalendar(dt.year, dt.month) - - calendar_day = (day_of_week - 1) % 7 - - if month[0][calendar_day] > 0: - day_of_month = month[0][calendar_day] - else: - day_of_month = month[1][calendar_day] - - return dt.set(day=day_of_month) - - def _last_of_month(self, day_of_week: int | None = None) -> DateTime: - """ - Modify to the last occurrence of a given day of the week - in the current month. If no day_of_week is provided, - modify to the last day of the month. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - dt = self.start_of("day") - - if day_of_week is None: - return dt.set(day=self.days_in_month) - - month = calendar.monthcalendar(dt.year, dt.month) - - calendar_day = (day_of_week - 1) % 7 - - if month[-1][calendar_day] > 0: - day_of_month = month[-1][calendar_day] - else: - day_of_month = month[-2][calendar_day] - - return dt.set(day=day_of_month) - - def _nth_of_month( - self, nth: int, day_of_week: int | None = None - ) -> DateTime | None: - """ - Modify to the given occurrence of a given day of the week - in the current month. If the calculated occurrence is outside, - the scope of the current month, then return False and no - modifications are made. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - if nth == 1: - return self.first_of("month", day_of_week) - - dt = self.first_of("month") - check = dt.format("%Y-%M") - for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): - dt = dt.next(day_of_week) - - if dt.format("%Y-%M") == check: - return self.set(day=dt.day).start_of("day") - - return None - - def _first_of_quarter(self, day_of_week: int | None = None) -> DateTime: - """ - Modify to the first occurrence of a given day of the week - in the current quarter. If no day_of_week is provided, - modify to the first day of the quarter. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - return self.on(self.year, self.quarter * 3 - 2, 1).first_of( - "month", day_of_week - ) - - def _last_of_quarter(self, day_of_week: int | None = None) -> DateTime: - """ - Modify to the last occurrence of a given day of the week - in the current quarter. If no day_of_week is provided, - modify to the last day of the quarter. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - return self.on(self.year, self.quarter * 3, 1).last_of("month", day_of_week) - - def _nth_of_quarter( - self, nth: int, day_of_week: int | None = None - ) -> DateTime | None: - """ - Modify to the given occurrence of a given day of the week - in the current quarter. If the calculated occurrence is outside, - the scope of the current quarter, then return False and no - modifications are made. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - if nth == 1: - return self.first_of("quarter", day_of_week) - - dt = self.set(day=1, month=self.quarter * 3) - last_month = dt.month - year = dt.year - dt = dt.first_of("quarter") - for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): - dt = dt.next(day_of_week) - - if last_month < dt.month or year != dt.year: - return None - - return self.on(self.year, dt.month, dt.day).start_of("day") - - def _first_of_year(self, day_of_week: int | None = None) -> DateTime: - """ - Modify to the first occurrence of a given day of the week - in the current year. If no day_of_week is provided, - modify to the first day of the year. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - return self.set(month=1).first_of("month", day_of_week) - - def _last_of_year(self, day_of_week: int | None = None) -> DateTime: - """ - Modify to the last occurrence of a given day of the week - in the current year. If no day_of_week is provided, - modify to the last day of the year. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - return self.set(month=MONTHS_PER_YEAR).last_of("month", day_of_week) - - def _nth_of_year(self, nth: int, day_of_week: int | None = None) -> DateTime | None: - """ - Modify to the given occurrence of a given day of the week - in the current year. If the calculated occurrence is outside, - the scope of the current year, then return False and no - modifications are made. Use the supplied consts - to indicate the desired day_of_week, ex. DateTime.MONDAY. - """ - if nth == 1: - return self.first_of("year", day_of_week) - - dt = self.first_of("year") - year = dt.year - for _ in range(nth - (1 if dt.day_of_week == day_of_week else 0)): - dt = dt.next(day_of_week) - - if year != dt.year: - return None - - return self.on(self.year, dt.month, dt.day).start_of("day") - - def average( # type: ignore[override] - self, dt: datetime.datetime | None = None - ) -> DateTime: - """ - Modify the current instance to the average - of a given instance (default now) and the current instance. - """ - if dt is None: - dt = self.now(self.tz) - - diff = self.diff(dt, False) - return self.add( - microseconds=(diff.in_seconds() * 1000000 + diff.microseconds) // 2 - ) - - @overload # type: ignore[override] - def __sub__(self, other: datetime.timedelta) -> DateTime: - ... - - @overload - def __sub__(self, other: DateTime) -> Interval: - ... - - def __sub__( - self, other: datetime.datetime | datetime.timedelta - ) -> DateTime | Interval: - if isinstance(other, datetime.timedelta): - return self._subtract_timedelta(other) - - if not isinstance(other, datetime.datetime): - return NotImplemented - - if not isinstance(other, self.__class__): - if other.tzinfo is None: - other = pendulum.naive( - other.year, - other.month, - other.day, - other.hour, - other.minute, - other.second, - other.microsecond, - ) - else: - other = pendulum.instance(other) - - return other.diff(self, False) - - def __rsub__(self, other: datetime.datetime) -> Interval: - if not isinstance(other, datetime.datetime): - return NotImplemented - - if not isinstance(other, self.__class__): - if other.tzinfo is None: - other = pendulum.naive( - other.year, - other.month, - other.day, - other.hour, - other.minute, - other.second, - other.microsecond, - ) - else: - other = pendulum.instance(other) - - return self.diff(other, False) - - def __add__(self, other: datetime.timedelta) -> DateTime: - if not isinstance(other, datetime.timedelta): - return NotImplemented - - if PY38: - # This is a workaround for Python 3.8+ - # since calling astimezone() will call this method - # instead of the base datetime class one. - import inspect - - caller = inspect.stack()[1][3] - if caller == "astimezone": - return cast(DateTime, super().__add__(other)) - - return self._add_timedelta_(other) - - def __radd__(self, other: datetime.timedelta) -> DateTime: - return self.__add__(other) - - # Native methods override - - @classmethod - def fromtimestamp(cls, t: float, tz: datetime.tzinfo | None = None) -> DateTime: - tzinfo = pendulum._safe_timezone(tz) - - return pendulum.instance( - datetime.datetime.fromtimestamp(t, tz=tzinfo), tz=tzinfo - ) - - @classmethod - def utcfromtimestamp(cls, t: float) -> DateTime: - return pendulum.instance(datetime.datetime.utcfromtimestamp(t), tz=None) - - @classmethod - def fromordinal(cls, n: int) -> DateTime: - return pendulum.instance(datetime.datetime.fromordinal(n), tz=None) - - @classmethod - def combine( - cls, - date: datetime.date, - time: datetime.time, - tzinfo: datetime.tzinfo | None = None, - ) -> DateTime: - return pendulum.instance(datetime.datetime.combine(date, time), tz=tzinfo) - - def astimezone(self, tz: datetime.tzinfo | None = None) -> DateTime: - dt = super().astimezone(tz) - - return self.__class__( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - fold=dt.fold, - tzinfo=dt.tzinfo, - ) - - def replace( - self, - year: int | None = None, - month: int | None = None, - day: int | None = None, - hour: int | None = None, - minute: int | None = None, - second: int | None = None, - microsecond: int | None = None, - tzinfo: bool | datetime.tzinfo | Literal[True] | None = True, - fold: int | None = None, - ) -> DateTime: - if year is None: - year = self.year - if month is None: - month = self.month - if day is None: - day = self.day - if hour is None: - hour = self.hour - if minute is None: - minute = self.minute - if second is None: - second = self.second - if microsecond is None: - microsecond = self.microsecond - if tzinfo is True: - tzinfo = self.tzinfo - if fold is None: - fold = self.fold - - if tzinfo is not None: - tzinfo = pendulum._safe_timezone(tzinfo) - - return DateTime.create( - year, - month, - day, - hour, - minute, - second, - microsecond, - tz=tzinfo, - fold=fold, - ) - - def __getnewargs__(self) -> tuple[DateTime]: - return (self,) - - def _getstate( - self, protocol: int = 3 - ) -> tuple[int, int, int, int, int, int, int, datetime.tzinfo | None]: - return ( - self.year, - self.month, - self.day, - self.hour, - self.minute, - self.second, - self.microsecond, - self.tzinfo, - ) - - def __reduce__( - self, - ) -> tuple[ - type[DateTime], tuple[int, int, int, int, int, int, int, datetime.tzinfo | None] - ]: - return self.__reduce_ex__(2) - - def __reduce_ex__( # type: ignore[override] - self, protocol: int - ) -> tuple[ - type[DateTime], tuple[int, int, int, int, int, int, int, datetime.tzinfo | None] - ]: - return self.__class__, self._getstate(protocol) - - def _cmp(self, other: datetime.datetime, **kwargs: Any) -> int: - # Fix for pypy which compares using this method - # which would lead to infinite recursion if we didn't override - dt = datetime.datetime( - self.year, - self.month, - self.day, - self.hour, - self.minute, - self.second, - self.microsecond, - tzinfo=self.tz, - fold=self.fold, - ) - - return 0 if dt == other else 1 if dt > other else -1 - - -DateTime.min: DateTime = DateTime(1, 1, 1, 0, 0, tzinfo=UTC) # type: ignore[misc] -DateTime.max: DateTime = DateTime( # type: ignore[misc] - 9999, 12, 31, 23, 59, 59, 999999, tzinfo=UTC -) -DateTime.EPOCH: DateTime = DateTime(1970, 1, 1, tzinfo=UTC) # type: ignore[misc] diff --git a/pendulum/duration.py b/pendulum/duration.py deleted file mode 100644 index a3a68b1..0000000 --- a/pendulum/duration.py +++ /dev/null @@ -1,502 +0,0 @@ -from __future__ import annotations - -from datetime import timedelta -from typing import cast -from typing import overload - -import pendulum - -from pendulum.constants import SECONDS_PER_DAY -from pendulum.constants import SECONDS_PER_HOUR -from pendulum.constants import SECONDS_PER_MINUTE -from pendulum.constants import US_PER_SECOND -from pendulum.utils._compat import PYPY - - -def _divide_and_round(a: float, b: float) -> int: - """divide a by b and round result to the nearest integer - - When the ratio is exactly half-way between two integers, - the even integer is returned. - """ - # Based on the reference implementation for divmod_near - # in Objects/longobject.c. - q, r = divmod(a, b) - - # The output of divmod() is either a float or an int, - # but we always want it to be an int. - q = int(q) - - # round up if either r / b > 0.5, or r / b == 0.5 and q is odd. - # The expression r / b > 0.5 is equivalent to 2 * r > b if b is - # positive, 2 * r < b if b negative. - r *= 2 - greater_than_half = r > b if b > 0 else r < b - if greater_than_half or r == b and q % 2 == 1: - q += 1 - - return q - - -class Duration(timedelta): - """ - Replacement for the standard timedelta class. - - Provides several improvements over the base class. - """ - - _total: float = 0 - _years: int = 0 - _months: int = 0 - _weeks: int = 0 - _days: int = 0 - _remaining_days: int = 0 - _seconds: int = 0 - _microseconds: int = 0 - - _y = None - _m = None - _w = None - _d = None - _h = None - _i = None - _s = None - _invert = None - - def __new__( - cls, - days: float = 0, - seconds: float = 0, - microseconds: float = 0, - milliseconds: float = 0, - minutes: float = 0, - hours: float = 0, - weeks: float = 0, - years: float = 0, - months: float = 0, - ) -> Duration: - if not isinstance(years, int) or not isinstance(months, int): - raise ValueError("Float year and months are not supported") - - self = timedelta.__new__( - cls, - days + years * 365 + months * 30, - seconds, - microseconds, - milliseconds, - minutes, - hours, - weeks, - ) - - # Intuitive normalization - total = self.total_seconds() - (years * 365 + months * 30) * SECONDS_PER_DAY - self._total = total - - m = 1 - if total < 0: - m = -1 - - self._microseconds = round(total % m * 1e6) - self._seconds = abs(int(total)) % SECONDS_PER_DAY * m - - _days = abs(int(total)) // SECONDS_PER_DAY * m - self._days = _days - self._remaining_days = abs(_days) % 7 * m - self._weeks = abs(_days) // 7 * m - self._months = months - self._years = years - - return self - - def total_minutes(self) -> float: - return self.total_seconds() / SECONDS_PER_MINUTE - - def total_hours(self) -> float: - return self.total_seconds() / SECONDS_PER_HOUR - - def total_days(self) -> float: - return self.total_seconds() / SECONDS_PER_DAY - - def total_weeks(self) -> float: - return self.total_days() / 7 - - if PYPY: - - def total_seconds(self) -> float: - days = 0 - - if hasattr(self, "_years"): - days += self._years * 365 - - if hasattr(self, "_months"): - days += self._months * 30 - - if hasattr(self, "_remaining_days"): - days += self._weeks * 7 + self._remaining_days - else: - days += self._days - - return ( - (days * SECONDS_PER_DAY + self._seconds) * US_PER_SECOND - + self._microseconds - ) / US_PER_SECOND - - @property - def years(self) -> int: - return self._years - - @property - def months(self) -> int: - return self._months - - @property - def weeks(self) -> int: - return self._weeks - - if PYPY: - - @property - def days(self) -> int: - return self._years * 365 + self._months * 30 + self._days - - @property - def remaining_days(self) -> int: - return self._remaining_days - - @property - def hours(self) -> int: - if self._h is None: - seconds = self._seconds - self._h = 0 - if abs(seconds) >= 3600: - self._h = (abs(seconds) // 3600 % 24) * self._sign(seconds) - - return self._h - - @property - def minutes(self) -> int: - if self._i is None: - seconds = self._seconds - self._i = 0 - if abs(seconds) >= 60: - self._i = (abs(seconds) // 60 % 60) * self._sign(seconds) - - return self._i - - @property - def seconds(self) -> int: - return self._seconds - - @property - def remaining_seconds(self) -> int: - if self._s is None: - self._s = self._seconds - self._s = abs(self._s) % 60 * self._sign(self._s) - - return self._s - - @property - def microseconds(self) -> int: - return self._microseconds - - @property - def invert(self) -> bool: - if self._invert is None: - self._invert = self.total_seconds() < 0 - - return self._invert - - def in_weeks(self) -> int: - return int(self.total_weeks()) - - def in_days(self) -> int: - return int(self.total_days()) - - def in_hours(self) -> int: - return int(self.total_hours()) - - def in_minutes(self) -> int: - return int(self.total_minutes()) - - def in_seconds(self) -> int: - return int(self.total_seconds()) - - def in_words(self, locale: str | None = None, separator: str = " ") -> str: - """ - Get the current interval in words in the current locale. - - Ex: 6 jours 23 heures 58 minutes - - :param locale: The locale to use. Defaults to current locale. - :param separator: The separator to use between each unit - """ - periods = [ - ("year", self.years), - ("month", self.months), - ("week", self.weeks), - ("day", self.remaining_days), - ("hour", self.hours), - ("minute", self.minutes), - ("second", self.remaining_seconds), - ] - - if locale is None: - locale = pendulum.get_locale() - - loaded_locale = pendulum.locale(locale) - - parts = [] - for period in periods: - unit, period_count = period - if abs(period_count) > 0: - translation = loaded_locale.translation( - f"units.{unit}.{loaded_locale.plural(abs(period_count))}" - ) - parts.append(translation.format(period_count)) - - if not parts: - count: int | str = 0 - if abs(self.microseconds) > 0: - unit = f"units.second.{loaded_locale.plural(1)}" - count = f"{abs(self.microseconds) / 1e6:.2f}" - else: - unit = f"units.microsecond.{loaded_locale.plural(0)}" - translation = loaded_locale.translation(unit) - parts.append(translation.format(count)) - - return separator.join(parts) - - def _sign(self, value: float) -> int: - if value < 0: - return -1 - - return 1 - - def as_timedelta(self) -> timedelta: - """ - Return the interval as a native timedelta. - """ - return timedelta(seconds=self.total_seconds()) - - def __str__(self) -> str: - return self.in_words() - - def __repr__(self) -> str: - rep = f"{self.__class__.__name__}(" - - if self._years: - rep += f"years={self._years}, " - - if self._months: - rep += f"months={self._months}, " - - if self._weeks: - rep += f"weeks={self._weeks}, " - - if self._days: - rep += f"days={self._remaining_days}, " - - if self.hours: - rep += f"hours={self.hours}, " - - if self.minutes: - rep += f"minutes={self.minutes}, " - - if self.remaining_seconds: - rep += f"seconds={self.remaining_seconds}, " - - if self.microseconds: - rep += f"microseconds={self.microseconds}, " - - rep += ")" - - return rep.replace(", )", ")") - - def __add__(self, other: timedelta) -> Duration: - if isinstance(other, timedelta): - return self.__class__(seconds=self.total_seconds() + other.total_seconds()) - - return NotImplemented - - __radd__ = __add__ - - def __sub__(self, other: timedelta) -> Duration: - if isinstance(other, timedelta): - return self.__class__(seconds=self.total_seconds() - other.total_seconds()) - - return NotImplemented - - def __neg__(self) -> Duration: - return self.__class__( - years=-self._years, - months=-self._months, - weeks=-self._weeks, - days=-self._remaining_days, - seconds=-self._seconds, - microseconds=-self._microseconds, - ) - - def _to_microseconds(self) -> int: - return (self._days * (24 * 3600) + self._seconds) * 1000000 + self._microseconds - - def __mul__(self, other: int | float) -> Duration: - if isinstance(other, int): - return self.__class__( - years=self._years * other, - months=self._months * other, - seconds=self._total * other, - ) - - if isinstance(other, float): - usec = self._to_microseconds() - a, b = other.as_integer_ratio() - - return self.__class__(0, 0, _divide_and_round(usec * a, b)) - - return NotImplemented - - __rmul__ = __mul__ - - @overload - def __floordiv__(self, other: timedelta) -> int: - ... - - @overload - def __floordiv__(self, other: int) -> Duration: - ... - - def __floordiv__(self, other: int | timedelta) -> int | Duration: - if not isinstance(other, (int, timedelta)): - return NotImplemented - - usec = self._to_microseconds() - if isinstance(other, timedelta): - return cast(int, usec // other._to_microseconds()) # type: ignore[attr-defined] - - if isinstance(other, int): - return self.__class__( - 0, - 0, - usec // other, - years=self._years // other, - months=self._months // other, - ) - - @overload - def __truediv__(self, other: timedelta) -> float: - ... - - @overload - def __truediv__(self, other: float) -> Duration: - ... - - def __truediv__(self, other: int | float | timedelta) -> Duration | float: - if not isinstance(other, (int, float, timedelta)): - return NotImplemented - - usec = self._to_microseconds() - if isinstance(other, timedelta): - return cast(float, usec / other._to_microseconds()) # type: ignore[attr-defined] - - if isinstance(other, int): - return self.__class__( - 0, - 0, - _divide_and_round(usec, other), - years=_divide_and_round(self._years, other), - months=_divide_and_round(self._months, other), - ) - - if isinstance(other, float): - a, b = other.as_integer_ratio() - - return self.__class__( - 0, - 0, - _divide_and_round(b * usec, a), - years=_divide_and_round(self._years * b, a), - months=_divide_and_round(self._months, other), - ) - - __div__ = __floordiv__ - - def __mod__(self, other: timedelta) -> Duration: - if isinstance(other, timedelta): - r = self._to_microseconds() % other._to_microseconds() # type: ignore[attr-defined] - - return self.__class__(0, 0, r) - - return NotImplemented - - def __divmod__(self, other: timedelta) -> tuple[int, Duration]: - if isinstance(other, timedelta): - q, r = divmod(self._to_microseconds(), other._to_microseconds()) # type: ignore[attr-defined] - - return q, self.__class__(0, 0, r) - - return NotImplemented - - -Duration.min = Duration(days=-999999999) -Duration.max = Duration( - days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999 -) -Duration.resolution = Duration(microseconds=1) - - -class AbsoluteDuration(Duration): - """ - Duration that expresses a time difference in absolute values. - """ - - def __new__( - cls, - days: float = 0, - seconds: float = 0, - microseconds: float = 0, - milliseconds: float = 0, - minutes: float = 0, - hours: float = 0, - weeks: float = 0, - years: float = 0, - months: float = 0, - ) -> AbsoluteDuration: - if not isinstance(years, int) or not isinstance(months, int): - raise ValueError("Float year and months are not supported") - - self = timedelta.__new__( - cls, days, seconds, microseconds, milliseconds, minutes, hours, weeks - ) - - # We need to compute the total_seconds() value - # on a native timedelta object - delta = timedelta( - days, seconds, microseconds, milliseconds, minutes, hours, weeks - ) - - # Intuitive normalization - self._total = delta.total_seconds() - total = abs(self._total) - - self._microseconds = round(total % 1 * 1e6) - self._seconds = int(total) % SECONDS_PER_DAY - - days = int(total) // SECONDS_PER_DAY - self._days = abs(days + years * 365 + months * 30) - self._remaining_days = days % 7 - self._weeks = days // 7 - self._months = abs(months) - self._years = abs(years) - - return self - - def total_seconds(self) -> float: - return abs(self._total) - - @property - def invert(self) -> bool: - if self._invert is None: - self._invert = self._total < 0 - - return self._invert diff --git a/pendulum/exceptions.py b/pendulum/exceptions.py deleted file mode 100644 index 3ab4db9..0000000 --- a/pendulum/exceptions.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import annotations - -from .parsing.exceptions import ParserError # noqa - - -class PendulumException(Exception): - - pass diff --git a/pendulum/formatting/__init__.py b/pendulum/formatting/__init__.py deleted file mode 100644 index 975c409..0000000 --- a/pendulum/formatting/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import annotations - -from pendulum.formatting.formatter import Formatter - -__all__ = ["Formatter"] diff --git a/pendulum/formatting/difference_formatter.py b/pendulum/formatting/difference_formatter.py deleted file mode 100644 index dad219d..0000000 --- a/pendulum/formatting/difference_formatter.py +++ /dev/null @@ -1,146 +0,0 @@ -from __future__ import annotations - -import typing as t - -from pendulum.locales.locale import Locale - -if t.TYPE_CHECKING: - from pendulum import Duration - - -class DifferenceFormatter: - """ - Handles formatting differences in text. - """ - - def __init__(self, locale: str = "en") -> None: - self._locale = Locale.load(locale) - - def format( - self, - diff: Duration, - is_now: bool = True, - absolute: bool = False, - locale: str | Locale | None = None, - ) -> str: - """ - Formats a difference. - - :param diff: The difference to format - :param is_now: Whether the difference includes now - :param absolute: Whether it's an absolute difference or not - :param locale: The locale to use - """ - if locale is None: - locale = self._locale - else: - locale = Locale.load(locale) - - if diff.years > 0: - unit = "year" - count = diff.years - - if diff.months > 6: - count += 1 - elif diff.months == 11 and (diff.weeks * 7 + diff.remaining_days) > 15: - unit = "year" - count = 1 - elif diff.months > 0: - unit = "month" - count = diff.months - - if (diff.weeks * 7 + diff.remaining_days) >= 27: - count += 1 - elif diff.weeks > 0: - unit = "week" - count = diff.weeks - - if diff.remaining_days > 3: - count += 1 - elif diff.remaining_days > 0: - unit = "day" - count = diff.remaining_days - - if diff.hours >= 22: - count += 1 - elif diff.hours > 0: - unit = "hour" - count = diff.hours - elif diff.minutes > 0: - unit = "minute" - count = diff.minutes - elif 10 < diff.remaining_seconds <= 59: - unit = "second" - count = diff.remaining_seconds - else: - # We check if the "a few seconds" unit exists - time = locale.get("custom.units.few_second") - if time is not None: - if absolute: - return t.cast(str, time) - - key = "custom" - is_future = diff.invert - if is_now: - if is_future: - key += ".from_now" - else: - key += ".ago" - else: - if is_future: - key += ".after" - else: - key += ".before" - - return t.cast(str, locale.get(key).format(time)) - else: - unit = "second" - count = diff.remaining_seconds - - if count == 0: - count = 1 - - if absolute: - key = f"translations.units.{unit}" - else: - is_future = diff.invert - - if is_now: - # Relative to now, so we can use - # the CLDR data - key = f"translations.relative.{unit}" - - if is_future: - key += ".future" - else: - key += ".past" - else: - # Absolute comparison - # So we have to use the custom locale data - - # Checking for special pluralization rules - key = "custom.units_relative" - if is_future: - key += f".{unit}.future" - else: - key += f".{unit}.past" - - trans = locale.get(key) - if not trans: - # No special rule - key = f"translations.units.{unit}.{locale.plural(count)}" - time = locale.get(key).format(count) - else: - time = trans[locale.plural(count)].format(count) - - key = "custom" - if is_future: - key += ".after" - else: - key += ".before" - - return t.cast(str, locale.get(key).format(time)) - - key += f".{locale.plural(count)}" - - return t.cast(str, locale.get(key).format(count)) diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py deleted file mode 100644 index f91d5d9..0000000 --- a/pendulum/formatting/formatter.py +++ /dev/null @@ -1,683 +0,0 @@ -from __future__ import annotations - -import datetime -import re - -from typing import TYPE_CHECKING -from typing import Any -from typing import Callable -from typing import Match -from typing import Sequence -from typing import cast - -import pendulum - -from pendulum.locales.locale import Locale - -if TYPE_CHECKING: - from pendulum import Timezone - -_MATCH_1 = r"\d" -_MATCH_2 = r"\d\d" -_MATCH_3 = r"\d{3}" -_MATCH_4 = r"\d{4}" -_MATCH_6 = r"[+-]?\d{6}" -_MATCH_1_TO_2 = r"\d\d?" -_MATCH_1_TO_2_LEFT_PAD = r"[0-9 ]\d?" -_MATCH_1_TO_3 = r"\d{1,3}" -_MATCH_1_TO_4 = r"\d{1,4}" -_MATCH_1_TO_6 = r"[+-]?\d{1,6}" -_MATCH_3_TO_4 = r"\d{3}\d?" -_MATCH_5_TO_6 = r"\d{5}\d?" -_MATCH_UNSIGNED = r"\d+" -_MATCH_SIGNED = r"[+-]?\d+" -_MATCH_OFFSET = r"[Zz]|[+-]\d\d:?\d\d" -_MATCH_SHORT_OFFSET = r"[Zz]|[+-]\d\d(?::?\d\d)?" -_MATCH_TIMESTAMP = r"[+-]?\d+(\.\d{1,6})?" -_MATCH_WORD = ( - "(?i)[0-9]*" - "['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+" - r"|[\u0600-\u06FF/]+(\s*?[\u0600-\u06FF]+){1,2}" -) -_MATCH_TIMEZONE = "[A-Za-z0-9-+]+(/[A-Za-z0-9-+_]+)?" - - -class Formatter: - _TOKENS: str = ( - r"\[([^\[]*)\]|\\(.)|" - "(" - "Mo|MM?M?M?" - "|Do|DDDo|DD?D?D?|ddd?d?|do?" - "|E{1,4}" - "|w[o|w]?|W[o|W]?|Qo?" - "|YYYY|YY|Y" - "|gg(ggg?)?|GG(GGG?)?" - "|a|A" - "|hh?|HH?|kk?" - "|mm?|ss?|S{1,9}" - "|x|X" - "|zz?|ZZ?" - "|LTS|LT|LL?L?L?" - ")" - ) - - _FORMAT_RE: re.Pattern[str] = re.compile(_TOKENS) - - _FROM_FORMAT_RE: re.Pattern[str] = re.compile(r"(?<!\\\[)" + _TOKENS + r"(?!\\\])") - - _LOCALIZABLE_TOKENS: dict[str, str | Callable[[Locale], Sequence[str]] | None] = { - "Qo": None, - "MMMM": "months.wide", - "MMM": "months.abbreviated", - "Mo": None, - "DDDo": None, - "Do": lambda locale: tuple( - rf"\d+{o}" for o in locale.get("custom.ordinal").values() - ), - "dddd": "days.wide", - "ddd": "days.abbreviated", - "dd": "days.short", - "do": None, - "Wo": None, - "wo": None, - "A": lambda locale: ( - locale.translation("day_periods.am"), - locale.translation("day_periods.pm"), - ), - "a": lambda locale: ( - locale.translation("day_periods.am").lower(), - locale.translation("day_periods.pm").lower(), - ), - } - - _TOKENS_RULES: dict[str, Callable[[pendulum.DateTime], str]] = { - # Year - "YYYY": lambda dt: f"{dt.year:d}", - "YY": lambda dt: f"{dt.year:d}"[2:], - "Y": lambda dt: f"{dt.year:d}", - # Quarter - "Q": lambda dt: f"{dt.quarter:d}", - # Month - "MM": lambda dt: f"{dt.month:02d}", - "M": lambda dt: f"{dt.month:d}", - # Day - "DD": lambda dt: f"{dt.day:02d}", - "D": lambda dt: f"{dt.day:d}", - # Day of Year - "DDDD": lambda dt: f"{dt.day_of_year:03d}", - "DDD": lambda dt: f"{dt.day_of_year:d}", - # Day of Week - "d": lambda dt: f"{dt.day_of_week:d}", - # Day of ISO Week - "E": lambda dt: f"{dt.isoweekday():d}", - # Hour - "HH": lambda dt: f"{dt.hour:02d}", - "H": lambda dt: f"{dt.hour:d}", - "hh": lambda dt: f"{dt.hour % 12 or 12:02d}", - "h": lambda dt: f"{dt.hour % 12 or 12:d}", - # Minute - "mm": lambda dt: f"{dt.minute:02d}", - "m": lambda dt: f"{dt.minute:d}", - # Second - "ss": lambda dt: f"{dt.second:02d}", - "s": lambda dt: f"{dt.second:d}", - # Fractional second - "S": lambda dt: f"{dt.microsecond // 100000:01d}", - "SS": lambda dt: f"{dt.microsecond // 10000:02d}", - "SSS": lambda dt: f"{dt.microsecond // 1000:03d}", - "SSSS": lambda dt: f"{dt.microsecond // 100:04d}", - "SSSSS": lambda dt: f"{dt.microsecond // 10:05d}", - "SSSSSS": lambda dt: f"{dt.microsecond:06d}", - # Timestamp - "X": lambda dt: f"{dt.int_timestamp:d}", - "x": lambda dt: f"{dt.int_timestamp * 1000 + dt.microsecond // 1000:d}", - # Timezone - "zz": lambda dt: f'{dt.tzname() if dt.tzinfo is not None else ""}', - "z": lambda dt: f'{dt.timezone_name or ""}', - } - - _DATE_FORMATS = { - "LTS": "formats.time.full", - "LT": "formats.time.short", - "L": "formats.date.short", - "LL": "formats.date.long", - "LLL": "formats.datetime.long", - "LLLL": "formats.datetime.full", - } - - _DEFAULT_DATE_FORMATS = { - "LTS": "h:mm:ss A", - "LT": "h:mm A", - "L": "MM/DD/YYYY", - "LL": "MMMM D, YYYY", - "LLL": "MMMM D, YYYY h:mm A", - "LLLL": "dddd, MMMM D, YYYY h:mm A", - } - - _REGEX_TOKENS: dict[str, str | Sequence[str] | None] = { - "Y": _MATCH_SIGNED, - "YY": (_MATCH_1_TO_2, _MATCH_2), - "YYYY": (_MATCH_1_TO_4, _MATCH_4), - "Q": _MATCH_1, - "Qo": None, - "M": _MATCH_1_TO_2, - "MM": (_MATCH_1_TO_2, _MATCH_2), - "MMM": _MATCH_WORD, - "MMMM": _MATCH_WORD, - "D": _MATCH_1_TO_2, - "DD": (_MATCH_1_TO_2_LEFT_PAD, _MATCH_2), - "DDD": _MATCH_1_TO_3, - "DDDD": _MATCH_3, - "dddd": _MATCH_WORD, - "ddd": _MATCH_WORD, - "dd": _MATCH_WORD, - "d": _MATCH_1, - "E": _MATCH_1, - "Do": None, - "H": _MATCH_1_TO_2, - "HH": (_MATCH_1_TO_2, _MATCH_2), - "h": _MATCH_1_TO_2, - "hh": (_MATCH_1_TO_2, _MATCH_2), - "m": _MATCH_1_TO_2, - "mm": (_MATCH_1_TO_2, _MATCH_2), - "s": _MATCH_1_TO_2, - "ss": (_MATCH_1_TO_2, _MATCH_2), - "S": (_MATCH_1_TO_3, _MATCH_1), - "SS": (_MATCH_1_TO_3, _MATCH_2), - "SSS": (_MATCH_1_TO_3, _MATCH_3), - "SSSS": _MATCH_UNSIGNED, - "SSSSS": _MATCH_UNSIGNED, - "SSSSSS": _MATCH_UNSIGNED, - "x": _MATCH_SIGNED, - "X": _MATCH_TIMESTAMP, - "ZZ": _MATCH_SHORT_OFFSET, - "Z": _MATCH_OFFSET, - "z": _MATCH_TIMEZONE, - } - - _PARSE_TOKENS: dict[str, Callable[[str], Any]] = { - "YYYY": lambda year: int(year), - "YY": lambda year: int(year), - "Q": lambda quarter: int(quarter), - "MMMM": lambda month: month, - "MMM": lambda month: month, - "MM": lambda month: int(month), - "M": lambda month: int(month), - "DDDD": lambda day: int(day), - "DDD": lambda day: int(day), - "DD": lambda day: int(day), - "D": lambda day: int(day), - "dddd": lambda weekday: weekday, - "ddd": lambda weekday: weekday, - "dd": lambda weekday: weekday, - "d": lambda weekday: int(weekday) % 7, - "E": lambda weekday: int(weekday), - "HH": lambda hour: int(hour), - "H": lambda hour: int(hour), - "hh": lambda hour: int(hour), - "h": lambda hour: int(hour), - "mm": lambda minute: int(minute), - "m": lambda minute: int(minute), - "ss": lambda second: int(second), - "s": lambda second: int(second), - "S": lambda us: int(us) * 100000, - "SS": lambda us: int(us) * 10000, - "SSS": lambda us: int(us) * 1000, - "SSSS": lambda us: int(us) * 100, - "SSSSS": lambda us: int(us) * 10, - "SSSSSS": lambda us: int(us), - "a": lambda meridiem: meridiem, - "X": lambda ts: float(ts), - "x": lambda ts: float(ts) / 1e3, - "ZZ": str, - "Z": str, - "z": str, - } - - def format( - self, dt: pendulum.DateTime, fmt: str, locale: str | Locale | None = None - ) -> str: - """ - Formats a DateTime instance with a given format and locale. - - :param dt: The instance to format - :param fmt: The format to use - :param locale: The locale to use - """ - loaded_locale: Locale = Locale.load(locale or pendulum.get_locale()) - - result = self._FORMAT_RE.sub( - lambda m: m.group(1) - if m.group(1) - else m.group(2) - if m.group(2) - else self._format_token(dt, m.group(3), loaded_locale), - fmt, - ) - - return result - - def _format_token(self, dt: pendulum.DateTime, token: str, locale: Locale) -> str: - """ - Formats a DateTime instance with a given token and locale. - - :param dt: The instance to format - :param token: The token to use - :param locale: The locale to use - """ - if token in self._DATE_FORMATS: - fmt = locale.get(f"custom.date_formats.{token}") - if fmt is None: - fmt = self._DEFAULT_DATE_FORMATS[token] - - return self.format(dt, fmt, locale) - - if token in self._LOCALIZABLE_TOKENS: - return self._format_localizable_token(dt, token, locale) - - if token in self._TOKENS_RULES: - return self._TOKENS_RULES[token](dt) - - # Timezone - if token in ["ZZ", "Z"]: - if dt.tzinfo is None: - return "" - - separator = ":" if token == "Z" else "" - offset = dt.utcoffset() or datetime.timedelta() - minutes = offset.total_seconds() / 60 - - if minutes >= 0: - sign = "+" - else: - sign = "-" - - hour, minute = divmod(abs(int(minutes)), 60) - - return f"{sign}{hour:02d}{separator}{minute:02d}" - - return token - - def _format_localizable_token( - self, dt: pendulum.DateTime, token: str, locale: Locale - ) -> str: - """ - Formats a DateTime instance - with a given localizable token and locale. - - :param dt: The instance to format - :param token: The token to use - :param locale: The locale to use - """ - if token == "MMM": - return cast(str, locale.get("translations.months.abbreviated")[dt.month]) - elif token == "MMMM": - return cast(str, locale.get("translations.months.wide")[dt.month]) - elif token == "dd": - return cast(str, locale.get("translations.days.short")[dt.day_of_week]) - elif token == "ddd": - return cast( - str, locale.get("translations.days.abbreviated")[dt.day_of_week] - ) - elif token == "dddd": - return cast(str, locale.get("translations.days.wide")[dt.day_of_week]) - elif token == "Do": - return locale.ordinalize(dt.day) - elif token == "do": - return locale.ordinalize(dt.day_of_week) - elif token == "Mo": - return locale.ordinalize(dt.month) - elif token == "Qo": - return locale.ordinalize(dt.quarter) - elif token == "wo": - return locale.ordinalize(dt.week_of_year) - elif token == "DDDo": - return locale.ordinalize(dt.day_of_year) - elif token == "A": - key = "translations.day_periods" - if dt.hour >= 12: - key += ".pm" - else: - key += ".am" - - return cast(str, locale.get(key)) - else: - return token - - def parse( - self, - time: str, - fmt: str, - now: pendulum.DateTime, - locale: str | None = None, - ) -> dict[str, Any]: - """ - Parses a time string matching a given format as a tuple. - - :param time: The timestring - :param fmt: The format - :param now: The datetime to use as "now" - :param locale: The locale to use - - :return: The parsed elements - """ - escaped_fmt = re.escape(fmt) - - tokens = self._FROM_FORMAT_RE.findall(escaped_fmt) - if not tokens: - raise ValueError("The given time string does not match the given format") - - if not locale: - locale = pendulum.get_locale() - - loaded_locale: Locale = Locale.load(locale) - - parsed = { - "year": None, - "month": None, - "day": None, - "hour": None, - "minute": None, - "second": None, - "microsecond": None, - "tz": None, - "quarter": None, - "day_of_week": None, - "day_of_year": None, - "meridiem": None, - "timestamp": None, - } - - pattern = self._FROM_FORMAT_RE.sub( - lambda m: self._replace_tokens(m.group(0), loaded_locale), escaped_fmt - ) - - if not re.search("^" + pattern + "$", time): - raise ValueError(f"String does not match format {fmt}") - - _get_parsed_values: Callable[ - [Match[str]], Any - ] = lambda m: self._get_parsed_values(m, parsed, loaded_locale, now) - - re.sub(pattern, _get_parsed_values, time) - - return self._check_parsed(parsed, now) - - def _check_parsed( - self, parsed: dict[str, Any], now: pendulum.DateTime - ) -> dict[str, Any]: - """ - Checks validity of parsed elements. - - :param parsed: The elements to parse. - - :return: The validated elements. - """ - validated: dict[str, int | Timezone | None] = { - "year": parsed["year"], - "month": parsed["month"], - "day": parsed["day"], - "hour": parsed["hour"], - "minute": parsed["minute"], - "second": parsed["second"], - "microsecond": parsed["microsecond"], - "tz": None, - } - - # If timestamp has been specified - # we use it and don't go any further - if parsed["timestamp"] is not None: - str_us = str(parsed["timestamp"]) - if "." in str_us: - microseconds = int(f'{str_us.split(".")[1].ljust(6, "0")}') - else: - microseconds = 0 - - from pendulum.helpers import local_time - - time = local_time(parsed["timestamp"], 0, microseconds) - validated["year"] = time[0] - validated["month"] = time[1] - validated["day"] = time[2] - validated["hour"] = time[3] - validated["minute"] = time[4] - validated["second"] = time[5] - validated["microsecond"] = time[6] - - return validated - - if parsed["quarter"] is not None: - if validated["year"] is not None: - dt = pendulum.datetime(validated["year"], 1, 1) - else: - dt = now - - dt = dt.start_of("year") - - while dt.quarter != parsed["quarter"]: - dt = dt.add(months=3) - - validated["year"] = dt.year - validated["month"] = dt.month - validated["day"] = dt.day - - if validated["year"] is None: - validated["year"] = now.year - - if parsed["day_of_year"] is not None: - dt = cast( - pendulum.DateTime, - pendulum.parse(f'{validated["year"]}-{parsed["day_of_year"]:>03d}'), - ) - - validated["month"] = dt.month - validated["day"] = dt.day - - if parsed["day_of_week"] is not None: - dt = pendulum.datetime( - cast(int, validated["year"]), - validated["month"] or now.month, - validated["day"] or now.day, - ) - dt = dt.start_of("week").subtract(days=1) - dt = dt.next(parsed["day_of_week"]) - validated["year"] = dt.year - validated["month"] = dt.month - validated["day"] = dt.day - - # Meridiem - if parsed["meridiem"] is not None: - # If the time is greater than 13:00:00 - # This is not valid - if validated["hour"] is None: - raise ValueError("Invalid Date") - - t = ( - validated["hour"], - validated["minute"], - validated["second"], - validated["microsecond"], - ) - if t >= (13, 0, 0, 0): - raise ValueError("Invalid date") - - pm = parsed["meridiem"] == "pm" - validated["hour"] %= 12 - if pm: - validated["hour"] += 12 - - if validated["month"] is None: - if parsed["year"] is not None: - validated["month"] = parsed["month"] or 1 - else: - validated["month"] = parsed["month"] or now.month - - if validated["day"] is None: - if parsed["year"] is not None or parsed["month"] is not None: - validated["day"] = parsed["day"] or 1 - else: - validated["day"] = parsed["day"] or now.day - - for part in ["hour", "minute", "second", "microsecond"]: - if validated[part] is None: - validated[part] = 0 - - validated["tz"] = parsed["tz"] - - return validated - - def _get_parsed_values( - self, - m: Match[str], - parsed: dict[str, Any], - locale: Locale, - now: pendulum.DateTime, - ) -> None: - for token, index in m.re.groupindex.items(): - if token in self._LOCALIZABLE_TOKENS: - self._get_parsed_locale_value(token, m.group(index), parsed, locale) - else: - self._get_parsed_value(token, m.group(index), parsed, now) - - def _get_parsed_value( - self, - token: str, - value: str, - parsed: dict[str, Any], - now: pendulum.DateTime, - ) -> None: - parsed_token = self._PARSE_TOKENS[token](value) - - if "Y" in token: - if token == "YY": - parsed_token = now.year // 100 * 100 + parsed_token - - parsed["year"] = parsed_token - elif token == "Q": - parsed["quarter"] = parsed_token - elif token in ["MM", "M"]: - parsed["month"] = parsed_token - elif token in ["DDDD", "DDD"]: - parsed["day_of_year"] = parsed_token - elif "D" in token: - parsed["day"] = parsed_token - elif "H" in token: - parsed["hour"] = parsed_token - elif token in ["hh", "h"]: - if parsed_token > 12: - raise ValueError("Invalid date") - - parsed["hour"] = parsed_token - elif "m" in token: - parsed["minute"] = parsed_token - elif "s" in token: - parsed["second"] = parsed_token - elif "S" in token: - parsed["microsecond"] = parsed_token - elif token in ["d", "E"]: - parsed["day_of_week"] = parsed_token - elif token in ["X", "x"]: - parsed["timestamp"] = parsed_token - elif token in ["ZZ", "Z"]: - negative = bool(value.startswith("-")) - tz = value[1:] - if ":" not in tz: - if len(tz) == 2: - tz = f"{tz}00" - - off_hour = tz[0:2] - off_minute = tz[2:4] - else: - off_hour, off_minute = tz.split(":") - - offset = ((int(off_hour) * 60) + int(off_minute)) * 60 - - if negative: - offset = -1 * offset - - parsed["tz"] = pendulum.timezone(offset) - elif token == "z": - # Full timezone - if value not in pendulum.timezones(): - raise ValueError("Invalid date") - - parsed["tz"] = pendulum.timezone(value) - - def _get_parsed_locale_value( - self, token: str, value: str, parsed: dict[str, Any], locale: Locale - ) -> None: - if token == "MMMM": - unit = "month" - match = "months.wide" - elif token == "MMM": - unit = "month" - match = "months.abbreviated" - elif token == "Do": - parsed["day"] = int(cast(Match[str], re.match(r"(\d+)", value)).group(1)) - - return - elif token == "dddd": - unit = "day_of_week" - match = "days.wide" - elif token == "ddd": - unit = "day_of_week" - match = "days.abbreviated" - elif token == "dd": - unit = "day_of_week" - match = "days.short" - elif token in ["a", "A"]: - valid_values = [ - locale.translation("day_periods.am"), - locale.translation("day_periods.pm"), - ] - - if token == "a": - value = value.lower() - valid_values = [x.lower() for x in valid_values] - - if value not in valid_values: - raise ValueError("Invalid date") - - parsed["meridiem"] = ["am", "pm"][valid_values.index(value)] - - return - else: - raise ValueError(f'Invalid token "{token}"') - - parsed[unit] = locale.match_translation(match, value) - if value is None: - raise ValueError("Invalid date") - - def _replace_tokens(self, token: str, locale: Locale) -> str: - if token.startswith("[") and token.endswith("]"): - return token[1:-1] - elif token.startswith("\\"): - if len(token) == 2 and token[1] in {"[", "]"}: - return "" - - return token - elif token not in self._REGEX_TOKENS and token not in self._LOCALIZABLE_TOKENS: - raise ValueError(f"Unsupported token: {token}") - - if token in self._LOCALIZABLE_TOKENS: - values = self._LOCALIZABLE_TOKENS[token] - if callable(values): - candidates = values(locale) - else: - candidates = tuple( - locale.translation( - cast(str, self._LOCALIZABLE_TOKENS[token]) - ).values() - ) - else: - candidates = cast(Sequence[str], self._REGEX_TOKENS[token]) - - if not candidates: - raise ValueError(f"Unsupported token: {token}") - - if not isinstance(candidates, tuple): - candidates = (cast(str, candidates),) - - pattern = f'(?P<{token}>{"|".join(candidates)})' - - return pattern diff --git a/pendulum/helpers.py b/pendulum/helpers.py deleted file mode 100644 index 13b7f22..0000000 --- a/pendulum/helpers.py +++ /dev/null @@ -1,223 +0,0 @@ -from __future__ import annotations - -import os -import struct - -from datetime import date -from datetime import datetime -from datetime import timedelta -from math import copysign -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import overload - -import pendulum - -from pendulum.constants import DAYS_PER_MONTHS -from pendulum.formatting.difference_formatter import DifferenceFormatter -from pendulum.locales.locale import Locale - -if TYPE_CHECKING: - # Prevent import cycles - from pendulum.duration import Duration - -with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" - -_DT = TypeVar("_DT", bound=datetime) -_D = TypeVar("_D", bound=date) - -try: - # nopycln: file # noqa: E800 - if not with_extensions or struct.calcsize("P") == 4: - raise ImportError() - - from pendulum._extensions._helpers import PreciseDiff - from pendulum._extensions._helpers import days_in_year - from pendulum._extensions._helpers import is_leap - from pendulum._extensions._helpers import is_long_year - from pendulum._extensions._helpers import local_time - from pendulum._extensions._helpers import precise_diff - from pendulum._extensions._helpers import timestamp - from pendulum._extensions._helpers import week_day -except ImportError: - from pendulum._extensions.helpers import PreciseDiff # type: ignore[misc] - from pendulum._extensions.helpers import days_in_year - from pendulum._extensions.helpers import is_leap - from pendulum._extensions.helpers import is_long_year - from pendulum._extensions.helpers import local_time - from pendulum._extensions.helpers import precise_diff # type: ignore[misc] - from pendulum._extensions.helpers import timestamp - from pendulum._extensions.helpers import week_day - -difference_formatter = DifferenceFormatter() - - -@overload -def add_duration( - dt: datetime, - years: int = 0, - months: int = 0, - weeks: int = 0, - days: int = 0, - hours: int = 0, - minutes: int = 0, - seconds: float = 0, - microseconds: int = 0, -) -> datetime: - ... - - -@overload -def add_duration( - dt: date, - years: int = 0, - months: int = 0, - weeks: int = 0, - days: int = 0, -) -> date: - pass - - -def add_duration( - dt: date | datetime, - years: int = 0, - months: int = 0, - weeks: int = 0, - days: int = 0, - hours: int = 0, - minutes: int = 0, - seconds: float = 0, - microseconds: int = 0, -) -> date | datetime: - """ - Adds a duration to a date/datetime instance. - """ - days += weeks * 7 - - if ( - isinstance(dt, date) - and not isinstance(dt, datetime) - and any([hours, minutes, seconds, microseconds]) - ): - raise RuntimeError("Time elements cannot be added to a date instance.") - - # Normalizing - if abs(microseconds) > 999999: - s = _sign(microseconds) - div, mod = divmod(microseconds * s, 1000000) - microseconds = mod * s - seconds += div * s - - if abs(seconds) > 59: - s = _sign(seconds) - div, mod = divmod(seconds * s, 60) # type: ignore[assignment] - seconds = mod * s - minutes += div * s - - if abs(minutes) > 59: - s = _sign(minutes) - div, mod = divmod(minutes * s, 60) - minutes = mod * s - hours += div * s - - if abs(hours) > 23: - s = _sign(hours) - div, mod = divmod(hours * s, 24) - hours = mod * s - days += div * s - - if abs(months) > 11: - s = _sign(months) - div, mod = divmod(months * s, 12) - months = mod * s - years += div * s - - year = dt.year + years - month = dt.month - - if months: - month += months - if month > 12: - year += 1 - month -= 12 - elif month < 1: - year -= 1 - month += 12 - - day = min(DAYS_PER_MONTHS[int(is_leap(year))][month], dt.day) - - dt = dt.replace(year=year, month=month, day=day) - - return dt + timedelta( - days=days, - hours=hours, - minutes=minutes, - seconds=seconds, - microseconds=microseconds, - ) - - -def format_diff( - diff: Duration, - is_now: bool = True, - absolute: bool = False, - locale: str | None = None, -) -> str: - if locale is None: - locale = get_locale() - - return difference_formatter.format(diff, is_now, absolute, locale) - - -def _sign(x: float) -> int: - return int(copysign(1, x)) - - -# Global helpers - - -def locale(name: str) -> Locale: - return Locale.load(name) - - -def set_locale(name: str) -> None: - locale(name) - - pendulum._LOCALE = name - - -def get_locale() -> str: - return pendulum._LOCALE - - -def week_starts_at(wday: int) -> None: - if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY: - raise ValueError("Invalid week day as start of week.") - - pendulum._WEEK_STARTS_AT = wday - - -def week_ends_at(wday: int) -> None: - if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY: - raise ValueError("Invalid week day as start of week.") - - pendulum._WEEK_ENDS_AT = wday - - -__all__ = [ - "PreciseDiff", - "days_in_year", - "is_leap", - "is_long_year", - "local_time", - "precise_diff", - "timestamp", - "week_day", - "add_duration", - "format_diff", - "locale", - "set_locale", - "get_locale", - "week_starts_at", - "week_ends_at", -] diff --git a/pendulum/interval.py b/pendulum/interval.py deleted file mode 100644 index f20042b..0000000 --- a/pendulum/interval.py +++ /dev/null @@ -1,448 +0,0 @@ -from __future__ import annotations - -import operator - -from datetime import date -from datetime import datetime -from datetime import timedelta -from typing import TYPE_CHECKING -from typing import Iterator -from typing import Union -from typing import cast -from typing import overload - -import pendulum - -from pendulum.constants import MONTHS_PER_YEAR -from pendulum.duration import Duration -from pendulum.helpers import precise_diff - -if TYPE_CHECKING: - from typing import SupportsIndex - - from pendulum.helpers import PreciseDiff - from pendulum.locales.locale import Locale # noqa - - -class Interval(Duration): - """ - A period of time between two datetimes. - """ - - @overload - def __new__( - cls, - start: pendulum.DateTime | datetime, - end: pendulum.DateTime | datetime, - absolute: bool = False, - ) -> Interval: - ... - - @overload - def __new__( - cls, - start: pendulum.Date | date, - end: pendulum.Date | date, - absolute: bool = False, - ) -> Interval: - ... - - def __new__( - cls, - start: pendulum.DateTime | pendulum.Date | datetime | date, - end: pendulum.DateTime | pendulum.Date | datetime | date, - absolute: bool = False, - ) -> Interval: - if ( - isinstance(start, datetime) - and not isinstance(end, datetime) - or not isinstance(start, datetime) - and isinstance(end, datetime) - ): - raise ValueError("Both start and end of a Period must have the same type") - - if ( - isinstance(start, datetime) - and isinstance(end, datetime) - and ( - start.tzinfo is None - and end.tzinfo is not None - or start.tzinfo is not None - and end.tzinfo is None - ) - ): - raise TypeError("can't compare offset-naive and offset-aware datetimes") - - if absolute and start > end: - end, start = start, end - - _start = start - _end = end - if isinstance(start, pendulum.DateTime): - _start = datetime( - start.year, - start.month, - start.day, - start.hour, - start.minute, - start.second, - start.microsecond, - tzinfo=start.tzinfo, - fold=start.fold, - ) - elif isinstance(start, pendulum.Date): - _start = date(start.year, start.month, start.day) - - if isinstance(end, pendulum.DateTime): - _end = datetime( - end.year, - end.month, - end.day, - end.hour, - end.minute, - end.second, - end.microsecond, - tzinfo=end.tzinfo, - fold=end.fold, - ) - elif isinstance(end, pendulum.Date): - _end = date(end.year, end.month, end.day) - - # Fixing issues with datetime.__sub__() - # not handling offsets if the tzinfo is the same - if ( - isinstance(_start, datetime) - and isinstance(_end, datetime) - and _start.tzinfo is _end.tzinfo - ): - if _start.tzinfo is not None: - offset = cast(timedelta, cast(datetime, start).utcoffset()) - _start = (_start - offset).replace(tzinfo=None) - - if isinstance(end, datetime) and _end.tzinfo is not None: - offset = cast(timedelta, end.utcoffset()) - _end = (_end - offset).replace(tzinfo=None) - - delta: timedelta = _end - _start # type: ignore[operator] - - return cast(Interval, super().__new__(cls, seconds=delta.total_seconds())) - - def __init__( - self, - start: pendulum.DateTime | pendulum.Date | datetime | date, - end: pendulum.DateTime | pendulum.Date | datetime | date, - absolute: bool = False, - ) -> None: - super().__init__() - - _start: pendulum.DateTime | pendulum.Date | datetime | date - if not isinstance(start, pendulum.Date): - if isinstance(start, datetime): - start = pendulum.instance(start) - else: - start = pendulum.date(start.year, start.month, start.day) - - _start = start - else: - if isinstance(start, pendulum.DateTime): - _start = datetime( - start.year, - start.month, - start.day, - start.hour, - start.minute, - start.second, - start.microsecond, - tzinfo=start.tzinfo, - ) - else: - _start = date(start.year, start.month, start.day) - - _end: pendulum.DateTime | pendulum.Date | datetime | date - if not isinstance(end, pendulum.Date): - if isinstance(end, datetime): - end = pendulum.instance(end) - else: - end = pendulum.date(end.year, end.month, end.day) - - _end = end - else: - if isinstance(end, pendulum.DateTime): - _end = datetime( - end.year, - end.month, - end.day, - end.hour, - end.minute, - end.second, - end.microsecond, - tzinfo=end.tzinfo, - ) - else: - _end = date(end.year, end.month, end.day) - - self._invert = False - if start > end: - self._invert = True - - if absolute: - end, start = start, end - _end, _start = _start, _end - - self._absolute = absolute - self._start: pendulum.DateTime | pendulum.Date = start - self._end: pendulum.DateTime | pendulum.Date = end - self._delta: PreciseDiff = precise_diff(_start, _end) - - @property - def years(self) -> int: - return self._delta.years - - @property - def months(self) -> int: - return self._delta.months - - @property - def weeks(self) -> int: - return abs(self._delta.days) // 7 * self._sign(self._delta.days) - - @property - def days(self) -> int: - return self._days - - @property - def remaining_days(self) -> int: - return abs(self._delta.days) % 7 * self._sign(self._days) - - @property - def hours(self) -> int: - return self._delta.hours - - @property - def minutes(self) -> int: - return self._delta.minutes - - @property - def start(self) -> pendulum.DateTime | pendulum.Date | datetime | date: - return self._start - - @property - def end(self) -> pendulum.DateTime | pendulum.Date | datetime | date: - return self._end - - def in_years(self) -> int: - """ - Gives the duration of the Period in full years. - """ - return self.years - - def in_months(self) -> int: - """ - Gives the duration of the Period in full months. - """ - return self.years * MONTHS_PER_YEAR + self.months - - def in_weeks(self) -> int: - days = self.in_days() - sign = 1 - - if days < 0: - sign = -1 - - return sign * (abs(days) // 7) - - def in_days(self) -> int: - return self._delta.total_days - - def in_words(self, locale: str | None = None, separator: str = " ") -> str: - """ - Get the current interval in words in the current locale. - - Ex: 6 jours 23 heures 58 minutes - - :param locale: The locale to use. Defaults to current locale. - :param separator: The separator to use between each unit - """ - from pendulum.locales.locale import Locale # noqa - - periods = [ - ("year", self.years), - ("month", self.months), - ("week", self.weeks), - ("day", self.remaining_days), - ("hour", self.hours), - ("minute", self.minutes), - ("second", self.remaining_seconds), - ] - loaded_locale: Locale = Locale.load(locale or pendulum.get_locale()) - parts = [] - for period in periods: - unit, period_count = period - if abs(period_count) > 0: - translation = loaded_locale.translation( - f"units.{unit}.{loaded_locale.plural(abs(period_count))}" - ) - parts.append(translation.format(period_count)) - - if not parts: - count: str | int = 0 - if abs(self.microseconds) > 0: - unit = f"units.second.{loaded_locale.plural(1)}" - count = f"{abs(self.microseconds) / 1e6:.2f}" - else: - unit = f"units.microsecond.{loaded_locale.plural(0)}" - - translation = loaded_locale.translation(unit) - parts.append(translation.format(count)) - - return separator.join(parts) - - def range( - self, unit: str, amount: int = 1 - ) -> Iterator[pendulum.DateTime | pendulum.Date]: - method = "add" - op = operator.le - if not self._absolute and self.invert: - method = "subtract" - op = operator.ge - - start, end = self.start, self.end - - i = amount - while op(start, end): - yield cast(Union[pendulum.DateTime, pendulum.Date], start) - - start = getattr(self.start, method)(**{unit: i}) - - i += amount - - def as_interval(self) -> Duration: - """ - Return the Period as a Duration. - """ - return Duration(seconds=self.total_seconds()) - - def __iter__(self) -> Iterator[pendulum.DateTime | pendulum.Date]: - return self.range("days") - - def __contains__( - self, item: datetime | date | pendulum.DateTime | pendulum.Date - ) -> bool: - return self.start <= item <= self.end - - def __add__(self, other: timedelta) -> Duration: - return self.as_interval().__add__(other) - - __radd__ = __add__ - - def __sub__(self, other: timedelta) -> Duration: - return self.as_interval().__sub__(other) - - def __neg__(self) -> Interval: - return self.__class__(self.end, self.start, self._absolute) - - def __mul__(self, other: int | float) -> Duration: - return self.as_interval().__mul__(other) - - __rmul__ = __mul__ - - @overload - def __floordiv__(self, other: timedelta) -> int: - ... - - @overload - def __floordiv__(self, other: int) -> Duration: - ... - - def __floordiv__(self, other: int | timedelta) -> int | Duration: - return self.as_interval().__floordiv__(other) - - __div__ = __floordiv__ # type: ignore[assignment] - - @overload - def __truediv__(self, other: timedelta) -> float: - ... - - @overload - def __truediv__(self, other: float) -> Duration: - ... - - def __truediv__(self, other: float | timedelta) -> Duration | float: - return self.as_interval().__truediv__(other) - - def __mod__(self, other: timedelta) -> Duration: - return self.as_interval().__mod__(other) - - def __divmod__(self, other: timedelta) -> tuple[int, Duration]: - return self.as_interval().__divmod__(other) - - def __abs__(self) -> Interval: - return self.__class__(self.start, self.end, absolute=True) - - def __repr__(self) -> str: - return f"<Period [{self._start} -> {self._end}]>" - - def __str__(self) -> str: - return self.__repr__() - - def _cmp(self, other: timedelta) -> int: - # Only needed for PyPy - assert isinstance(other, timedelta) - - if isinstance(other, Interval): - other = other.as_timedelta() - - td = self.as_timedelta() - - return 0 if td == other else 1 if td > other else -1 - - def _getstate( - self, protocol: SupportsIndex = 3 - ) -> tuple[ - pendulum.DateTime | pendulum.Date | datetime | date, - pendulum.DateTime | pendulum.Date | datetime | date, - bool, - ]: - start, end = self.start, self.end - - if self._invert and self._absolute: - end, start = start, end - - return start, end, self._absolute - - def __reduce__( - self, - ) -> tuple[ - type[Interval], - tuple[ - pendulum.DateTime | pendulum.Date | datetime | date, - pendulum.DateTime | pendulum.Date | datetime | date, - bool, - ], - ]: - return self.__reduce_ex__(2) - - def __reduce_ex__( - self, protocol: SupportsIndex - ) -> tuple[ - type[Interval], - tuple[ - pendulum.DateTime | pendulum.Date | datetime | date, - pendulum.DateTime | pendulum.Date | datetime | date, - bool, - ], - ]: - return self.__class__, self._getstate(protocol) - - def __hash__(self) -> int: - return hash((self.start, self.end, self._absolute)) - - def __eq__(self, other: object) -> bool: - if isinstance(other, Interval): - return (self.start, self.end, self._absolute) == ( - other.start, - other.end, - other._absolute, - ) - else: - return self.as_interval() == other diff --git a/pendulum/locales/__init__.py b/pendulum/locales/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/__init__.py +++ /dev/null diff --git a/pendulum/locales/cs/__init__.py b/pendulum/locales/cs/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/cs/__init__.py +++ /dev/null diff --git a/pendulum/locales/cs/custom.py b/pendulum/locales/cs/custom.py deleted file mode 100644 index 5f66b69..0000000 --- a/pendulum/locales/cs/custom.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -cs custom locale file. -""" - -translations = { - "units": {"few_second": "pár vteřin"}, - # Relative time - "ago": "{} zpět", - "from_now": "za {}", - "after": "{0} po", - "before": "{0} zpět", - # Ordinals - "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, - # Date formats - "date_formats": { - "LTS": "h:mm:ss", - "LT": "h:mm", - "L": "DD. M. YYYY", - "LL": "D. MMMM, YYYY", - "LLL": "D. MMMM, YYYY h:mm", - "LLLL": "dddd, D. MMMM, YYYY h:mm", - }, -} diff --git a/pendulum/locales/cs/locale.py b/pendulum/locales/cs/locale.py deleted file mode 100644 index 2c51c78..0000000 --- a/pendulum/locales/cs/locale.py +++ /dev/null @@ -1,266 +0,0 @@ -from .custom import translations as custom_translations - - -""" -cs locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "few" - if ((n == n and (n >= 2 and n <= 4)) and (0 == 0 and (0 == 0))) - else "many" - if (not (0 == 0 and (0 == 0))) - else "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "ne", - 1: "po", - 2: "út", - 3: "st", - 4: "čt", - 5: "pá", - 6: "so", - }, - "narrow": { - 0: "N", - 1: "P", - 2: "Ú", - 3: "S", - 4: "Č", - 5: "P", - 6: "S", - }, - "short": { - 0: "ne", - 1: "po", - 2: "út", - 3: "st", - 4: "čt", - 5: "pá", - 6: "so", - }, - "wide": { - 0: "neděle", - 1: "pondělí", - 2: "úterý", - 3: "středa", - 4: "čtvrtek", - 5: "pátek", - 6: "sobota", - }, - }, - "months": { - "abbreviated": { - 1: "led", - 2: "úno", - 3: "bře", - 4: "dub", - 5: "kvě", - 6: "čvn", - 7: "čvc", - 8: "srp", - 9: "zář", - 10: "říj", - 11: "lis", - 12: "pro", - }, - "narrow": { - 1: "1", - 2: "2", - 3: "3", - 4: "4", - 5: "5", - 6: "6", - 7: "7", - 8: "8", - 9: "9", - 10: "10", - 11: "11", - 12: "12", - }, - "wide": { - 1: "ledna", - 2: "února", - 3: "března", - 4: "dubna", - 5: "května", - 6: "června", - 7: "července", - 8: "srpna", - 9: "září", - 10: "října", - 11: "listopadu", - 12: "prosince", - }, - }, - "units": { - "year": { - "one": "{0} rok", - "few": "{0} roky", - "many": "{0} roku", - "other": "{0} let", - }, - "month": { - "one": "{0} měsíc", - "few": "{0} měsíce", - "many": "{0} měsíce", - "other": "{0} měsíců", - }, - "week": { - "one": "{0} týden", - "few": "{0} týdny", - "many": "{0} týdne", - "other": "{0} týdnů", - }, - "day": { - "one": "{0} den", - "few": "{0} dny", - "many": "{0} dne", - "other": "{0} dní", - }, - "hour": { - "one": "{0} hodina", - "few": "{0} hodiny", - "many": "{0} hodiny", - "other": "{0} hodin", - }, - "minute": { - "one": "{0} minuta", - "few": "{0} minuty", - "many": "{0} minuty", - "other": "{0} minut", - }, - "second": { - "one": "{0} sekunda", - "few": "{0} sekundy", - "many": "{0} sekundy", - "other": "{0} sekund", - }, - "microsecond": { - "one": "{0} mikrosekunda", - "few": "{0} mikrosekundy", - "many": "{0} mikrosekundy", - "other": "{0} mikrosekund", - }, - }, - "relative": { - "year": { - "future": { - "other": "za {0} let", - "one": "za {0} rok", - "few": "za {0} roky", - "many": "za {0} roku", - }, - "past": { - "other": "před {0} lety", - "one": "před {0} rokem", - "few": "před {0} lety", - "many": "před {0} roku", - }, - }, - "month": { - "future": { - "other": "za {0} měsíců", - "one": "za {0} měsíc", - "few": "za {0} měsíce", - "many": "za {0} měsíce", - }, - "past": { - "other": "před {0} měsíci", - "one": "před {0} měsícem", - "few": "před {0} měsíci", - "many": "před {0} měsíce", - }, - }, - "week": { - "future": { - "other": "za {0} týdnů", - "one": "za {0} týden", - "few": "za {0} týdny", - "many": "za {0} týdne", - }, - "past": { - "other": "před {0} týdny", - "one": "před {0} týdnem", - "few": "před {0} týdny", - "many": "před {0} týdne", - }, - }, - "day": { - "future": { - "other": "za {0} dní", - "one": "za {0} den", - "few": "za {0} dny", - "many": "za {0} dne", - }, - "past": { - "other": "před {0} dny", - "one": "před {0} dnem", - "few": "před {0} dny", - "many": "před {0} dne", - }, - }, - "hour": { - "future": { - "other": "za {0} hodin", - "one": "za {0} hodinu", - "few": "za {0} hodiny", - "many": "za {0} hodiny", - }, - "past": { - "other": "před {0} hodinami", - "one": "před {0} hodinou", - "few": "před {0} hodinami", - "many": "před {0} hodiny", - }, - }, - "minute": { - "future": { - "other": "za {0} minut", - "one": "za {0} minutu", - "few": "za {0} minuty", - "many": "za {0} minuty", - }, - "past": { - "other": "před {0} minutami", - "one": "před {0} minutou", - "few": "před {0} minutami", - "many": "před {0} minuty", - }, - }, - "second": { - "future": { - "other": "za {0} sekund", - "one": "za {0} sekundu", - "few": "za {0} sekundy", - "many": "za {0} sekundy", - }, - "past": { - "other": "před {0} sekundami", - "one": "před {0} sekundou", - "few": "před {0} sekundami", - "many": "před {0} sekundy", - }, - }, - }, - "day_periods": { - "midnight": "půlnoc", - "am": "dop.", - "noon": "poledne", - "pm": "odp.", - "morning1": "ráno", - "morning2": "dopoledne", - "afternoon1": "odpoledne", - "evening1": "večer", - "night1": "v noci", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/da/__init__.py b/pendulum/locales/da/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/da/__init__.py +++ /dev/null diff --git a/pendulum/locales/da/custom.py b/pendulum/locales/da/custom.py deleted file mode 100644 index c62ab83..0000000 --- a/pendulum/locales/da/custom.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -da custom locale file. -""" - -translations = { - # Relative time - "after": "{0} efter", - "before": "{0} før", - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd [d.] D. MMMM YYYY HH:mm", - "LLL": "D. MMMM YYYY HH:mm", - "LL": "D. MMMM YYYY", - "L": "DD/MM/YYYY", - }, -} diff --git a/pendulum/locales/da/locale.py b/pendulum/locales/da/locale.py deleted file mode 100644 index 936af3a..0000000 --- a/pendulum/locales/da/locale.py +++ /dev/null @@ -1,147 +0,0 @@ -from .custom import translations as custom_translations - - -""" -da locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" - if ( - (n == n and (n == 1)) - or ((not (0 == 0 and (0 == 0))) and (n == n and ((n == 0) or (n == 1)))) - ) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "søn.", - 1: "man.", - 2: "tir.", - 3: "ons.", - 4: "tor.", - 5: "fre.", - 6: "lør.", - }, - "narrow": {0: "S", 1: "M", 2: "T", 3: "O", 4: "T", 5: "F", 6: "L"}, - "short": {0: "sø", 1: "ma", 2: "ti", 3: "on", 4: "to", 5: "fr", 6: "lø"}, - "wide": { - 0: "søndag", - 1: "mandag", - 2: "tirsdag", - 3: "onsdag", - 4: "torsdag", - 5: "fredag", - 6: "lørdag", - }, - }, - "months": { - "abbreviated": { - 1: "jan.", - 2: "feb.", - 3: "mar.", - 4: "apr.", - 5: "maj", - 6: "jun.", - 7: "jul.", - 8: "aug.", - 9: "sep.", - 10: "okt.", - 11: "nov.", - 12: "dec.", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "januar", - 2: "februar", - 3: "marts", - 4: "april", - 5: "maj", - 6: "juni", - 7: "juli", - 8: "august", - 9: "september", - 10: "oktober", - 11: "november", - 12: "december", - }, - }, - "units": { - "year": {"one": "{0} år", "other": "{0} år"}, - "month": {"one": "{0} måned", "other": "{0} måneder"}, - "week": {"one": "{0} uge", "other": "{0} uger"}, - "day": {"one": "{0} dag", "other": "{0} dage"}, - "hour": {"one": "{0} time", "other": "{0} timer"}, - "minute": {"one": "{0} minut", "other": "{0} minutter"}, - "second": {"one": "{0} sekund", "other": "{0} sekunder"}, - "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekunder"}, - }, - "relative": { - "year": { - "future": {"other": "om {0} år", "one": "om {0} år"}, - "past": {"other": "for {0} år siden", "one": "for {0} år siden"}, - }, - "month": { - "future": {"other": "om {0} måneder", "one": "om {0} måned"}, - "past": { - "other": "for {0} måneder siden", - "one": "for {0} måned siden", - }, - }, - "week": { - "future": {"other": "om {0} uger", "one": "om {0} uge"}, - "past": {"other": "for {0} uger siden", "one": "for {0} uge siden"}, - }, - "day": { - "future": {"other": "om {0} dage", "one": "om {0} dag"}, - "past": {"other": "for {0} dage siden", "one": "for {0} dag siden"}, - }, - "hour": { - "future": {"other": "om {0} timer", "one": "om {0} time"}, - "past": {"other": "for {0} timer siden", "one": "for {0} time siden"}, - }, - "minute": { - "future": {"other": "om {0} minutter", "one": "om {0} minut"}, - "past": { - "other": "for {0} minutter siden", - "one": "for {0} minut siden", - }, - }, - "second": { - "future": {"other": "om {0} sekunder", "one": "om {0} sekund"}, - "past": { - "other": "for {0} sekunder siden", - "one": "for {0} sekund siden", - }, - }, - }, - "day_periods": { - "midnight": "midnat", - "am": "AM", - "pm": "PM", - "morning1": "om morgenen", - "morning2": "om formiddagen", - "afternoon1": "om eftermiddagen", - "evening1": "om aftenen", - "night1": "om natten", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/de/__init__.py b/pendulum/locales/de/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/de/__init__.py +++ /dev/null diff --git a/pendulum/locales/de/custom.py b/pendulum/locales/de/custom.py deleted file mode 100644 index a19a8e1..0000000 --- a/pendulum/locales/de/custom.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -de custom locale file. -""" - -translations = { - # Relative time - "after": "{0} später", - "before": "{0} zuvor", - "units_relative": { - "year": { - "future": {"one": "{0} Jahr", "other": "{0} Jahren"}, - "past": {"one": "{0} Jahr", "other": "{0} Jahren"}, - }, - "month": { - "future": {"one": "{0} Monat", "other": "{0} Monaten"}, - "past": {"one": "{0} Monat", "other": "{0} Monaten"}, - }, - "week": { - "future": {"one": "{0} Woche", "other": "{0} Wochen"}, - "past": {"one": "{0} Woche", "other": "{0} Wochen"}, - }, - "day": { - "future": {"one": "{0} Tag", "other": "{0} Tagen"}, - "past": {"one": "{0} Tag", "other": "{0} Tagen"}, - }, - }, - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd, D. MMMM YYYY HH:mm", - "LLL": "D. MMMM YYYY HH:mm", - "LL": "D. MMMM YYYY", - "L": "DD.MM.YYYY", - }, -} diff --git a/pendulum/locales/de/locale.py b/pendulum/locales/de/locale.py deleted file mode 100644 index 94d2ff1..0000000 --- a/pendulum/locales/de/locale.py +++ /dev/null @@ -1,144 +0,0 @@ -from .custom import translations as custom_translations - - -""" -de locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "So.", - 1: "Mo.", - 2: "Di.", - 3: "Mi.", - 4: "Do.", - 5: "Fr.", - 6: "Sa.", - }, - "narrow": {0: "S", 1: "M", 2: "D", 3: "M", 4: "D", 5: "F", 6: "S"}, - "short": { - 0: "So.", - 1: "Mo.", - 2: "Di.", - 3: "Mi.", - 4: "Do.", - 5: "Fr.", - 6: "Sa.", - }, - "wide": { - 0: "Sonntag", - 1: "Montag", - 2: "Dienstag", - 3: "Mittwoch", - 4: "Donnerstag", - 5: "Freitag", - 6: "Samstag", - }, - }, - "months": { - "abbreviated": { - 1: "Jan.", - 2: "Feb.", - 3: "März", - 4: "Apr.", - 5: "Mai", - 6: "Juni", - 7: "Juli", - 8: "Aug.", - 9: "Sep.", - 10: "Okt.", - 11: "Nov.", - 12: "Dez.", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "Januar", - 2: "Februar", - 3: "März", - 4: "April", - 5: "Mai", - 6: "Juni", - 7: "Juli", - 8: "August", - 9: "September", - 10: "Oktober", - 11: "November", - 12: "Dezember", - }, - }, - "units": { - "year": {"one": "{0} Jahr", "other": "{0} Jahre"}, - "month": {"one": "{0} Monat", "other": "{0} Monate"}, - "week": {"one": "{0} Woche", "other": "{0} Wochen"}, - "day": {"one": "{0} Tag", "other": "{0} Tage"}, - "hour": {"one": "{0} Stunde", "other": "{0} Stunden"}, - "minute": {"one": "{0} Minute", "other": "{0} Minuten"}, - "second": {"one": "{0} Sekunde", "other": "{0} Sekunden"}, - "microsecond": {"one": "{0} Mikrosekunde", "other": "{0} Mikrosekunden"}, - }, - "relative": { - "year": { - "future": {"other": "in {0} Jahren", "one": "in {0} Jahr"}, - "past": {"other": "vor {0} Jahren", "one": "vor {0} Jahr"}, - }, - "month": { - "future": {"other": "in {0} Monaten", "one": "in {0} Monat"}, - "past": {"other": "vor {0} Monaten", "one": "vor {0} Monat"}, - }, - "week": { - "future": {"other": "in {0} Wochen", "one": "in {0} Woche"}, - "past": {"other": "vor {0} Wochen", "one": "vor {0} Woche"}, - }, - "day": { - "future": {"other": "in {0} Tagen", "one": "in {0} Tag"}, - "past": {"other": "vor {0} Tagen", "one": "vor {0} Tag"}, - }, - "hour": { - "future": {"other": "in {0} Stunden", "one": "in {0} Stunde"}, - "past": {"other": "vor {0} Stunden", "one": "vor {0} Stunde"}, - }, - "minute": { - "future": {"other": "in {0} Minuten", "one": "in {0} Minute"}, - "past": {"other": "vor {0} Minuten", "one": "vor {0} Minute"}, - }, - "second": { - "future": {"other": "in {0} Sekunden", "one": "in {0} Sekunde"}, - "past": {"other": "vor {0} Sekunden", "one": "vor {0} Sekunde"}, - }, - }, - "day_periods": { - "midnight": "Mitternacht", - "am": "vorm.", - "pm": "nachm.", - "morning1": "morgens", - "morning2": "vormittags", - "afternoon1": "mittags", - "afternoon2": "nachmittags", - "evening1": "abends", - "night1": "nachts", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/en/__init__.py b/pendulum/locales/en/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/en/__init__.py +++ /dev/null diff --git a/pendulum/locales/en/custom.py b/pendulum/locales/en/custom.py deleted file mode 100644 index a403ad8..0000000 --- a/pendulum/locales/en/custom.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -en custom locale file. -""" - -translations = { - "units": {"few_second": "a few seconds"}, - # Relative time - "ago": "{} ago", - "from_now": "in {}", - "after": "{0} after", - "before": "{0} before", - # Ordinals - "ordinal": {"one": "st", "two": "nd", "few": "rd", "other": "th"}, - # Date formats - "date_formats": { - "LTS": "h:mm:ss A", - "LT": "h:mm A", - "L": "MM/DD/YYYY", - "LL": "MMMM D, YYYY", - "LLL": "MMMM D, YYYY h:mm A", - "LLLL": "dddd, MMMM D, YYYY h:mm A", - }, -} diff --git a/pendulum/locales/en/locale.py b/pendulum/locales/en/locale.py deleted file mode 100644 index 00eafc2..0000000 --- a/pendulum/locales/en/locale.py +++ /dev/null @@ -1,150 +0,0 @@ -from .custom import translations as custom_translations - - -""" -en locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "few" - if ( - ((n % 10) == (n % 10) and ((n % 10) == 3)) - and (not ((n % 100) == (n % 100) and ((n % 100) == 13))) - ) - else "one" - if ( - ((n % 10) == (n % 10) and ((n % 10) == 1)) - and (not ((n % 100) == (n % 100) and ((n % 100) == 11))) - ) - else "two" - if ( - ((n % 10) == (n % 10) and ((n % 10) == 2)) - and (not ((n % 100) == (n % 100) and ((n % 100) == 12))) - ) - else "other", - "translations": { - "days": { - "abbreviated": { - 0: "Sun", - 1: "Mon", - 2: "Tue", - 3: "Wed", - 4: "Thu", - 5: "Fri", - 6: "Sat", - }, - "narrow": {0: "S", 1: "M", 2: "T", 3: "W", 4: "T", 5: "F", 6: "S"}, - "short": {0: "Su", 1: "Mo", 2: "Tu", 3: "We", 4: "Th", 5: "Fr", 6: "Sa"}, - "wide": { - 0: "Sunday", - 1: "Monday", - 2: "Tuesday", - 3: "Wednesday", - 4: "Thursday", - 5: "Friday", - 6: "Saturday", - }, - }, - "months": { - "abbreviated": { - 1: "Jan", - 2: "Feb", - 3: "Mar", - 4: "Apr", - 5: "May", - 6: "Jun", - 7: "Jul", - 8: "Aug", - 9: "Sep", - 10: "Oct", - 11: "Nov", - 12: "Dec", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "January", - 2: "February", - 3: "March", - 4: "April", - 5: "May", - 6: "June", - 7: "July", - 8: "August", - 9: "September", - 10: "October", - 11: "November", - 12: "December", - }, - }, - "units": { - "year": {"one": "{0} year", "other": "{0} years"}, - "month": {"one": "{0} month", "other": "{0} months"}, - "week": {"one": "{0} week", "other": "{0} weeks"}, - "day": {"one": "{0} day", "other": "{0} days"}, - "hour": {"one": "{0} hour", "other": "{0} hours"}, - "minute": {"one": "{0} minute", "other": "{0} minutes"}, - "second": {"one": "{0} second", "other": "{0} seconds"}, - "microsecond": {"one": "{0} microsecond", "other": "{0} microseconds"}, - }, - "relative": { - "year": { - "future": {"other": "in {0} years", "one": "in {0} year"}, - "past": {"other": "{0} years ago", "one": "{0} year ago"}, - }, - "month": { - "future": {"other": "in {0} months", "one": "in {0} month"}, - "past": {"other": "{0} months ago", "one": "{0} month ago"}, - }, - "week": { - "future": {"other": "in {0} weeks", "one": "in {0} week"}, - "past": {"other": "{0} weeks ago", "one": "{0} week ago"}, - }, - "day": { - "future": {"other": "in {0} days", "one": "in {0} day"}, - "past": {"other": "{0} days ago", "one": "{0} day ago"}, - }, - "hour": { - "future": {"other": "in {0} hours", "one": "in {0} hour"}, - "past": {"other": "{0} hours ago", "one": "{0} hour ago"}, - }, - "minute": { - "future": {"other": "in {0} minutes", "one": "in {0} minute"}, - "past": {"other": "{0} minutes ago", "one": "{0} minute ago"}, - }, - "second": { - "future": {"other": "in {0} seconds", "one": "in {0} second"}, - "past": {"other": "{0} seconds ago", "one": "{0} second ago"}, - }, - }, - "day_periods": { - "midnight": "midnight", - "am": "AM", - "noon": "noon", - "pm": "PM", - "morning1": "in the morning", - "afternoon1": "in the afternoon", - "evening1": "in the evening", - "night1": "at night", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/es/__init__.py b/pendulum/locales/es/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/es/__init__.py +++ /dev/null diff --git a/pendulum/locales/es/custom.py b/pendulum/locales/es/custom.py deleted file mode 100644 index 4b7e2b5..0000000 --- a/pendulum/locales/es/custom.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -es custom locale file. -""" - -translations = { - "units": {"few_second": "unos segundos"}, - # Relative time - "ago": "hace {0}", - "from_now": "dentro de {0}", - "after": "{0} después", - "before": "{0} antes", - # Ordinals - "ordinal": {"other": "º"}, - # Date formats - "date_formats": { - "LTS": "H:mm:ss", - "LT": "H:mm", - "LLLL": "dddd, D [de] MMMM [de] YYYY H:mm", - "LLL": "D [de] MMMM [de] YYYY H:mm", - "LL": "D [de] MMMM [de] YYYY", - "L": "DD/MM/YYYY", - }, -} diff --git a/pendulum/locales/es/locale.py b/pendulum/locales/es/locale.py deleted file mode 100644 index edba4d3..0000000 --- a/pendulum/locales/es/locale.py +++ /dev/null @@ -1,141 +0,0 @@ -from .custom import translations as custom_translations - - -""" -es locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" if (n == n and (n == 1)) else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "dom.", - 1: "lun.", - 2: "mar.", - 3: "mié.", - 4: "jue.", - 5: "vie.", - 6: "sáb.", - }, - "narrow": {0: "D", 1: "L", 2: "M", 3: "X", 4: "J", 5: "V", 6: "S"}, - "short": {0: "DO", 1: "LU", 2: "MA", 3: "MI", 4: "JU", 5: "VI", 6: "SA"}, - "wide": { - 0: "domingo", - 1: "lunes", - 2: "martes", - 3: "miércoles", - 4: "jueves", - 5: "viernes", - 6: "sábado", - }, - }, - "months": { - "abbreviated": { - 1: "ene.", - 2: "feb.", - 3: "mar.", - 4: "abr.", - 5: "may.", - 6: "jun.", - 7: "jul.", - 8: "ago.", - 9: "sept.", - 10: "oct.", - 11: "nov.", - 12: "dic.", - }, - "narrow": { - 1: "E", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "enero", - 2: "febrero", - 3: "marzo", - 4: "abril", - 5: "mayo", - 6: "junio", - 7: "julio", - 8: "agosto", - 9: "septiembre", - 10: "octubre", - 11: "noviembre", - 12: "diciembre", - }, - }, - "units": { - "year": {"one": "{0} año", "other": "{0} años"}, - "month": {"one": "{0} mes", "other": "{0} meses"}, - "week": {"one": "{0} semana", "other": "{0} semanas"}, - "day": {"one": "{0} día", "other": "{0} días"}, - "hour": {"one": "{0} hora", "other": "{0} horas"}, - "minute": {"one": "{0} minuto", "other": "{0} minutos"}, - "second": {"one": "{0} segundo", "other": "{0} segundos"}, - "microsecond": {"one": "{0} microsegundo", "other": "{0} microsegundos"}, - }, - "relative": { - "year": { - "future": {"other": "dentro de {0} años", "one": "dentro de {0} año"}, - "past": {"other": "hace {0} años", "one": "hace {0} año"}, - }, - "month": { - "future": {"other": "dentro de {0} meses", "one": "dentro de {0} mes"}, - "past": {"other": "hace {0} meses", "one": "hace {0} mes"}, - }, - "week": { - "future": { - "other": "dentro de {0} semanas", - "one": "dentro de {0} semana", - }, - "past": {"other": "hace {0} semanas", "one": "hace {0} semana"}, - }, - "day": { - "future": {"other": "dentro de {0} días", "one": "dentro de {0} día"}, - "past": {"other": "hace {0} días", "one": "hace {0} día"}, - }, - "hour": { - "future": {"other": "dentro de {0} horas", "one": "dentro de {0} hora"}, - "past": {"other": "hace {0} horas", "one": "hace {0} hora"}, - }, - "minute": { - "future": { - "other": "dentro de {0} minutos", - "one": "dentro de {0} minuto", - }, - "past": {"other": "hace {0} minutos", "one": "hace {0} minuto"}, - }, - "second": { - "future": { - "other": "dentro de {0} segundos", - "one": "dentro de {0} segundo", - }, - "past": {"other": "hace {0} segundos", "one": "hace {0} segundo"}, - }, - }, - "day_periods": { - "am": "a. m.", - "noon": "del mediodía", - "pm": "p. m.", - "morning1": "de la madrugada", - "morning2": "de la mañana", - "evening1": "de la tarde", - "night1": "de la noche", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/fa/__init__.py b/pendulum/locales/fa/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/fa/__init__.py +++ /dev/null diff --git a/pendulum/locales/fa/custom.py b/pendulum/locales/fa/custom.py deleted file mode 100644 index 082bfad..0000000 --- a/pendulum/locales/fa/custom.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -fa custom locale file. -""" - -translations = { - # Relative time - "after": "{0} پس از", - "before": "{0} پیش از", - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd, D MMMM YYYY HH:mm", - "LLL": "D MMMM YYYY HH:mm", - "LL": "D MMMM YYYY", - "L": "DD/MM/YYYY", - }, -} diff --git a/pendulum/locales/fa/locale.py b/pendulum/locales/fa/locale.py deleted file mode 100644 index 32f8e5f..0000000 --- a/pendulum/locales/fa/locale.py +++ /dev/null @@ -1,135 +0,0 @@ -from .custom import translations as custom_translations - - -""" -fa locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" - if ((n == n and (n == 0)) or (n == n and (n == 1))) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "یکشنبه", - 1: "دوشنبه", - 2: "سه\u200cشنبه", - 3: "چهارشنبه", - 4: "پنجشنبه", - 5: "جمعه", - 6: "شنبه", - }, - "narrow": {0: "ی", 1: "د", 2: "س", 3: "چ", 4: "پ", 5: "ج", 6: "ش"}, - "short": {0: "۱ش", 1: "۲ش", 2: "۳ش", 3: "۴ش", 4: "۵ش", 5: "ج", 6: "ش"}, - "wide": { - 0: "یکشنبه", - 1: "دوشنبه", - 2: "سه\u200cشنبه", - 3: "چهارشنبه", - 4: "پنجشنبه", - 5: "جمعه", - 6: "شنبه", - }, - }, - "months": { - "abbreviated": { - 1: "ژانویهٔ", - 2: "فوریهٔ", - 3: "مارس", - 4: "آوریل", - 5: "مهٔ", - 6: "ژوئن", - 7: "ژوئیهٔ", - 8: "اوت", - 9: "سپتامبر", - 10: "اکتبر", - 11: "نوامبر", - 12: "دسامبر", - }, - "narrow": { - 1: "ژ", - 2: "ف", - 3: "م", - 4: "آ", - 5: "م", - 6: "ژ", - 7: "ژ", - 8: "ا", - 9: "س", - 10: "ا", - 11: "ن", - 12: "د", - }, - "wide": { - 1: "ژانویهٔ", - 2: "فوریهٔ", - 3: "مارس", - 4: "آوریل", - 5: "مهٔ", - 6: "ژوئن", - 7: "ژوئیهٔ", - 8: "اوت", - 9: "سپتامبر", - 10: "اکتبر", - 11: "نوامبر", - 12: "دسامبر", - }, - }, - "units": { - "year": {"one": "{0} سال", "other": "{0} سال"}, - "month": {"one": "{0} ماه", "other": "{0} ماه"}, - "week": {"one": "{0} هفته", "other": "{0} هفته"}, - "day": {"one": "{0} روز", "other": "{0} روز"}, - "hour": {"one": "{0} ساعت", "other": "{0} ساعت"}, - "minute": {"one": "{0} دقیقه", "other": "{0} دقیقه"}, - "second": {"one": "{0} ثانیه", "other": "{0} ثانیه"}, - "microsecond": {"one": "{0} میکروثانیه", "other": "{0} میکروثانیه"}, - }, - "relative": { - "year": { - "future": {"other": "{0} سال بعد", "one": "{0} سال بعد"}, - "past": {"other": "{0} سال پیش", "one": "{0} سال پیش"}, - }, - "month": { - "future": {"other": "{0} ماه بعد", "one": "{0} ماه بعد"}, - "past": {"other": "{0} ماه پیش", "one": "{0} ماه پیش"}, - }, - "week": { - "future": {"other": "{0} هفته بعد", "one": "{0} هفته بعد"}, - "past": {"other": "{0} هفته پیش", "one": "{0} هفته پیش"}, - }, - "day": { - "future": {"other": "{0} روز بعد", "one": "{0} روز بعد"}, - "past": {"other": "{0} روز پیش", "one": "{0} روز پیش"}, - }, - "hour": { - "future": {"other": "{0} ساعت بعد", "one": "{0} ساعت بعد"}, - "past": {"other": "{0} ساعت پیش", "one": "{0} ساعت پیش"}, - }, - "minute": { - "future": {"other": "{0} دقیقه بعد", "one": "{0} دقیقه بعد"}, - "past": {"other": "{0} دقیقه پیش", "one": "{0} دقیقه پیش"}, - }, - "second": { - "future": {"other": "{0} ثانیه بعد", "one": "{0} ثانیه بعد"}, - "past": {"other": "{0} ثانیه پیش", "one": "{0} ثانیه پیش"}, - }, - }, - "day_periods": { - "midnight": "نیمه\u200cشب", - "am": "قبل\u200cازظهر", - "noon": "ظهر", - "pm": "بعدازظهر", - "morning1": "صبح", - "afternoon1": "عصر", - "evening1": "عصر", - "night1": "شب", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/fo/__init__.py b/pendulum/locales/fo/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/fo/__init__.py +++ /dev/null diff --git a/pendulum/locales/fo/custom.py b/pendulum/locales/fo/custom.py deleted file mode 100644 index 456dd59..0000000 --- a/pendulum/locales/fo/custom.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -fo custom locale file. -""" - -translations = { - # Relative time - "after": "{0} aftaná", - "before": "{0} áðrenn", - # Ordinals - "ordinal": {"other": "."}, - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd D. MMMM, YYYY HH:mm", - "LLL": "D MMMM YYYY HH:mm", - "LL": "D MMMM YYYY", - "L": "DD/MM/YYYY", - }, -} diff --git a/pendulum/locales/fo/locale.py b/pendulum/locales/fo/locale.py deleted file mode 100644 index 10319ea..0000000 --- a/pendulum/locales/fo/locale.py +++ /dev/null @@ -1,132 +0,0 @@ -from .custom import translations as custom_translations - - -""" -fo locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" if (n == n and (n == 1)) else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "sun.", - 1: "mán.", - 2: "týs.", - 3: "mik.", - 4: "hós.", - 5: "frí.", - 6: "ley.", - }, - "narrow": {0: "S", 1: "M", 2: "T", 3: "M", 4: "H", 5: "F", 6: "L"}, - "short": { - 0: "su.", - 1: "má.", - 2: "tý.", - 3: "mi.", - 4: "hó.", - 5: "fr.", - 6: "le.", - }, - "wide": { - 0: "sunnudagur", - 1: "mánadagur", - 2: "týsdagur", - 3: "mikudagur", - 4: "hósdagur", - 5: "fríggjadagur", - 6: "leygardagur", - }, - }, - "months": { - "abbreviated": { - 1: "jan.", - 2: "feb.", - 3: "mar.", - 4: "apr.", - 5: "mai", - 6: "jun.", - 7: "jul.", - 8: "aug.", - 9: "sep.", - 10: "okt.", - 11: "nov.", - 12: "des.", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "januar", - 2: "februar", - 3: "mars", - 4: "apríl", - 5: "mai", - 6: "juni", - 7: "juli", - 8: "august", - 9: "september", - 10: "oktober", - 11: "november", - 12: "desember", - }, - }, - "units": { - "year": {"one": "{0} ár", "other": "{0} ár"}, - "month": {"one": "{0} mánaður", "other": "{0} mánaðir"}, - "week": {"one": "{0} vika", "other": "{0} vikur"}, - "day": {"one": "{0} dagur", "other": "{0} dagar"}, - "hour": {"one": "{0} tími", "other": "{0} tímar"}, - "minute": {"one": "{0} minuttur", "other": "{0} minuttir"}, - "second": {"one": "{0} sekund", "other": "{0} sekundir"}, - "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekundir"}, - }, - "relative": { - "year": { - "future": {"other": "um {0} ár", "one": "um {0} ár"}, - "past": {"other": "{0} ár síðan", "one": "{0} ár síðan"}, - }, - "month": { - "future": {"other": "um {0} mánaðir", "one": "um {0} mánað"}, - "past": {"other": "{0} mánaðir síðan", "one": "{0} mánað síðan"}, - }, - "week": { - "future": {"other": "um {0} vikur", "one": "um {0} viku"}, - "past": {"other": "{0} vikur síðan", "one": "{0} vika síðan"}, - }, - "day": { - "future": {"other": "um {0} dagar", "one": "um {0} dag"}, - "past": {"other": "{0} dagar síðan", "one": "{0} dagur síðan"}, - }, - "hour": { - "future": {"other": "um {0} tímar", "one": "um {0} tíma"}, - "past": {"other": "{0} tímar síðan", "one": "{0} tími síðan"}, - }, - "minute": { - "future": {"other": "um {0} minuttir", "one": "um {0} minutt"}, - "past": {"other": "{0} minuttir síðan", "one": "{0} minutt síðan"}, - }, - "second": { - "future": {"other": "um {0} sekund", "one": "um {0} sekund"}, - "past": {"other": "{0} sekund síðan", "one": "{0} sekund síðan"}, - }, - }, - "day_periods": {"am": "AM", "pm": "PM"}, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/fr/__init__.py b/pendulum/locales/fr/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/fr/__init__.py +++ /dev/null diff --git a/pendulum/locales/fr/custom.py b/pendulum/locales/fr/custom.py deleted file mode 100644 index 134f297..0000000 --- a/pendulum/locales/fr/custom.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -fr custom locale file. -""" - -translations = { - "units": {"few_second": "quelques secondes"}, - # Relative Time - "ago": "il y a {0}", - "from_now": "dans {0}", - "after": "{0} après", - "before": "{0} avant", - # Ordinals - "ordinal": {"one": "er", "other": "e"}, - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd D MMMM YYYY HH:mm", - "LLL": "D MMMM YYYY HH:mm", - "LL": "D MMMM YYYY", - "L": "DD/MM/YYYY", - }, -} diff --git a/pendulum/locales/fr/locale.py b/pendulum/locales/fr/locale.py deleted file mode 100644 index 8855d53..0000000 --- a/pendulum/locales/fr/locale.py +++ /dev/null @@ -1,133 +0,0 @@ -from .custom import translations as custom_translations - - -""" -fr locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" if (n == n and ((n == 0) or (n == 1))) else "other", - "ordinal": lambda n: "one" if (n == n and (n == 1)) else "other", - "translations": { - "days": { - "abbreviated": { - 0: "dim.", - 1: "lun.", - 2: "mar.", - 3: "mer.", - 4: "jeu.", - 5: "ven.", - 6: "sam.", - }, - "narrow": {0: "D", 1: "L", 2: "M", 3: "M", 4: "J", 5: "V", 6: "S"}, - "short": {0: "di", 1: "lu", 2: "ma", 3: "me", 4: "je", 5: "ve", 6: "sa"}, - "wide": { - 0: "dimanche", - 1: "lundi", - 2: "mardi", - 3: "mercredi", - 4: "jeudi", - 5: "vendredi", - 6: "samedi", - }, - }, - "months": { - "abbreviated": { - 1: "janv.", - 2: "févr.", - 3: "mars", - 4: "avr.", - 5: "mai", - 6: "juin", - 7: "juil.", - 8: "août", - 9: "sept.", - 10: "oct.", - 11: "nov.", - 12: "déc.", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "janvier", - 2: "février", - 3: "mars", - 4: "avril", - 5: "mai", - 6: "juin", - 7: "juillet", - 8: "août", - 9: "septembre", - 10: "octobre", - 11: "novembre", - 12: "décembre", - }, - }, - "units": { - "year": {"one": "{0} an", "other": "{0} ans"}, - "month": {"one": "{0} mois", "other": "{0} mois"}, - "week": {"one": "{0} semaine", "other": "{0} semaines"}, - "day": {"one": "{0} jour", "other": "{0} jours"}, - "hour": {"one": "{0} heure", "other": "{0} heures"}, - "minute": {"one": "{0} minute", "other": "{0} minutes"}, - "second": {"one": "{0} seconde", "other": "{0} secondes"}, - "microsecond": {"one": "{0} microseconde", "other": "{0} microsecondes"}, - }, - "relative": { - "year": { - "future": {"other": "dans {0} ans", "one": "dans {0} an"}, - "past": {"other": "il y a {0} ans", "one": "il y a {0} an"}, - }, - "month": { - "future": {"other": "dans {0} mois", "one": "dans {0} mois"}, - "past": {"other": "il y a {0} mois", "one": "il y a {0} mois"}, - }, - "week": { - "future": {"other": "dans {0} semaines", "one": "dans {0} semaine"}, - "past": {"other": "il y a {0} semaines", "one": "il y a {0} semaine"}, - }, - "day": { - "future": {"other": "dans {0} jours", "one": "dans {0} jour"}, - "past": {"other": "il y a {0} jours", "one": "il y a {0} jour"}, - }, - "hour": { - "future": {"other": "dans {0} heures", "one": "dans {0} heure"}, - "past": {"other": "il y a {0} heures", "one": "il y a {0} heure"}, - }, - "minute": { - "future": {"other": "dans {0} minutes", "one": "dans {0} minute"}, - "past": {"other": "il y a {0} minutes", "one": "il y a {0} minute"}, - }, - "second": { - "future": {"other": "dans {0} secondes", "one": "dans {0} seconde"}, - "past": {"other": "il y a {0} secondes", "one": "il y a {0} seconde"}, - }, - }, - "day_periods": { - "midnight": "minuit", - "am": "AM", - "noon": "midi", - "pm": "PM", - "morning1": "du matin", - "afternoon1": "de l’après-midi", - "evening1": "du soir", - "night1": "de nuit", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/he/__init__.py b/pendulum/locales/he/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/he/__init__.py +++ /dev/null diff --git a/pendulum/locales/he/custom.py b/pendulum/locales/he/custom.py deleted file mode 100644 index 51f8476..0000000 --- a/pendulum/locales/he/custom.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -he custom locale file. -""" - -translations = { - "units": {"few_second": "כמה שניות"}, - # Relative time - "ago": "לפני {0}", - "from_now": "תוך {0}", - "after": "בעוד {0}", - "before": "{0} קודם", - # Ordinals - "ordinal": {"other": "º"}, - # Date formats - "date_formats": { - "LTS": "H:mm:ss", - "LT": "H:mm", - "LLLL": "dddd, D [ב] MMMM [ב] YYYY H:mm", - "LLL": "D [ב] MMMM [ב] YYYY H:mm", - "LL": "D [ב] MMMM [ב] YYYY", - "L": "DD/MM/YYYY", - }, -} diff --git a/pendulum/locales/he/locale.py b/pendulum/locales/he/locale.py deleted file mode 100644 index 457c101..0000000 --- a/pendulum/locales/he/locale.py +++ /dev/null @@ -1,269 +0,0 @@ -from .custom import translations as custom_translations - - -""" -he locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "many" - if ( - ((0 == 0 and (0 == 0)) and (not (n == n and (n >= 0 and n <= 10)))) - and ((n % 10) == (n % 10) and ((n % 10) == 0)) - ) - else "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "two" - if ((n == n and (n == 2)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "יום א׳", - 1: "יום ב׳", - 2: "יום ג׳", - 3: "יום ד׳", - 4: "יום ה׳", - 5: "יום ו׳", - 6: "שבת", - }, - "narrow": { - 0: "א׳", - 1: "ב׳", - 2: "ג׳", - 3: "ד׳", - 4: "ה׳", - 5: "ו׳", - 6: "ש׳", - }, - "short": { - 0: "א׳", - 1: "ב׳", - 2: "ג׳", - 3: "ד׳", - 4: "ה׳", - 5: "ו׳", - 6: "ש׳", - }, - "wide": { - 0: "יום ראשון", - 1: "יום שני", - 2: "יום שלישי", - 3: "יום רביעי", - 4: "יום חמישי", - 5: "יום שישי", - 6: "יום שבת", - }, - }, - "months": { - "abbreviated": { - 1: "ינו׳", - 2: "פבר׳", - 3: "מרץ", - 4: "אפר׳", - 5: "מאי", - 6: "יוני", - 7: "יולי", - 8: "אוג׳", - 9: "ספט׳", - 10: "אוק׳", - 11: "נוב׳", - 12: "דצמ׳", - }, - "narrow": { - 1: "1", - 2: "2", - 3: "3", - 4: "4", - 5: "5", - 6: "6", - 7: "7", - 8: "8", - 9: "9", - 10: "10", - 11: "11", - 12: "12", - }, - "wide": { - 1: "ינואר", - 2: "פברואר", - 3: "מרץ", - 4: "אפריל", - 5: "מאי", - 6: "יוני", - 7: "יולי", - 8: "אוגוסט", - 9: "ספטמבר", - 10: "אוקטובר", - 11: "נובמבר", - 12: "דצמבר", - }, - }, - "units": { - "year": { - "one": "שנה", - "two": "שנתיים", - "many": "{0} שנים", - "other": "{0} שנים", - }, - "month": { - "one": "חודש", - "two": "חודשיים", - "many": "{0} חודשים", - "other": "{0} חודשים", - }, - "week": { - "one": "שבוע", - "two": "שבועיים", - "many": "{0} שבועות", - "other": "{0} שבועות", - }, - "day": { - "one": "יום {0}", - "two": "יומיים", - "many": "{0} יום", - "other": "{0} ימים", - }, - "hour": { - "one": "שעה", - "two": "שעתיים", - "many": "{0} שעות", - "other": "{0} שעות", - }, - "minute": { - "one": "דקה", - "two": "שתי דקות", - "many": "{0} דקות", - "other": "{0} דקות", - }, - "second": { - "one": "שניה", - "two": "שתי שניות", - "many": "\u200f{0} שניות", - "other": "{0} שניות", - }, - "microsecond": { - "one": "{0} מיליונית שנייה", - "two": "{0} מיליוניות שנייה", - "many": "{0} מיליוניות שנייה", - "other": "{0} מיליוניות שנייה", - }, - }, - "relative": { - "year": { - "future": { - "other": "בעוד {0} שנים", - "one": "בעוד שנה", - "two": "בעוד שנתיים", - "many": "בעוד {0} שנה", - }, - "past": { - "other": "לפני {0} שנים", - "one": "לפני שנה", - "two": "לפני שנתיים", - "many": "לפני {0} שנה", - }, - }, - "month": { - "future": { - "other": "בעוד {0} חודשים", - "one": "בעוד חודש", - "two": "בעוד חודשיים", - "many": "בעוד {0} חודשים", - }, - "past": { - "other": "לפני {0} חודשים", - "one": "לפני חודש", - "two": "לפני חודשיים", - "many": "לפני {0} חודשים", - }, - }, - "week": { - "future": { - "other": "בעוד {0} שבועות", - "one": "בעוד שבוע", - "two": "בעוד שבועיים", - "many": "בעוד {0} שבועות", - }, - "past": { - "other": "לפני {0} שבועות", - "one": "לפני שבוע", - "two": "לפני שבועיים", - "many": "לפני {0} שבועות", - }, - }, - "day": { - "future": { - "other": "בעוד {0} ימים", - "one": "בעוד יום {0}", - "two": "בעוד יומיים", - "many": "בעוד {0} ימים", - }, - "past": { - "other": "לפני {0} ימים", - "one": "לפני יום {0}", - "two": "לפני יומיים", - "many": "לפני {0} ימים", - }, - }, - "hour": { - "future": { - "other": "בעוד {0} שעות", - "one": "בעוד שעה", - "two": "בעוד שעתיים", - "many": "בעוד {0} שעות", - }, - "past": { - "other": "לפני {0} שעות", - "one": "לפני שעה", - "two": "לפני שעתיים", - "many": "לפני {0} שעות", - }, - }, - "minute": { - "future": { - "other": "בעוד {0} דקות", - "one": "בעוד דקה", - "two": "בעוד שתי דקות", - "many": "בעוד {0} דקות", - }, - "past": { - "other": "לפני {0} דקות", - "one": "לפני דקה", - "two": "לפני שתי דקות", - "many": "לפני {0} דקות", - }, - }, - "second": { - "future": { - "other": "בעוד {0} שניות", - "one": "בעוד שנייה", - "two": "בעוד שתי שניות", - "many": "בעוד {0} שניות", - }, - "past": { - "other": "לפני {0} שניות", - "one": "לפני שנייה", - "two": "לפני שתי שניות", - "many": "לפני {0} שניות", - }, - }, - }, - "day_periods": { - "midnight": "חצות", - "am": "לפנה״צ", - "pm": "אחה״צ", - "morning1": "בבוקר", - "afternoon1": "בצהריים", - "afternoon2": "אחר הצהריים", - "evening1": "בערב", - "night1": "בלילה", - "night2": "לפנות בוקר", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/id/__init__.py b/pendulum/locales/id/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/id/__init__.py +++ /dev/null diff --git a/pendulum/locales/id/custom.py b/pendulum/locales/id/custom.py deleted file mode 100644 index 3ba2035..0000000 --- a/pendulum/locales/id/custom.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -id custom locale file. -""" - -translations = { - "units": {"few_second": "beberapa detik"}, - "ago": "{} yang lalu", - "from_now": "dalam {}", - "after": "{0} kemudian", - "before": "{0} yang lalu", - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd [d.] D. MMMM YYYY HH:mm", - "LLL": "D. MMMM YYYY HH:mm", - "LL": "D. MMMM YYYY", - "L": "DD/MM/YYYY", - }, -} diff --git a/pendulum/locales/id/locale.py b/pendulum/locales/id/locale.py deleted file mode 100644 index bc994ce..0000000 --- a/pendulum/locales/id/locale.py +++ /dev/null @@ -1,141 +0,0 @@ -from .custom import translations as custom_translations - - -""" -id locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "Min", - 1: "Sen", - 2: "Sel", - 3: "Rab", - 4: "Kam", - 5: "Jum", - 6: "Sab", - }, - "narrow": {0: "M", 1: "S", 2: "S", 3: "R", 4: "K", 5: "J", 6: "S"}, - "short": { - 0: "Min", - 1: "Sen", - 2: "Sel", - 3: "Rab", - 4: "Kam", - 5: "Jum", - 6: "Sab", - }, - "wide": { - 0: "Minggu", - 1: "Senin", - 2: "Selasa", - 3: "Rabu", - 4: "Kamis", - 5: "Jumat", - 6: "Sabtu", - }, - }, - "months": { - "abbreviated": { - 1: "Jan", - 2: "Feb", - 3: "Mar", - 4: "Apr", - 5: "Mei", - 6: "Jun", - 7: "Jul", - 8: "Agt", - 9: "Sep", - 10: "Okt", - 11: "Nov", - 12: "Des", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "Januari", - 2: "Februari", - 3: "Maret", - 4: "April", - 5: "Mei", - 6: "Juni", - 7: "Juli", - 8: "Agustus", - 9: "September", - 10: "Oktober", - 11: "November", - 12: "Desember", - }, - }, - "units": { - "year": {"other": "{0} tahun"}, - "month": {"other": "{0} bulan"}, - "week": {"other": "{0} minggu"}, - "day": {"other": "{0} hari"}, - "hour": {"other": "{0} jam"}, - "minute": {"other": "{0} menit"}, - "second": {"other": "{0} detik"}, - "microsecond": {"other": "{0} mikrodetik"}, - }, - "relative": { - "year": { - "future": {"other": "dalam {0} tahun"}, - "past": {"other": "{0} tahun yang lalu"}, - }, - "month": { - "future": {"other": "dalam {0} bulan"}, - "past": {"other": "{0} bulan yang lalu"}, - }, - "week": { - "future": {"other": "dalam {0} minggu"}, - "past": {"other": "{0} minggu yang lalu"}, - }, - "day": { - "future": {"other": "dalam {0} hari"}, - "past": {"other": "{0} hari yang lalu"}, - }, - "hour": { - "future": {"other": "dalam {0} jam"}, - "past": {"other": "{0} jam yang lalu"}, - }, - "minute": { - "future": {"other": "dalam {0} menit"}, - "past": {"other": "{0} menit yang lalu"}, - }, - "second": { - "future": {"other": "dalam {0} detik"}, - "past": {"other": "{0} detik yang lalu"}, - }, - }, - "day_periods": { - "midnight": "tengah malam", - "am": "AM", - "noon": "tengah hari", - "pm": "PM", - "morning1": "pagi", - "afternoon1": "siang", - "evening1": "sore", - "night1": "malam", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/it/__init__.py b/pendulum/locales/it/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/it/__init__.py +++ /dev/null diff --git a/pendulum/locales/it/custom.py b/pendulum/locales/it/custom.py deleted file mode 100644 index e5cf1cc..0000000 --- a/pendulum/locales/it/custom.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -it custom locale file. -""" - - -translations = { - "units": {"few_second": "alcuni secondi"}, - # Relative Time - "ago": "{0} fa", - "from_now": "in {0}", - "after": "{0} dopo", - "before": "{0} prima", - # Ordinals - "ordinal": {"other": "°"}, - # Date formats - "date_formats": { - "LTS": "H:mm:ss", - "LT": "H:mm", - "L": "DD/MM/YYYY", - "LL": "D MMMM YYYY", - "LLL": "D MMMM YYYY [alle] H:mm", - "LLLL": "dddd, D MMMM YYYY [alle] H:mm", - }, -} diff --git a/pendulum/locales/it/locale.py b/pendulum/locales/it/locale.py deleted file mode 100644 index bb3fdcf..0000000 --- a/pendulum/locales/it/locale.py +++ /dev/null @@ -1,145 +0,0 @@ -from .custom import translations as custom_translations - - -""" -it locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "many" - if (n == n and ((n == 11) or (n == 8) or (n == 80) or (n == 800))) - else "other", - "translations": { - "days": { - "abbreviated": { - 0: "dom", - 1: "lun", - 2: "mar", - 3: "mer", - 4: "gio", - 5: "ven", - 6: "sab", - }, - "narrow": {0: "D", 1: "L", 2: "M", 3: "M", 4: "G", 5: "V", 6: "S"}, - "short": { - 0: "dom", - 1: "lun", - 2: "mar", - 3: "mer", - 4: "gio", - 5: "ven", - 6: "sab", - }, - "wide": { - 0: "domenica", - 1: "lunedì", - 2: "martedì", - 3: "mercoledì", - 4: "giovedì", - 5: "venerdì", - 6: "sabato", - }, - }, - "months": { - "abbreviated": { - 1: "gen", - 2: "feb", - 3: "mar", - 4: "apr", - 5: "mag", - 6: "giu", - 7: "lug", - 8: "ago", - 9: "set", - 10: "ott", - 11: "nov", - 12: "dic", - }, - "narrow": { - 1: "G", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "G", - 7: "L", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "gennaio", - 2: "febbraio", - 3: "marzo", - 4: "aprile", - 5: "maggio", - 6: "giugno", - 7: "luglio", - 8: "agosto", - 9: "settembre", - 10: "ottobre", - 11: "novembre", - 12: "dicembre", - }, - }, - "units": { - "year": {"one": "{0} anno", "other": "{0} anni"}, - "month": {"one": "{0} mese", "other": "{0} mesi"}, - "week": {"one": "{0} settimana", "other": "{0} settimane"}, - "day": {"one": "{0} giorno", "other": "{0} giorni"}, - "hour": {"one": "{0} ora", "other": "{0} ore"}, - "minute": {"one": "{0} minuto", "other": "{0} minuti"}, - "second": {"one": "{0} secondo", "other": "{0} secondi"}, - "microsecond": {"one": "{0} microsecondo", "other": "{0} microsecondi"}, - }, - "relative": { - "year": { - "future": {"other": "tra {0} anni", "one": "tra {0} anno"}, - "past": {"other": "{0} anni fa", "one": "{0} anno fa"}, - }, - "month": { - "future": {"other": "tra {0} mesi", "one": "tra {0} mese"}, - "past": {"other": "{0} mesi fa", "one": "{0} mese fa"}, - }, - "week": { - "future": {"other": "tra {0} settimane", "one": "tra {0} settimana"}, - "past": {"other": "{0} settimane fa", "one": "{0} settimana fa"}, - }, - "day": { - "future": {"other": "tra {0} giorni", "one": "tra {0} giorno"}, - "past": {"other": "{0} giorni fa", "one": "{0} giorno fa"}, - }, - "hour": { - "future": {"other": "tra {0} ore", "one": "tra {0} ora"}, - "past": {"other": "{0} ore fa", "one": "{0} ora fa"}, - }, - "minute": { - "future": {"other": "tra {0} minuti", "one": "tra {0} minuto"}, - "past": {"other": "{0} minuti fa", "one": "{0} minuto fa"}, - }, - "second": { - "future": {"other": "tra {0} secondi", "one": "tra {0} secondo"}, - "past": {"other": "{0} secondi fa", "one": "{0} secondo fa"}, - }, - }, - "day_periods": { - "midnight": "mezzanotte", - "am": "AM", - "noon": "mezzogiorno", - "pm": "PM", - "morning1": "di mattina", - "afternoon1": "del pomeriggio", - "evening1": "di sera", - "night1": "di notte", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/ja/__init__.py b/pendulum/locales/ja/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/ja/__init__.py +++ /dev/null diff --git a/pendulum/locales/ja/custom.py b/pendulum/locales/ja/custom.py deleted file mode 100644 index c076250..0000000 --- a/pendulum/locales/ja/custom.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -ja custom locale file. -""" - -translations = { - "units": {"few_second": "数秒"}, - # Relative time - "ago": "{} 前に", - "from_now": "今から {}", - "after": "{0} 後", - "before": "{0} 前", - # Date formats - "date_formats": { - "LTS": "h:mm:ss A", - "LT": "h:mm A", - "L": "MM/DD/YYYY", - "LL": "MMMM D, YYYY", - "LLL": "MMMM D, YYYY h:mm A", - "LLLL": "dddd, MMMM D, YYYY h:mm A", - }, -} diff --git a/pendulum/locales/ja/locale.py b/pendulum/locales/ja/locale.py deleted file mode 100644 index 574d2ec..0000000 --- a/pendulum/locales/ja/locale.py +++ /dev/null @@ -1,194 +0,0 @@ -from .custom import translations as custom_translations - - -""" -ja locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "日", - 1: "月", - 2: "火", - 3: "水", - 4: "木", - 5: "金", - 6: "土", - }, - "narrow": { - 0: "日", - 1: "月", - 2: "火", - 3: "水", - 4: "木", - 5: "金", - 6: "土", - }, - "short": { - 0: "日", - 1: "月", - 2: "火", - 3: "水", - 4: "木", - 5: "金", - 6: "土", - }, - "wide": { - 0: "日曜日", - 1: "月曜日", - 2: "火曜日", - 3: "水曜日", - 4: "木曜日", - 5: "金曜日", - 6: "土曜日", - }, - }, - "months": { - "abbreviated": { - 1: "1月", - 2: "2月", - 3: "3月", - 4: "4月", - 5: "5月", - 6: "6月", - 7: "7月", - 8: "8月", - 9: "9月", - 10: "10月", - 11: "11月", - 12: "12月", - }, - "narrow": { - 1: "1", - 2: "2", - 3: "3", - 4: "4", - 5: "5", - 6: "6", - 7: "7", - 8: "8", - 9: "9", - 10: "10", - 11: "11", - 12: "12", - }, - "wide": { - 1: "1月", - 2: "2月", - 3: "3月", - 4: "4月", - 5: "5月", - 6: "6月", - 7: "7月", - 8: "8月", - 9: "9月", - 10: "10月", - 11: "11月", - 12: "12月", - }, - }, - "units": { - "year": { - "other": "{0} 年", - }, - "month": { - "other": "{0} か月", - }, - "week": { - "other": "{0} 週間", - }, - "day": { - "other": "{0} 日", - }, - "hour": { - "other": "{0} 時間", - }, - "minute": { - "other": "{0} 分", - }, - "second": { - "other": "{0} 秒", - }, - "microsecond": { - "other": "{0} マイクロ秒", - }, - }, - "relative": { - "year": { - "future": { - "other": "{0} 年後", - }, - "past": { - "other": "{0} 年前", - }, - }, - "month": { - "future": { - "other": "{0} か月後", - }, - "past": { - "other": "{0} か月前", - }, - }, - "week": { - "future": { - "other": "{0} 週間後", - }, - "past": { - "other": "{0} 週間前", - }, - }, - "day": { - "future": { - "other": "{0} 日後", - }, - "past": { - "other": "{0} 日前", - }, - }, - "hour": { - "future": { - "other": "{0} 時間後", - }, - "past": { - "other": "{0} 時間前", - }, - }, - "minute": { - "future": { - "other": "{0} 分後", - }, - "past": { - "other": "{0} 分前", - }, - }, - "second": { - "future": { - "other": "{0} 秒後", - }, - "past": { - "other": "{0} 秒前", - }, - }, - }, - "day_periods": { - "midnight": "真夜中", - "am": "午前", - "noon": "正午", - "pm": "午後", - "morning1": "朝", - "afternoon1": "昼", - "evening1": "夕方", - "night1": "夜", - "night2": "夜中", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/ko/__init__.py b/pendulum/locales/ko/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/ko/__init__.py +++ /dev/null diff --git a/pendulum/locales/ko/custom.py b/pendulum/locales/ko/custom.py deleted file mode 100644 index 2c0e50c..0000000 --- a/pendulum/locales/ko/custom.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -ko custom locale file. -""" - -translations = { - # Relative time - "after": "{0} 뒤", - "before": "{0} 앞", - # Date formats - "date_formats": { - "LTS": "A h시 m분 s초", - "LT": "A h시 m분", - "LLLL": "YYYY년 MMMM D일 dddd A h시 m분", - "LLL": "YYYY년 MMMM D일 A h시 m분", - "LL": "YYYY년 MMMM D일", - "L": "YYYY.MM.DD", - }, -} diff --git a/pendulum/locales/ko/locale.py b/pendulum/locales/ko/locale.py deleted file mode 100644 index 0f5a346..0000000 --- a/pendulum/locales/ko/locale.py +++ /dev/null @@ -1,105 +0,0 @@ -from .custom import translations as custom_translations - - -""" -ko locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": {0: "일", 1: "월", 2: "화", 3: "수", 4: "목", 5: "금", 6: "토"}, - "narrow": {0: "일", 1: "월", 2: "화", 3: "수", 4: "목", 5: "금", 6: "토"}, - "short": {0: "일", 1: "월", 2: "화", 3: "수", 4: "목", 5: "금", 6: "토"}, - "wide": { - 0: "일요일", - 1: "월요일", - 2: "화요일", - 3: "수요일", - 4: "목요일", - 5: "금요일", - 6: "토요일", - }, - }, - "months": { - "abbreviated": { - 1: "1월", - 2: "2월", - 3: "3월", - 4: "4월", - 5: "5월", - 6: "6월", - 7: "7월", - 8: "8월", - 9: "9월", - 10: "10월", - 11: "11월", - 12: "12월", - }, - "narrow": { - 1: "1월", - 2: "2월", - 3: "3월", - 4: "4월", - 5: "5월", - 6: "6월", - 7: "7월", - 8: "8월", - 9: "9월", - 10: "10월", - 11: "11월", - 12: "12월", - }, - "wide": { - 1: "1월", - 2: "2월", - 3: "3월", - 4: "4월", - 5: "5월", - 6: "6월", - 7: "7월", - 8: "8월", - 9: "9월", - 10: "10월", - 11: "11월", - 12: "12월", - }, - }, - "units": { - "year": {"other": "{0}년"}, - "month": {"other": "{0}개월"}, - "week": {"other": "{0}주"}, - "day": {"other": "{0}일"}, - "hour": {"other": "{0}시간"}, - "minute": {"other": "{0}분"}, - "second": {"other": "{0}초"}, - "microsecond": {"other": "{0}마이크로초"}, - }, - "relative": { - "year": {"future": {"other": "{0}년 후"}, "past": {"other": "{0}년 전"}}, - "month": {"future": {"other": "{0}개월 후"}, "past": {"other": "{0}개월 전"}}, - "week": {"future": {"other": "{0}주 후"}, "past": {"other": "{0}주 전"}}, - "day": {"future": {"other": "{0}일 후"}, "past": {"other": "{0}일 전"}}, - "hour": {"future": {"other": "{0}시간 후"}, "past": {"other": "{0}시간 전"}}, - "minute": {"future": {"other": "{0}분 후"}, "past": {"other": "{0}분 전"}}, - "second": {"future": {"other": "{0}초 후"}, "past": {"other": "{0}초 전"}}, - }, - "day_periods": { - "midnight": "자정", - "am": "오전", - "noon": "정오", - "pm": "오후", - "morning1": "새벽", - "morning2": "오전", - "afternoon1": "오후", - "evening1": "저녁", - "night1": "밤", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/locale.py b/pendulum/locales/locale.py deleted file mode 100644 index 637509a..0000000 --- a/pendulum/locales/locale.py +++ /dev/null @@ -1,102 +0,0 @@ -from __future__ import annotations - -from importlib import import_module -from pathlib import Path - -import re -import sys -from typing import Any, cast -from typing import Dict - -if sys.version_info >= (3, 9): - from importlib import resources -else: - import importlib_resources as resources - - -class Locale: - """ - Represent a specific locale. - """ - - _cache: dict[str, Locale] = {} - - def __init__(self, locale: str, data: Any) -> None: - self._locale: str = locale - self._data: Any = data - self._key_cache: dict[str, str] = {} - - @classmethod - def load(cls, locale: str | Locale) -> Locale: - if isinstance(locale, Locale): - return locale - - locale = cls.normalize_locale(locale) - if locale in cls._cache: - return cls._cache[locale] - - # Checking locale existence - actual_locale = locale - locale_path = cast(Path, resources.files(__package__).joinpath(actual_locale)) - while not locale_path.exists(): - if actual_locale == locale: - raise ValueError(f"Locale [{locale}] does not exist.") - - actual_locale = actual_locale.split("_")[0] - - m = import_module(f"pendulum.locales.{actual_locale}.locale") - - cls._cache[locale] = cls(locale, m.locale) - - return cls._cache[locale] - - @classmethod - def normalize_locale(cls, locale: str) -> str: - m = re.match("([a-z]{2})[-_]([a-z]{2})", locale, re.I) - if m: - return f"{m.group(1).lower()}_{m.group(2).lower()}" - else: - return locale.lower() - - def get(self, key: str, default: Any | None = None) -> Any: - if key in self._key_cache: - return self._key_cache[key] - - parts = key.split(".") - try: - result = self._data[parts[0]] - for part in parts[1:]: - result = result[part] - except KeyError: - result = default - - self._key_cache[key] = result - - return self._key_cache[key] - - def translation(self, key: str) -> Any: - return self.get(f"translations.{key}") - - def plural(self, number: int) -> str: - return cast(str, self._data["plural"](number)) - - def ordinal(self, number: int) -> str: - return cast(str, self._data["ordinal"](number)) - - def ordinalize(self, number: int) -> str: - ordinal = self.get(f"custom.ordinal.{self.ordinal(number)}") - - if not ordinal: - return f"{number}" - - return f"{number}{ordinal}" - - def match_translation(self, key: str, value: Any) -> dict[str, str] | None: - translations = self.translation(key) - if value not in translations.values(): - return None - - return cast(Dict[str, str], {v: k for k, v in translations.items()}[value]) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}('{self._locale}')" diff --git a/pendulum/locales/lt/__init__.py b/pendulum/locales/lt/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/lt/__init__.py +++ /dev/null diff --git a/pendulum/locales/lt/custom.py b/pendulum/locales/lt/custom.py deleted file mode 100644 index 6480c31..0000000 --- a/pendulum/locales/lt/custom.py +++ /dev/null @@ -1,118 +0,0 @@ -""" -lt custom locale file. -""" - -translations = { - # Relative time - "units_relative": { - "year": { - "future": { - "other": "{0} metų", - "one": "{0} metų", - "few": "{0} metų", - "many": "{0} metų", - }, - "past": { - "other": "{0} metų", - "one": "{0} metus", - "few": "{0} metus", - "many": "{0} metų", - }, - }, - "month": { - "future": { - "other": "{0} mėnesių", - "one": "{0} mėnesio", - "few": "{0} mėnesių", - "many": "{0} mėnesio", - }, - "past": { - "other": "{0} mėnesių", - "one": "{0} mėnesį", - "few": "{0} mėnesius", - "many": "{0} mėnesio", - }, - }, - "week": { - "future": { - "other": "{0} savaičių", - "one": "{0} savaitės", - "few": "{0} savaičių", - "many": "{0} savaitės", - }, - "past": { - "other": "{0} savaičių", - "one": "{0} savaitę", - "few": "{0} savaites", - "many": "{0} savaitės", - }, - }, - "day": { - "future": { - "other": "{0} dienų", - "one": "{0} dienos", - "few": "{0} dienų", - "many": "{0} dienos", - }, - "past": { - "other": "{0} dienų", - "one": "{0} dieną", - "few": "{0} dienas", - "many": "{0} dienos", - }, - }, - "hour": { - "future": { - "other": "{0} valandų", - "one": "{0} valandos", - "few": "{0} valandų", - "many": "{0} valandos", - }, - "past": { - "other": "{0} valandų", - "one": "{0} valandą", - "few": "{0} valandas", - "many": "{0} valandos", - }, - }, - "minute": { - "future": { - "other": "{0} minučių", - "one": "{0} minutės", - "few": "{0} minučių", - "many": "{0} minutės", - }, - "past": { - "other": "{0} minučių", - "one": "{0} minutę", - "few": "{0} minutes", - "many": "{0} minutės", - }, - }, - "second": { - "future": { - "other": "{0} sekundžių", - "one": "{0} sekundės", - "few": "{0} sekundžių", - "many": "{0} sekundės", - }, - "past": { - "other": "{0} sekundžių", - "one": "{0} sekundę", - "few": "{0} sekundes", - "many": "{0} sekundės", - }, - }, - }, - "after": "po {0}", - "before": "{0} nuo dabar", - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]", - "LLL": "YYYY [m.] MMMM D [d.], HH:mm [val.]", - "LL": "YYYY [m.] MMMM D [d.]", - "L": "YYYY-MM-DD", - }, -} diff --git a/pendulum/locales/lt/locale.py b/pendulum/locales/lt/locale.py deleted file mode 100644 index fb017ef..0000000 --- a/pendulum/locales/lt/locale.py +++ /dev/null @@ -1,255 +0,0 @@ -from .custom import translations as custom_translations - - -""" -lt locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "few" - if ( - ((n % 10) == (n % 10) and ((n % 10) >= 2 and (n % 10) <= 9)) - and (not ((n % 100) == (n % 100) and ((n % 100) >= 11 and (n % 100) <= 19))) - ) - else "many" - if (not (0 == 0 and (0 == 0))) - else "one" - if ( - ((n % 10) == (n % 10) and ((n % 10) == 1)) - and (not ((n % 100) == (n % 100) and ((n % 100) >= 11 and (n % 100) <= 19))) - ) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "sk", - 1: "pr", - 2: "an", - 3: "tr", - 4: "kt", - 5: "pn", - 6: "št", - }, - "narrow": {0: "S", 1: "P", 2: "A", 3: "T", 4: "K", 5: "P", 6: "Š"}, - "short": {0: "Sk", 1: "Pr", 2: "An", 3: "Tr", 4: "Kt", 5: "Pn", 6: "Št"}, - "wide": { - 0: "sekmadienis", - 1: "pirmadienis", - 2: "antradienis", - 3: "trečiadienis", - 4: "ketvirtadienis", - 5: "penktadienis", - 6: "šeštadienis", - }, - }, - "months": { - "abbreviated": { - 1: "saus.", - 2: "vas.", - 3: "kov.", - 4: "bal.", - 5: "geg.", - 6: "birž.", - 7: "liep.", - 8: "rugp.", - 9: "rugs.", - 10: "spal.", - 11: "lapkr.", - 12: "gruod.", - }, - "narrow": { - 1: "S", - 2: "V", - 3: "K", - 4: "B", - 5: "G", - 6: "B", - 7: "L", - 8: "R", - 9: "R", - 10: "S", - 11: "L", - 12: "G", - }, - "wide": { - 1: "sausio", - 2: "vasario", - 3: "kovo", - 4: "balandžio", - 5: "gegužės", - 6: "birželio", - 7: "liepos", - 8: "rugpjūčio", - 9: "rugsėjo", - 10: "spalio", - 11: "lapkričio", - 12: "gruodžio", - }, - }, - "units": { - "year": { - "one": "{0} metai", - "few": "{0} metai", - "many": "{0} metų", - "other": "{0} metų", - }, - "month": { - "one": "{0} mėnuo", - "few": "{0} mėnesiai", - "many": "{0} mėnesio", - "other": "{0} mėnesių", - }, - "week": { - "one": "{0} savaitė", - "few": "{0} savaitės", - "many": "{0} savaitės", - "other": "{0} savaičių", - }, - "day": { - "one": "{0} diena", - "few": "{0} dienos", - "many": "{0} dienos", - "other": "{0} dienų", - }, - "hour": { - "one": "{0} valanda", - "few": "{0} valandos", - "many": "{0} valandos", - "other": "{0} valandų", - }, - "minute": { - "one": "{0} minutė", - "few": "{0} minutės", - "many": "{0} minutės", - "other": "{0} minučių", - }, - "second": { - "one": "{0} sekundė", - "few": "{0} sekundės", - "many": "{0} sekundės", - "other": "{0} sekundžių", - }, - "microsecond": { - "one": "{0} mikrosekundė", - "few": "{0} mikrosekundės", - "many": "{0} mikrosekundės", - "other": "{0} mikrosekundžių", - }, - }, - "relative": { - "year": { - "future": { - "other": "po {0} metų", - "one": "po {0} metų", - "few": "po {0} metų", - "many": "po {0} metų", - }, - "past": { - "other": "prieš {0} metų", - "one": "prieš {0} metus", - "few": "prieš {0} metus", - "many": "prieš {0} metų", - }, - }, - "month": { - "future": { - "other": "po {0} mėnesių", - "one": "po {0} mėnesio", - "few": "po {0} mėnesių", - "many": "po {0} mėnesio", - }, - "past": { - "other": "prieš {0} mėnesių", - "one": "prieš {0} mėnesį", - "few": "prieš {0} mėnesius", - "many": "prieš {0} mėnesio", - }, - }, - "week": { - "future": { - "other": "po {0} savaičių", - "one": "po {0} savaitės", - "few": "po {0} savaičių", - "many": "po {0} savaitės", - }, - "past": { - "other": "prieš {0} savaičių", - "one": "prieš {0} savaitę", - "few": "prieš {0} savaites", - "many": "prieš {0} savaitės", - }, - }, - "day": { - "future": { - "other": "po {0} dienų", - "one": "po {0} dienos", - "few": "po {0} dienų", - "many": "po {0} dienos", - }, - "past": { - "other": "prieš {0} dienų", - "one": "prieš {0} dieną", - "few": "prieš {0} dienas", - "many": "prieš {0} dienos", - }, - }, - "hour": { - "future": { - "other": "po {0} valandų", - "one": "po {0} valandos", - "few": "po {0} valandų", - "many": "po {0} valandos", - }, - "past": { - "other": "prieš {0} valandų", - "one": "prieš {0} valandą", - "few": "prieš {0} valandas", - "many": "prieš {0} valandos", - }, - }, - "minute": { - "future": { - "other": "po {0} minučių", - "one": "po {0} minutės", - "few": "po {0} minučių", - "many": "po {0} minutės", - }, - "past": { - "other": "prieš {0} minučių", - "one": "prieš {0} minutę", - "few": "prieš {0} minutes", - "many": "prieš {0} minutės", - }, - }, - "second": { - "future": { - "other": "po {0} sekundžių", - "one": "po {0} sekundės", - "few": "po {0} sekundžių", - "many": "po {0} sekundės", - }, - "past": { - "other": "prieš {0} sekundžių", - "one": "prieš {0} sekundę", - "few": "prieš {0} sekundes", - "many": "prieš {0} sekundės", - }, - }, - }, - "day_periods": { - "midnight": "vidurnaktis", - "am": "priešpiet", - "noon": "perpiet", - "pm": "popiet", - "morning1": "rytas", - "afternoon1": "popietė", - "evening1": "vakaras", - "night1": "naktis", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/nb/__init__.py b/pendulum/locales/nb/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/nb/__init__.py +++ /dev/null diff --git a/pendulum/locales/nb/custom.py b/pendulum/locales/nb/custom.py deleted file mode 100644 index 4c7cd6a..0000000 --- a/pendulum/locales/nb/custom.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -nn custom locale file. -""" - -translations = { - # Relative time - "after": "{0} etter", - "before": "{0} før", - # Ordinals - "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd Do MMMM YYYY HH:mm", - "LLL": "Do MMMM YYYY HH:mm", - "LL": "Do MMMM YYYY", - "L": "DD.MM.YYYY", - }, -} diff --git a/pendulum/locales/nb/locale.py b/pendulum/locales/nb/locale.py deleted file mode 100644 index c8297a8..0000000 --- a/pendulum/locales/nb/locale.py +++ /dev/null @@ -1,150 +0,0 @@ -from .custom import translations as custom_translations - - -""" -nb locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" if (n == n and (n == 1)) else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "søn.", - 1: "man.", - 2: "tir.", - 3: "ons.", - 4: "tor.", - 5: "fre.", - 6: "lør.", - }, - "narrow": {0: "S", 1: "M", 2: "T", 3: "O", 4: "T", 5: "F", 6: "L"}, - "short": { - 0: "sø.", - 1: "ma.", - 2: "ti.", - 3: "on.", - 4: "to.", - 5: "fr.", - 6: "lø.", - }, - "wide": { - 0: "søndag", - 1: "mandag", - 2: "tirsdag", - 3: "onsdag", - 4: "torsdag", - 5: "fredag", - 6: "lørdag", - }, - }, - "months": { - "abbreviated": { - 1: "jan.", - 2: "feb.", - 3: "mar.", - 4: "apr.", - 5: "mai", - 6: "jun.", - 7: "jul.", - 8: "aug.", - 9: "sep.", - 10: "okt.", - 11: "nov.", - 12: "des.", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "januar", - 2: "februar", - 3: "mars", - 4: "april", - 5: "mai", - 6: "juni", - 7: "juli", - 8: "august", - 9: "september", - 10: "oktober", - 11: "november", - 12: "desember", - }, - }, - "units": { - "year": {"one": "{0} år", "other": "{0} år"}, - "month": {"one": "{0} måned", "other": "{0} måneder"}, - "week": {"one": "{0} uke", "other": "{0} uker"}, - "day": {"one": "{0} dag", "other": "{0} dager"}, - "hour": {"one": "{0} time", "other": "{0} timer"}, - "minute": {"one": "{0} minutt", "other": "{0} minutter"}, - "second": {"one": "{0} sekund", "other": "{0} sekunder"}, - "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekunder"}, - }, - "relative": { - "year": { - "future": {"other": "om {0} år", "one": "om {0} år"}, - "past": {"other": "for {0} år siden", "one": "for {0} år siden"}, - }, - "month": { - "future": {"other": "om {0} måneder", "one": "om {0} måned"}, - "past": { - "other": "for {0} måneder siden", - "one": "for {0} måned siden", - }, - }, - "week": { - "future": {"other": "om {0} uker", "one": "om {0} uke"}, - "past": {"other": "for {0} uker siden", "one": "for {0} uke siden"}, - }, - "day": { - "future": {"other": "om {0} dager", "one": "om {0} dag"}, - "past": {"other": "for {0} dager siden", "one": "for {0} dag siden"}, - }, - "hour": { - "future": {"other": "om {0} timer", "one": "om {0} time"}, - "past": {"other": "for {0} timer siden", "one": "for {0} time siden"}, - }, - "minute": { - "future": {"other": "om {0} minutter", "one": "om {0} minutt"}, - "past": { - "other": "for {0} minutter siden", - "one": "for {0} minutt siden", - }, - }, - "second": { - "future": {"other": "om {0} sekunder", "one": "om {0} sekund"}, - "past": { - "other": "for {0} sekunder siden", - "one": "for {0} sekund siden", - }, - }, - }, - "day_periods": { - "midnight": "midnatt", - "am": "a.m.", - "pm": "p.m.", - "morning1": "morgenen", - "morning2": "formiddagen", - "afternoon1": "ettermiddagen", - "evening1": "kvelden", - "night1": "natten", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/nl/__init__.py b/pendulum/locales/nl/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/nl/__init__.py +++ /dev/null diff --git a/pendulum/locales/nl/custom.py b/pendulum/locales/nl/custom.py deleted file mode 100644 index 2ca5a85..0000000 --- a/pendulum/locales/nl/custom.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -nl custom locale file. -""" - -translations = { - "units": {"few_second": "enkele seconden"}, - # Relative time - "ago": "{} geleden", - "from_now": "over {}", - "after": "{0} later", - "before": "{0} eerder", - # Ordinals - "ordinal": {"other": "e"}, - # Date formats - "date_formats": { - "L": "DD-MM-YYYY", - "LL": "D MMMM YYYY", - "LLL": "D MMMM YYYY HH:mm", - "LLLL": "dddd D MMMM YYYY HH:mm", - "LT": "HH:mm", - "LTS": "HH:mm:ss", - }, -} diff --git a/pendulum/locales/nl/locale.py b/pendulum/locales/nl/locale.py deleted file mode 100644 index cb1570d..0000000 --- a/pendulum/locales/nl/locale.py +++ /dev/null @@ -1,134 +0,0 @@ -from .custom import translations as custom_translations - - -""" -nl locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "zo", - 1: "ma", - 2: "di", - 3: "wo", - 4: "do", - 5: "vr", - 6: "za", - }, - "narrow": {0: "Z", 1: "M", 2: "D", 3: "W", 4: "D", 5: "V", 6: "Z"}, - "short": {0: "zo", 1: "ma", 2: "di", 3: "wo", 4: "do", 5: "vr", 6: "za"}, - "wide": { - 0: "zondag", - 1: "maandag", - 2: "dinsdag", - 3: "woensdag", - 4: "donderdag", - 5: "vrijdag", - 6: "zaterdag", - }, - }, - "months": { - "abbreviated": { - 1: "jan.", - 2: "feb.", - 3: "mrt.", - 4: "apr.", - 5: "mei", - 6: "jun.", - 7: "jul.", - 8: "aug.", - 9: "sep.", - 10: "okt.", - 11: "nov.", - 12: "dec.", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "januari", - 2: "februari", - 3: "maart", - 4: "april", - 5: "mei", - 6: "juni", - 7: "juli", - 8: "augustus", - 9: "september", - 10: "oktober", - 11: "november", - 12: "december", - }, - }, - "units": { - "year": {"one": "{0} jaar", "other": "{0} jaar"}, - "month": {"one": "{0} maand", "other": "{0} maanden"}, - "week": {"one": "{0} week", "other": "{0} weken"}, - "day": {"one": "{0} dag", "other": "{0} dagen"}, - "hour": {"one": "{0} uur", "other": "{0} uur"}, - "minute": {"one": "{0} minuut", "other": "{0} minuten"}, - "second": {"one": "{0} seconde", "other": "{0} seconden"}, - "microsecond": {"one": "{0} microseconde", "other": "{0} microseconden"}, - }, - "relative": { - "year": { - "future": {"other": "over {0} jaar", "one": "over {0} jaar"}, - "past": {"other": "{0} jaar geleden", "one": "{0} jaar geleden"}, - }, - "month": { - "future": {"other": "over {0} maanden", "one": "over {0} maand"}, - "past": {"other": "{0} maanden geleden", "one": "{0} maand geleden"}, - }, - "week": { - "future": {"other": "over {0} weken", "one": "over {0} week"}, - "past": {"other": "{0} weken geleden", "one": "{0} week geleden"}, - }, - "day": { - "future": {"other": "over {0} dagen", "one": "over {0} dag"}, - "past": {"other": "{0} dagen geleden", "one": "{0} dag geleden"}, - }, - "hour": { - "future": {"other": "over {0} uur", "one": "over {0} uur"}, - "past": {"other": "{0} uur geleden", "one": "{0} uur geleden"}, - }, - "minute": { - "future": {"other": "over {0} minuten", "one": "over {0} minuut"}, - "past": {"other": "{0} minuten geleden", "one": "{0} minuut geleden"}, - }, - "second": { - "future": {"other": "over {0} seconden", "one": "over {0} seconde"}, - "past": {"other": "{0} seconden geleden", "one": "{0} seconde geleden"}, - }, - }, - "day_periods": { - "midnight": "middernacht", - "am": "a.m.", - "pm": "p.m.", - "morning1": "‘s ochtends", - "afternoon1": "‘s middags", - "evening1": "‘s avonds", - "night1": "‘s nachts", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/nn/__init__.py b/pendulum/locales/nn/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/nn/__init__.py +++ /dev/null diff --git a/pendulum/locales/nn/custom.py b/pendulum/locales/nn/custom.py deleted file mode 100644 index 4c7cd6a..0000000 --- a/pendulum/locales/nn/custom.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -nn custom locale file. -""" - -translations = { - # Relative time - "after": "{0} etter", - "before": "{0} før", - # Ordinals - "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd Do MMMM YYYY HH:mm", - "LLL": "Do MMMM YYYY HH:mm", - "LL": "Do MMMM YYYY", - "L": "DD.MM.YYYY", - }, -} diff --git a/pendulum/locales/nn/locale.py b/pendulum/locales/nn/locale.py deleted file mode 100644 index eb46e1d..0000000 --- a/pendulum/locales/nn/locale.py +++ /dev/null @@ -1,141 +0,0 @@ -from .custom import translations as custom_translations - - -""" -nn locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" if (n == n and (n == 1)) else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "søn.", - 1: "mån.", - 2: "tys.", - 3: "ons.", - 4: "tor.", - 5: "fre.", - 6: "lau.", - }, - "narrow": {0: "S", 1: "M", 2: "T", 3: "O", 4: "T", 5: "F", 6: "L"}, - "short": { - 0: "sø.", - 1: "må.", - 2: "ty.", - 3: "on.", - 4: "to.", - 5: "fr.", - 6: "la.", - }, - "wide": { - 0: "søndag", - 1: "måndag", - 2: "tysdag", - 3: "onsdag", - 4: "torsdag", - 5: "fredag", - 6: "laurdag", - }, - }, - "months": { - "abbreviated": { - 1: "jan.", - 2: "feb.", - 3: "mars", - 4: "apr.", - 5: "mai", - 6: "juni", - 7: "juli", - 8: "aug.", - 9: "sep.", - 10: "okt.", - 11: "nov.", - 12: "des.", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "januar", - 2: "februar", - 3: "mars", - 4: "april", - 5: "mai", - 6: "juni", - 7: "juli", - 8: "august", - 9: "september", - 10: "oktober", - 11: "november", - 12: "desember", - }, - }, - "units": { - "year": {"one": "{0} år", "other": "{0} år"}, - "month": {"one": "{0} månad", "other": "{0} månadar"}, - "week": {"one": "{0} veke", "other": "{0} veker"}, - "day": {"one": "{0} dag", "other": "{0} dagar"}, - "hour": {"one": "{0} time", "other": "{0} timar"}, - "minute": {"one": "{0} minutt", "other": "{0} minutt"}, - "second": {"one": "{0} sekund", "other": "{0} sekund"}, - "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekund"}, - }, - "relative": { - "year": { - "future": {"other": "om {0} år", "one": "om {0} år"}, - "past": {"other": "for {0} år sidan", "one": "for {0} år sidan"}, - }, - "month": { - "future": {"other": "om {0} månadar", "one": "om {0} månad"}, - "past": { - "other": "for {0} månadar sidan", - "one": "for {0} månad sidan", - }, - }, - "week": { - "future": {"other": "om {0} veker", "one": "om {0} veke"}, - "past": {"other": "for {0} veker sidan", "one": "for {0} veke sidan"}, - }, - "day": { - "future": {"other": "om {0} dagar", "one": "om {0} dag"}, - "past": {"other": "for {0} dagar sidan", "one": "for {0} dag sidan"}, - }, - "hour": { - "future": {"other": "om {0} timar", "one": "om {0} time"}, - "past": {"other": "for {0} timar sidan", "one": "for {0} time sidan"}, - }, - "minute": { - "future": {"other": "om {0} minutt", "one": "om {0} minutt"}, - "past": { - "other": "for {0} minutt sidan", - "one": "for {0} minutt sidan", - }, - }, - "second": { - "future": {"other": "om {0} sekund", "one": "om {0} sekund"}, - "past": { - "other": "for {0} sekund sidan", - "one": "for {0} sekund sidan", - }, - }, - }, - "day_periods": {"am": "formiddag", "pm": "ettermiddag"}, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/pl/__init__.py b/pendulum/locales/pl/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/pl/__init__.py +++ /dev/null diff --git a/pendulum/locales/pl/custom.py b/pendulum/locales/pl/custom.py deleted file mode 100644 index 9741b74..0000000 --- a/pendulum/locales/pl/custom.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -pl custom locale file. -""" - -translations = { - "units": {"few_second": "kilka sekund"}, - # Relative time - "ago": "{} temu", - "from_now": "za {}", - "after": "{0} po", - "before": "{0} przed", - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "L": "DD.MM.YYYY", - "LL": "D MMMM YYYY", - "LLL": "D MMMM YYYY HH:mm", - "LLLL": "dddd, D MMMM YYYY HH:mm", - }, -} diff --git a/pendulum/locales/pl/locale.py b/pendulum/locales/pl/locale.py deleted file mode 100644 index bf6af10..0000000 --- a/pendulum/locales/pl/locale.py +++ /dev/null @@ -1,279 +0,0 @@ -from .custom import translations as custom_translations - - -""" -pl locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "few" - if ( - ( - (0 == 0 and (0 == 0)) - and ((n % 10) == (n % 10) and ((n % 10) >= 2 and (n % 10) <= 4)) - ) - and (not ((n % 100) == (n % 100) and ((n % 100) >= 12 and (n % 100) <= 14))) - ) - else "many" - if ( - ( - ( - ((0 == 0 and (0 == 0)) and (not (n == n and (n == 1)))) - and ((n % 10) == (n % 10) and ((n % 10) >= 0 and (n % 10) <= 1)) - ) - or ( - (0 == 0 and (0 == 0)) - and ((n % 10) == (n % 10) and ((n % 10) >= 5 and (n % 10) <= 9)) - ) - ) - or ( - (0 == 0 and (0 == 0)) - and ((n % 100) == (n % 100) and ((n % 100) >= 12 and (n % 100) <= 14)) - ) - ) - else "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "niedz.", - 1: "pon.", - 2: "wt.", - 3: "śr.", - 4: "czw.", - 5: "pt.", - 6: "sob.", - }, - "narrow": {0: "n", 1: "p", 2: "w", 3: "ś", 4: "c", 5: "p", 6: "s"}, - "short": { - 0: "nie", - 1: "pon", - 2: "wto", - 3: "śro", - 4: "czw", - 5: "pią", - 6: "sob", - }, - "wide": { - 0: "niedziela", - 1: "poniedziałek", - 2: "wtorek", - 3: "środa", - 4: "czwartek", - 5: "piątek", - 6: "sobota", - }, - }, - "months": { - "abbreviated": { - 1: "sty", - 2: "lut", - 3: "mar", - 4: "kwi", - 5: "maj", - 6: "cze", - 7: "lip", - 8: "sie", - 9: "wrz", - 10: "paź", - 11: "lis", - 12: "gru", - }, - "narrow": { - 1: "s", - 2: "l", - 3: "m", - 4: "k", - 5: "m", - 6: "c", - 7: "l", - 8: "s", - 9: "w", - 10: "p", - 11: "l", - 12: "g", - }, - "wide": { - 1: "stycznia", - 2: "lutego", - 3: "marca", - 4: "kwietnia", - 5: "maja", - 6: "czerwca", - 7: "lipca", - 8: "sierpnia", - 9: "września", - 10: "października", - 11: "listopada", - 12: "grudnia", - }, - }, - "units": { - "year": { - "one": "{0} rok", - "few": "{0} lata", - "many": "{0} lat", - "other": "{0} roku", - }, - "month": { - "one": "{0} miesiąc", - "few": "{0} miesiące", - "many": "{0} miesięcy", - "other": "{0} miesiąca", - }, - "week": { - "one": "{0} tydzień", - "few": "{0} tygodnie", - "many": "{0} tygodni", - "other": "{0} tygodnia", - }, - "day": { - "one": "{0} dzień", - "few": "{0} dni", - "many": "{0} dni", - "other": "{0} dnia", - }, - "hour": { - "one": "{0} godzina", - "few": "{0} godziny", - "many": "{0} godzin", - "other": "{0} godziny", - }, - "minute": { - "one": "{0} minuta", - "few": "{0} minuty", - "many": "{0} minut", - "other": "{0} minuty", - }, - "second": { - "one": "{0} sekunda", - "few": "{0} sekundy", - "many": "{0} sekund", - "other": "{0} sekundy", - }, - "microsecond": { - "one": "{0} mikrosekunda", - "few": "{0} mikrosekundy", - "many": "{0} mikrosekund", - "other": "{0} mikrosekundy", - }, - }, - "relative": { - "year": { - "future": { - "other": "za {0} roku", - "one": "za {0} rok", - "few": "za {0} lata", - "many": "za {0} lat", - }, - "past": { - "other": "{0} roku temu", - "one": "{0} rok temu", - "few": "{0} lata temu", - "many": "{0} lat temu", - }, - }, - "month": { - "future": { - "other": "za {0} miesiąca", - "one": "za {0} miesiąc", - "few": "za {0} miesiące", - "many": "za {0} miesięcy", - }, - "past": { - "other": "{0} miesiąca temu", - "one": "{0} miesiąc temu", - "few": "{0} miesiące temu", - "many": "{0} miesięcy temu", - }, - }, - "week": { - "future": { - "other": "za {0} tygodnia", - "one": "za {0} tydzień", - "few": "za {0} tygodnie", - "many": "za {0} tygodni", - }, - "past": { - "other": "{0} tygodnia temu", - "one": "{0} tydzień temu", - "few": "{0} tygodnie temu", - "many": "{0} tygodni temu", - }, - }, - "day": { - "future": { - "other": "za {0} dnia", - "one": "za {0} dzień", - "few": "za {0} dni", - "many": "za {0} dni", - }, - "past": { - "other": "{0} dnia temu", - "one": "{0} dzień temu", - "few": "{0} dni temu", - "many": "{0} dni temu", - }, - }, - "hour": { - "future": { - "other": "za {0} godziny", - "one": "za {0} godzinę", - "few": "za {0} godziny", - "many": "za {0} godzin", - }, - "past": { - "other": "{0} godziny temu", - "one": "{0} godzinę temu", - "few": "{0} godziny temu", - "many": "{0} godzin temu", - }, - }, - "minute": { - "future": { - "other": "za {0} minuty", - "one": "za {0} minutę", - "few": "za {0} minuty", - "many": "za {0} minut", - }, - "past": { - "other": "{0} minuty temu", - "one": "{0} minutę temu", - "few": "{0} minuty temu", - "many": "{0} minut temu", - }, - }, - "second": { - "future": { - "other": "za {0} sekundy", - "one": "za {0} sekundę", - "few": "za {0} sekundy", - "many": "za {0} sekund", - }, - "past": { - "other": "{0} sekundy temu", - "one": "{0} sekundę temu", - "few": "{0} sekundy temu", - "many": "{0} sekund temu", - }, - }, - }, - "day_periods": { - "midnight": "o północy", - "am": "AM", - "noon": "w południe", - "pm": "PM", - "morning1": "rano", - "morning2": "przed południem", - "afternoon1": "po południu", - "evening1": "wieczorem", - "night1": "w nocy", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/pt_br/__init__.py b/pendulum/locales/pt_br/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/pt_br/__init__.py +++ /dev/null diff --git a/pendulum/locales/pt_br/custom.py b/pendulum/locales/pt_br/custom.py deleted file mode 100644 index 12aced7..0000000 --- a/pendulum/locales/pt_br/custom.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -pt-br custom locale file. -""" - -translations = { - # Relative time - "after": "após {0}", - "before": "{0} atrás", - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd, D [de] MMMM [de] YYYY [às] HH:mm", - "LLL": "D [de] MMMM [de] YYYY [às] HH:mm", - "LL": "D [de] MMMM [de] YYYY", - "L": "DD/MM/YYYY", - }, -} diff --git a/pendulum/locales/pt_br/locale.py b/pendulum/locales/pt_br/locale.py deleted file mode 100644 index 742c41f..0000000 --- a/pendulum/locales/pt_br/locale.py +++ /dev/null @@ -1,143 +0,0 @@ -from .custom import translations as custom_translations - - -""" -pt_br locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" - if ((n == n and (n >= 0 and n <= 2)) and (not (n == n and (n == 2)))) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "dom", - 1: "seg", - 2: "ter", - 3: "qua", - 4: "qui", - 5: "sex", - 6: "sáb", - }, - "narrow": {0: "D", 1: "S", 2: "T", 3: "Q", 4: "Q", 5: "S", 6: "S"}, - "short": { - 0: "dom", - 1: "seg", - 2: "ter", - 3: "qua", - 4: "qui", - 5: "sex", - 6: "sáb", - }, - "wide": { - 0: "domingo", - 1: "segunda-feira", - 2: "terça-feira", - 3: "quarta-feira", - 4: "quinta-feira", - 5: "sexta-feira", - 6: "sábado", - }, - }, - "months": { - "abbreviated": { - 1: "jan", - 2: "fev", - 3: "mar", - 4: "abr", - 5: "mai", - 6: "jun", - 7: "jul", - 8: "ago", - 9: "set", - 10: "out", - 11: "nov", - 12: "dez", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "janeiro", - 2: "fevereiro", - 3: "março", - 4: "abril", - 5: "maio", - 6: "junho", - 7: "julho", - 8: "agosto", - 9: "setembro", - 10: "outubro", - 11: "novembro", - 12: "dezembro", - }, - }, - "units": { - "year": {"one": "{0} ano", "other": "{0} anos"}, - "month": {"one": "{0} mês", "other": "{0} meses"}, - "week": {"one": "{0} semana", "other": "{0} semanas"}, - "day": {"one": "{0} dia", "other": "{0} dias"}, - "hour": {"one": "{0} hora", "other": "{0} horas"}, - "minute": {"one": "{0} minuto", "other": "{0} minutos"}, - "second": {"one": "{0} segundo", "other": "{0} segundos"}, - "microsecond": {"one": "{0} microssegundo", "other": "{0} microssegundos"}, - }, - "relative": { - "year": { - "future": {"other": "em {0} anos", "one": "em {0} ano"}, - "past": {"other": "há {0} anos", "one": "há {0} ano"}, - }, - "month": { - "future": {"other": "em {0} meses", "one": "em {0} mês"}, - "past": {"other": "há {0} meses", "one": "há {0} mês"}, - }, - "week": { - "future": {"other": "em {0} semanas", "one": "em {0} semana"}, - "past": {"other": "há {0} semanas", "one": "há {0} semana"}, - }, - "day": { - "future": {"other": "em {0} dias", "one": "em {0} dia"}, - "past": {"other": "há {0} dias", "one": "há {0} dia"}, - }, - "hour": { - "future": {"other": "em {0} horas", "one": "em {0} hora"}, - "past": {"other": "há {0} horas", "one": "há {0} hora"}, - }, - "minute": { - "future": {"other": "em {0} minutos", "one": "em {0} minuto"}, - "past": {"other": "há {0} minutos", "one": "há {0} minuto"}, - }, - "second": { - "future": {"other": "em {0} segundos", "one": "em {0} segundo"}, - "past": {"other": "há {0} segundos", "one": "há {0} segundo"}, - }, - }, - "day_periods": { - "midnight": "meia-noite", - "am": "AM", - "noon": "meio-dia", - "pm": "PM", - "morning1": "da manhã", - "afternoon1": "da tarde", - "evening1": "da noite", - "night1": "da madrugada", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/ru/__init__.py b/pendulum/locales/ru/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/ru/__init__.py +++ /dev/null diff --git a/pendulum/locales/ru/custom.py b/pendulum/locales/ru/custom.py deleted file mode 100644 index b4c89bb..0000000 --- a/pendulum/locales/ru/custom.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -ru custom locale file. -""" - -translations = { - # Relative time - "ago": "{} назад", - "from_now": "через {}", - "after": "{0} после", - "before": "{0} до", - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "L": "DD.MM.YYYY", - "LL": "D MMMM YYYY г.", - "LLL": "D MMMM YYYY г., HH:mm", - "LLLL": "dddd, D MMMM YYYY г., HH:mm", - }, -} diff --git a/pendulum/locales/ru/locale.py b/pendulum/locales/ru/locale.py deleted file mode 100644 index 3736e0b..0000000 --- a/pendulum/locales/ru/locale.py +++ /dev/null @@ -1,270 +0,0 @@ -from .custom import translations as custom_translations - - -""" -ru locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "few" - if ( - ( - (0 == 0 and (0 == 0)) - and ((n % 10) == (n % 10) and ((n % 10) >= 2 and (n % 10) <= 4)) - ) - and (not ((n % 100) == (n % 100) and ((n % 100) >= 12 and (n % 100) <= 14))) - ) - else "many" - if ( - ( - ((0 == 0 and (0 == 0)) and ((n % 10) == (n % 10) and ((n % 10) == 0))) - or ( - (0 == 0 and (0 == 0)) - and ((n % 10) == (n % 10) and ((n % 10) >= 5 and (n % 10) <= 9)) - ) - ) - or ( - (0 == 0 and (0 == 0)) - and ((n % 100) == (n % 100) and ((n % 100) >= 11 and (n % 100) <= 14)) - ) - ) - else "one" - if ( - ((0 == 0 and (0 == 0)) and ((n % 10) == (n % 10) and ((n % 10) == 1))) - and (not ((n % 100) == (n % 100) and ((n % 100) == 11))) - ) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "вс", - 1: "пн", - 2: "вт", - 3: "ср", - 4: "чт", - 5: "пт", - 6: "сб", - }, - "narrow": {0: "вс", 1: "пн", 2: "вт", 3: "ср", 4: "чт", 5: "пт", 6: "сб"}, - "short": {0: "вс", 1: "пн", 2: "вт", 3: "ср", 4: "чт", 5: "пт", 6: "сб"}, - "wide": { - 0: "воскресенье", - 1: "понедельник", - 2: "вторник", - 3: "среда", - 4: "четверг", - 5: "пятница", - 6: "суббота", - }, - }, - "months": { - "abbreviated": { - 1: "янв.", - 2: "февр.", - 3: "мар.", - 4: "апр.", - 5: "мая", - 6: "июн.", - 7: "июл.", - 8: "авг.", - 9: "сент.", - 10: "окт.", - 11: "нояб.", - 12: "дек.", - }, - "narrow": { - 1: "Я", - 2: "Ф", - 3: "М", - 4: "А", - 5: "М", - 6: "И", - 7: "И", - 8: "А", - 9: "С", - 10: "О", - 11: "Н", - 12: "Д", - }, - "wide": { - 1: "января", - 2: "февраля", - 3: "марта", - 4: "апреля", - 5: "мая", - 6: "июня", - 7: "июля", - 8: "августа", - 9: "сентября", - 10: "октября", - 11: "ноября", - 12: "декабря", - }, - }, - "units": { - "year": { - "one": "{0} год", - "few": "{0} года", - "many": "{0} лет", - "other": "{0} года", - }, - "month": { - "one": "{0} месяц", - "few": "{0} месяца", - "many": "{0} месяцев", - "other": "{0} месяца", - }, - "week": { - "one": "{0} неделя", - "few": "{0} недели", - "many": "{0} недель", - "other": "{0} недели", - }, - "day": { - "one": "{0} день", - "few": "{0} дня", - "many": "{0} дней", - "other": "{0} дня", - }, - "hour": { - "one": "{0} час", - "few": "{0} часа", - "many": "{0} часов", - "other": "{0} часа", - }, - "minute": { - "one": "{0} минута", - "few": "{0} минуты", - "many": "{0} минут", - "other": "{0} минуты", - }, - "second": { - "one": "{0} секунда", - "few": "{0} секунды", - "many": "{0} секунд", - "other": "{0} секунды", - }, - "microsecond": { - "one": "{0} микросекунда", - "few": "{0} микросекунды", - "many": "{0} микросекунд", - "other": "{0} микросекунды", - }, - }, - "relative": { - "year": { - "future": { - "other": "через {0} года", - "one": "через {0} год", - "few": "через {0} года", - "many": "через {0} лет", - }, - "past": { - "other": "{0} года назад", - "one": "{0} год назад", - "few": "{0} года назад", - "many": "{0} лет назад", - }, - }, - "month": { - "future": { - "other": "через {0} месяца", - "one": "через {0} месяц", - "few": "через {0} месяца", - "many": "через {0} месяцев", - }, - "past": { - "other": "{0} месяца назад", - "one": "{0} месяц назад", - "few": "{0} месяца назад", - "many": "{0} месяцев назад", - }, - }, - "week": { - "future": { - "other": "через {0} недели", - "one": "через {0} неделю", - "few": "через {0} недели", - "many": "через {0} недель", - }, - "past": { - "other": "{0} недели назад", - "one": "{0} неделю назад", - "few": "{0} недели назад", - "many": "{0} недель назад", - }, - }, - "day": { - "future": { - "other": "через {0} дня", - "one": "через {0} день", - "few": "через {0} дня", - "many": "через {0} дней", - }, - "past": { - "other": "{0} дня назад", - "one": "{0} день назад", - "few": "{0} дня назад", - "many": "{0} дней назад", - }, - }, - "hour": { - "future": { - "other": "через {0} часа", - "one": "через {0} час", - "few": "через {0} часа", - "many": "через {0} часов", - }, - "past": { - "other": "{0} часа назад", - "one": "{0} час назад", - "few": "{0} часа назад", - "many": "{0} часов назад", - }, - }, - "minute": { - "future": { - "other": "через {0} минуты", - "one": "через {0} минуту", - "few": "через {0} минуты", - "many": "через {0} минут", - }, - "past": { - "other": "{0} минуты назад", - "one": "{0} минуту назад", - "few": "{0} минуты назад", - "many": "{0} минут назад", - }, - }, - "second": { - "future": { - "other": "через {0} секунды", - "one": "через {0} секунду", - "few": "через {0} секунды", - "many": "через {0} секунд", - }, - "past": { - "other": "{0} секунды назад", - "one": "{0} секунду назад", - "few": "{0} секунды назад", - "many": "{0} секунд назад", - }, - }, - }, - "day_periods": { - "midnight": "полночь", - "am": "AM", - "noon": "полдень", - "pm": "PM", - "morning1": "утра", - "afternoon1": "дня", - "evening1": "вечера", - "night1": "ночи", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/sk/__init__.py b/pendulum/locales/sk/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/sk/__init__.py +++ /dev/null diff --git a/pendulum/locales/sk/custom.py b/pendulum/locales/sk/custom.py deleted file mode 100644 index 71afb15..0000000 --- a/pendulum/locales/sk/custom.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -sk custom locale file. -""" - -translations = { - # Relative time - "ago": "pred {}", - "from_now": "o {}", - "after": "{0} po", - "before": "{0} pred", - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "LLLL": "dddd, D. MMMM YYYY HH:mm", - "LLL": "D. MMMM YYYY HH:mm", - "LL": "D. MMMM YYYY", - "L": "DD.MM.YYYY", - }, -} diff --git a/pendulum/locales/sk/locale.py b/pendulum/locales/sk/locale.py deleted file mode 100644 index 8d3459f..0000000 --- a/pendulum/locales/sk/locale.py +++ /dev/null @@ -1,266 +0,0 @@ -from .custom import translations as custom_translations - - -""" -sk locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "few" - if ((n == n and (n >= 2 and n <= 4)) and (0 == 0 and (0 == 0))) - else "many" - if (not (0 == 0 and (0 == 0))) - else "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "ne", - 1: "po", - 2: "ut", - 3: "st", - 4: "št", - 5: "pi", - 6: "so", - }, - "narrow": { - 0: "n", - 1: "p", - 2: "u", - 3: "s", - 4: "š", - 5: "p", - 6: "s", - }, - "short": { - 0: "ne", - 1: "po", - 2: "ut", - 3: "st", - 4: "št", - 5: "pi", - 6: "so", - }, - "wide": { - 0: "nedeľa", - 1: "pondelok", - 2: "utorok", - 3: "streda", - 4: "štvrtok", - 5: "piatok", - 6: "sobota", - }, - }, - "months": { - "abbreviated": { - 1: "jan", - 2: "feb", - 3: "mar", - 4: "apr", - 5: "máj", - 6: "jún", - 7: "júl", - 8: "aug", - 9: "sep", - 10: "okt", - 11: "nov", - 12: "dec", - }, - "narrow": { - 1: "j", - 2: "f", - 3: "m", - 4: "a", - 5: "m", - 6: "j", - 7: "j", - 8: "a", - 9: "s", - 10: "o", - 11: "n", - 12: "d", - }, - "wide": { - 1: "januára", - 2: "februára", - 3: "marca", - 4: "apríla", - 5: "mája", - 6: "júna", - 7: "júla", - 8: "augusta", - 9: "septembra", - 10: "októbra", - 11: "novembra", - 12: "decembra", - }, - }, - "units": { - "year": { - "one": "{0} rok", - "few": "{0} roky", - "many": "{0} roka", - "other": "{0} rokov", - }, - "month": { - "one": "{0} mesiac", - "few": "{0} mesiace", - "many": "{0} mesiaca", - "other": "{0} mesiacov", - }, - "week": { - "one": "{0} týždeň", - "few": "{0} týždne", - "many": "{0} týždňa", - "other": "{0} týždňov", - }, - "day": { - "one": "{0} deň", - "few": "{0} dni", - "many": "{0} dňa", - "other": "{0} dní", - }, - "hour": { - "one": "{0} hodina", - "few": "{0} hodiny", - "many": "{0} hodiny", - "other": "{0} hodín", - }, - "minute": { - "one": "{0} minúta", - "few": "{0} minúty", - "many": "{0} minúty", - "other": "{0} minút", - }, - "second": { - "one": "{0} sekunda", - "few": "{0} sekundy", - "many": "{0} sekundy", - "other": "{0} sekúnd", - }, - "microsecond": { - "one": "{0} mikrosekunda", - "few": "{0} mikrosekundy", - "many": "{0} mikrosekundy", - "other": "{0} mikrosekúnd", - }, - }, - "relative": { - "year": { - "future": { - "other": "o {0} rokov", - "one": "o {0} rok", - "few": "o {0} roky", - "many": "o {0} roka", - }, - "past": { - "other": "pred {0} rokmi", - "one": "pred {0} rokom", - "few": "pred {0} rokmi", - "many": "pred {0} roka", - }, - }, - "month": { - "future": { - "other": "o {0} mesiacov", - "one": "o {0} mesiac", - "few": "o {0} mesiace", - "many": "o {0} mesiaca", - }, - "past": { - "other": "pred {0} mesiacmi", - "one": "pred {0} mesiacom", - "few": "pred {0} mesiacmi", - "many": "pred {0} mesiaca", - }, - }, - "week": { - "future": { - "other": "o {0} týždňov", - "one": "o {0} týždeň", - "few": "o {0} týždne", - "many": "o {0} týždňa", - }, - "past": { - "other": "pred {0} týždňami", - "one": "pred {0} týždňom", - "few": "pred {0} týždňami", - "many": "pred {0} týždňa", - }, - }, - "day": { - "future": { - "other": "o {0} dní", - "one": "o {0} deň", - "few": "o {0} dni", - "many": "o {0} dňa", - }, - "past": { - "other": "pred {0} dňami", - "one": "pred {0} dňom", - "few": "pred {0} dňami", - "many": "pred {0} dňa", - }, - }, - "hour": { - "future": { - "other": "o {0} hodín", - "one": "o {0} hodinu", - "few": "o {0} hodiny", - "many": "o {0} hodiny", - }, - "past": { - "other": "pred {0} hodinami", - "one": "pred {0} hodinou", - "few": "pred {0} hodinami", - "many": "pred {0} hodinou", - }, - }, - "minute": { - "future": { - "other": "o {0} minút", - "one": "o {0} minútu", - "few": "o {0} minúty", - "many": "o {0} minúty", - }, - "past": { - "other": "pred {0} minútami", - "one": "pred {0} minútou", - "few": "pred {0} minútami", - "many": "pred {0} minúty", - }, - }, - "second": { - "future": { - "other": "o {0} sekúnd", - "one": "o {0} sekundu", - "few": "o {0} sekundy", - "many": "o {0} sekundy", - }, - "past": { - "other": "pred {0} sekundami", - "one": "pred {0} sekundou", - "few": "pred {0} sekundami", - "many": "pred {0} sekundy", - }, - }, - }, - "day_periods": { - "midnight": "o polnoci", - "am": "AM", - "noon": "napoludnie", - "pm": "PM", - "morning1": "ráno", - "morning2": "dopoludnia", - "afternoon1": "popoludní", - "evening1": "večer", - "night1": "v noci", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/sv/__init__.py b/pendulum/locales/sv/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/sv/__init__.py +++ /dev/null diff --git a/pendulum/locales/sv/custom.py b/pendulum/locales/sv/custom.py deleted file mode 100644 index 7158f4b..0000000 --- a/pendulum/locales/sv/custom.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -sv custom locale file. -""" - -translations = { - # Relative time - "ago": "{} sedan", - "from_now": "från nu {}", - "after": "{0} efter", - "before": "{0} innan", - # Date formats - "date_formats": { - "LTS": "HH:mm:ss", - "LT": "HH:mm", - "L": "YYYY-MM-DD", - "LL": "D MMMM YYYY", - "LLL": "D MMMM YYYY, HH:mm", - "LLLL": "dddd, D MMMM YYYY, HH:mm", - }, -} diff --git a/pendulum/locales/sv/locale.py b/pendulum/locales/sv/locale.py deleted file mode 100644 index 5b74a6e..0000000 --- a/pendulum/locales/sv/locale.py +++ /dev/null @@ -1,222 +0,0 @@ -from .custom import translations as custom_translations - - -""" -sv locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "one" - if ((n == n and (n == 1)) and (0 == 0 and (0 == 0))) - else "other", - "ordinal": lambda n: "one" - if ( - ((n % 10) == (n % 10) and (((n % 10) == 1) or ((n % 10) == 2))) - and (not ((n % 100) == (n % 100) and (((n % 100) == 11) or ((n % 100) == 12)))) - ) - else "other", - "translations": { - "days": { - "abbreviated": { - 0: "sön", - 1: "mån", - 2: "tis", - 3: "ons", - 4: "tors", - 5: "fre", - 6: "lör", - }, - "narrow": { - 0: "S", - 1: "M", - 2: "T", - 3: "O", - 4: "T", - 5: "F", - 6: "L", - }, - "short": { - 0: "sö", - 1: "må", - 2: "ti", - 3: "on", - 4: "to", - 5: "fr", - 6: "lö", - }, - "wide": { - 0: "söndag", - 1: "måndag", - 2: "tisdag", - 3: "onsdag", - 4: "torsdag", - 5: "fredag", - 6: "lördag", - }, - }, - "months": { - "abbreviated": { - 1: "jan.", - 2: "feb.", - 3: "mars", - 4: "apr.", - 5: "maj", - 6: "juni", - 7: "juli", - 8: "aug.", - 9: "sep.", - 10: "okt.", - 11: "nov.", - 12: "dec.", - }, - "narrow": { - 1: "J", - 2: "F", - 3: "M", - 4: "A", - 5: "M", - 6: "J", - 7: "J", - 8: "A", - 9: "S", - 10: "O", - 11: "N", - 12: "D", - }, - "wide": { - 1: "januari", - 2: "februari", - 3: "mars", - 4: "april", - 5: "maj", - 6: "juni", - 7: "juli", - 8: "augusti", - 9: "september", - 10: "oktober", - 11: "november", - 12: "december", - }, - }, - "units": { - "year": { - "one": "{0} år", - "other": "{0} år", - }, - "month": { - "one": "{0} månad", - "other": "{0} månader", - }, - "week": { - "one": "{0} vecka", - "other": "{0} veckor", - }, - "day": { - "one": "{0} dygn", - "other": "{0} dygn", - }, - "hour": { - "one": "{0} timme", - "other": "{0} timmar", - }, - "minute": { - "one": "{0} minut", - "other": "{0} minuter", - }, - "second": { - "one": "{0} sekund", - "other": "{0} sekunder", - }, - "microsecond": { - "one": "{0} mikrosekund", - "other": "{0} mikrosekunder", - }, - }, - "relative": { - "year": { - "future": { - "other": "om {0} år", - "one": "om {0} år", - }, - "past": { - "other": "för {0} år sedan", - "one": "för {0} år sedan", - }, - }, - "month": { - "future": { - "other": "om {0} månader", - "one": "om {0} månad", - }, - "past": { - "other": "för {0} månader sedan", - "one": "för {0} månad sedan", - }, - }, - "week": { - "future": { - "other": "om {0} veckor", - "one": "om {0} vecka", - }, - "past": { - "other": "för {0} veckor sedan", - "one": "för {0} vecka sedan", - }, - }, - "day": { - "future": { - "other": "om {0} dagar", - "one": "om {0} dag", - }, - "past": { - "other": "för {0} dagar sedan", - "one": "för {0} dag sedan", - }, - }, - "hour": { - "future": { - "other": "om {0} timmar", - "one": "om {0} timme", - }, - "past": { - "other": "för {0} timmar sedan", - "one": "för {0} timme sedan", - }, - }, - "minute": { - "future": { - "other": "om {0} minuter", - "one": "om {0} minut", - }, - "past": { - "other": "för {0} minuter sedan", - "one": "för {0} minut sedan", - }, - }, - "second": { - "future": { - "other": "om {0} sekunder", - "one": "om {0} sekund", - }, - "past": { - "other": "för {0} sekunder sedan", - "one": "för {0} sekund sedan", - }, - }, - }, - "day_periods": { - "midnight": "midnatt", - "am": "fm", - "pm": "em", - "morning1": "på morgonen", - "morning2": "på förmiddagen", - "afternoon1": "på eftermiddagen", - "evening1": "på kvällen", - "night1": "på natten", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/locales/zh/__init__.py b/pendulum/locales/zh/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/locales/zh/__init__.py +++ /dev/null diff --git a/pendulum/locales/zh/custom.py b/pendulum/locales/zh/custom.py deleted file mode 100644 index 69bc4ca..0000000 --- a/pendulum/locales/zh/custom.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -zh custom locale file. -""" - -translations = { - # Relative time - "after": "{time}后", - "before": "{time}前", - # Date formats - "date_formats": { - "LTS": "Ah点m分s秒", - "LT": "Ah点mm分", - "LLLL": "YYYY年MMMD日ddddAh点mm分", - "LLL": "YYYY年MMMD日Ah点mm分", - "LL": "YYYY年MMMD日", - "L": "YYYY-MM-DD", - }, -} diff --git a/pendulum/locales/zh/locale.py b/pendulum/locales/zh/locale.py deleted file mode 100644 index 2df477f..0000000 --- a/pendulum/locales/zh/locale.py +++ /dev/null @@ -1,113 +0,0 @@ -from .custom import translations as custom_translations - - -""" -zh locale file. - -It has been generated automatically and must not be modified directly. -""" - - -locale = { - "plural": lambda n: "other", - "ordinal": lambda n: "other", - "translations": { - "days": { - "abbreviated": { - 0: "周日", - 1: "周一", - 2: "周二", - 3: "周三", - 4: "周四", - 5: "周五", - 6: "周六", - }, - "narrow": {0: "日", 1: "一", 2: "二", 3: "三", 4: "四", 5: "五", 6: "六"}, - "short": {0: "周日", 1: "周一", 2: "周二", 3: "周三", 4: "周四", 5: "周五", 6: "周六"}, - "wide": { - 0: "星期日", - 1: "星期一", - 2: "星期二", - 3: "星期三", - 4: "星期四", - 5: "星期五", - 6: "星期六", - }, - }, - "months": { - "abbreviated": { - 1: "1月", - 2: "2月", - 3: "3月", - 4: "4月", - 5: "5月", - 6: "6月", - 7: "7月", - 8: "8月", - 9: "9月", - 10: "10月", - 11: "11月", - 12: "12月", - }, - "narrow": { - 1: "1", - 2: "2", - 3: "3", - 4: "4", - 5: "5", - 6: "6", - 7: "7", - 8: "8", - 9: "9", - 10: "10", - 11: "11", - 12: "12", - }, - "wide": { - 1: "一月", - 2: "二月", - 3: "三月", - 4: "四月", - 5: "五月", - 6: "六月", - 7: "七月", - 8: "八月", - 9: "九月", - 10: "十月", - 11: "十一月", - 12: "十二月", - }, - }, - "units": { - "year": {"other": "{0}年"}, - "month": {"other": "{0}个月"}, - "week": {"other": "{0}周"}, - "day": {"other": "{0}天"}, - "hour": {"other": "{0}小时"}, - "minute": {"other": "{0}分钟"}, - "second": {"other": "{0}秒钟"}, - "microsecond": {"other": "{0}微秒"}, - }, - "relative": { - "year": {"future": {"other": "{0}年后"}, "past": {"other": "{0}年前"}}, - "month": {"future": {"other": "{0}个月后"}, "past": {"other": "{0}个月前"}}, - "week": {"future": {"other": "{0}周后"}, "past": {"other": "{0}周前"}}, - "day": {"future": {"other": "{0}天后"}, "past": {"other": "{0}天前"}}, - "hour": {"future": {"other": "{0}小时后"}, "past": {"other": "{0}小时前"}}, - "minute": {"future": {"other": "{0}分钟后"}, "past": {"other": "{0}分钟前"}}, - "second": {"future": {"other": "{0}秒钟后"}, "past": {"other": "{0}秒钟前"}}, - }, - "day_periods": { - "midnight": "午夜", - "am": "上午", - "pm": "下午", - "morning1": "清晨", - "morning2": "上午", - "afternoon1": "下午", - "afternoon2": "下午", - "evening1": "晚上", - "night1": "凌晨", - }, - }, - "custom": custom_translations, -} diff --git a/pendulum/mixins/__init__.py b/pendulum/mixins/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/mixins/__init__.py +++ /dev/null diff --git a/pendulum/mixins/default.py b/pendulum/mixins/default.py deleted file mode 100644 index 59f985e..0000000 --- a/pendulum/mixins/default.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -from pendulum.formatting import Formatter - -_formatter = Formatter() - - -class FormattableMixin: - _formatter: Formatter = _formatter - - def format(self, fmt: str, locale: str | None = None) -> str: - """ - Formats the instance using the given format. - - :param fmt: The format to use - :param locale: The locale to use - """ - return self._formatter.format(self, fmt, locale) - - def for_json(self) -> str: - """ - Methods for automatic json serialization by simplejson. - """ - return str(self) - - def __format__(self, format_spec: str) -> str: - if len(format_spec) > 0: - if "%" in format_spec: - return self.strftime(format_spec) - - return self.format(format_spec) - - return str(self) - - def __str__(self) -> str: - return self.isoformat() diff --git a/pendulum/parser.py b/pendulum/parser.py deleted file mode 100644 index 77900e2..0000000 --- a/pendulum/parser.py +++ /dev/null @@ -1,124 +0,0 @@ -from __future__ import annotations - -import datetime -import typing as t - -import pendulum - -from pendulum.parsing import _Interval -from pendulum.parsing import parse as base_parse -from pendulum.tz.timezone import UTC - -if t.TYPE_CHECKING: - from pendulum.date import Date - from pendulum.datetime import DateTime - from pendulum.duration import Duration - from pendulum.interval import Interval - from pendulum.time import Time - -try: - from pendulum.parsing._iso8601 import Duration as CDuration -except ImportError: - CDuration = None # type: ignore[misc, assignment] - - -def parse(text: str, **options: t.Any) -> Date | Time | DateTime | Duration: - # Use the mock now value if it exists - options["now"] = options.get("now") - - return _parse(text, **options) - - -def _parse(text: str, **options: t.Any) -> Date | DateTime | Time | Duration | Interval: - """ - Parses a string with the given options. - - :param text: The string to parse. - """ - # Handling special cases - if text == "now": - return pendulum.now() - - parsed = base_parse(text, **options) - - if isinstance(parsed, datetime.datetime): - return pendulum.datetime( - parsed.year, - parsed.month, - parsed.day, - parsed.hour, - parsed.minute, - parsed.second, - parsed.microsecond, - tz=parsed.tzinfo or options.get("tz", UTC), - ) - - if isinstance(parsed, datetime.date): - return pendulum.date(parsed.year, parsed.month, parsed.day) - - if isinstance(parsed, datetime.time): - return pendulum.time( - parsed.hour, parsed.minute, parsed.second, parsed.microsecond - ) - - if isinstance(parsed, _Interval): - if parsed.duration is not None: - duration = parsed.duration - - if parsed.start is not None: - dt = pendulum.instance(parsed.start, tz=options.get("tz", UTC)) - - return pendulum.interval( - dt, - dt.add( - years=duration.years, - months=duration.months, - weeks=duration.weeks, - days=duration.remaining_days, - hours=duration.hours, - minutes=duration.minutes, - seconds=duration.remaining_seconds, - microseconds=duration.microseconds, - ), - ) - - dt = pendulum.instance( - t.cast(datetime.datetime, parsed.end), tz=options.get("tz", UTC) - ) - - return pendulum.interval( - dt.subtract( - years=duration.years, - months=duration.months, - weeks=duration.weeks, - days=duration.remaining_days, - hours=duration.hours, - minutes=duration.minutes, - seconds=duration.remaining_seconds, - microseconds=duration.microseconds, - ), - dt, - ) - - return pendulum.interval( - pendulum.instance( - t.cast(datetime.datetime, parsed.start), tz=options.get("tz", UTC) - ), - pendulum.instance( - t.cast(datetime.datetime, parsed.end), tz=options.get("tz", UTC) - ), - ) - - if CDuration and isinstance(parsed, CDuration): - return pendulum.duration( - years=parsed.years, - months=parsed.months, - weeks=parsed.weeks, - days=parsed.days, - hours=parsed.hours, - minutes=parsed.minutes, - seconds=parsed.seconds, - microseconds=parsed.microseconds, - ) - - return parsed diff --git a/pendulum/parsing/__init__.py b/pendulum/parsing/__init__.py deleted file mode 100644 index 0e64065..0000000 --- a/pendulum/parsing/__init__.py +++ /dev/null @@ -1,233 +0,0 @@ -from __future__ import annotations - -import contextlib -import copy -import os -import re -import struct - -from datetime import date -from datetime import datetime -from datetime import time -from typing import Any -from typing import Optional -from typing import cast - -from dateutil import parser - -from pendulum.parsing.exceptions import ParserError - -with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" - -try: - if not with_extensions or struct.calcsize("P") == 4: - raise ImportError() - - from pendulum.parsing._iso8601 import Duration - from pendulum.parsing._iso8601 import parse_iso8601 -except ImportError: - from pendulum.duration import Duration # type: ignore[misc] - from pendulum.parsing.iso8601 import parse_iso8601 # type: ignore[misc] - -COMMON = re.compile( - # Date (optional) # noqa: E800 - "^" - "(?P<date>" - " (?P<classic>" # Classic date (YYYY-MM-DD) - r" (?P<year>\d{4})" # Year - " (?P<monthday>" - r" (?P<monthsep>[/:])?(?P<month>\d{2})" # Month (optional) - r" ((?P<daysep>[/:])?(?P<day>\d{2}))" # Day (optional) - " )?" - " )" - ")?" - # Time (optional) # noqa: E800 - "(?P<time>" - r" (?P<timesep>\ )?" # Separator (space) - r" (?P<hour>\d{1,2}):(?P<minute>\d{1,2})?(?::(?P<second>\d{1,2}))?" # HH:mm:ss (optional mm and ss) - # Subsecond part (optional) - " (?P<subsecondsection>" - " (?:[.|,])" # Subsecond separator (optional) - r" (?P<subsecond>\d{1,9})" # Subsecond - " )?" - ")?" - "$", - re.VERBOSE, -) - -DEFAULT_OPTIONS = { - "day_first": False, - "year_first": True, - "strict": True, - "exact": False, - "now": None, -} - - -def parse(text: str, **options: Any) -> datetime | date | time | _Interval | Duration: - """ - Parses a string with the given options. - - :param text: The string to parse. - """ - _options: dict[str, Any] = copy.copy(DEFAULT_OPTIONS) - _options.update(options) - - return _normalize(_parse(text, **_options), **_options) - - -def _normalize( - parsed: datetime | date | time | _Interval | Duration, **options: Any -) -> datetime | date | time | _Interval | Duration: - """ - Normalizes the parsed element. - - :param parsed: The parsed elements. - """ - if options.get("exact"): - return parsed - - if isinstance(parsed, time): - now = cast(Optional[datetime], options["now"]) or datetime.now() - - return datetime( - now.year, - now.month, - now.day, - parsed.hour, - parsed.minute, - parsed.second, - parsed.microsecond, - ) - elif isinstance(parsed, date) and not isinstance(parsed, datetime): - return datetime(parsed.year, parsed.month, parsed.day) - - return parsed - - -def _parse(text: str, **options: Any) -> datetime | date | time | _Interval | Duration: - # Trying to parse ISO8601 - with contextlib.suppress(ValueError): - return parse_iso8601(text) - - with contextlib.suppress(ValueError): - return _parse_iso8601_interval(text) - - with contextlib.suppress(ParserError): - return _parse_common(text, **options) - - # We couldn't parse the string - # so we fallback on the dateutil parser - # If not strict - if options.get("strict", True): - raise ParserError(f"Unable to parse string [{text}]") - - try: - dt = parser.parse( - text, dayfirst=options["day_first"], yearfirst=options["year_first"] - ) - except ValueError: - raise ParserError(f"Invalid date string: {text}") - - return dt - - -def _parse_common(text: str, **options: Any) -> datetime | date | time: - """ - Tries to parse the string as a common datetime format. - - :param text: The string to parse. - """ - m = COMMON.match(text) - has_date = False - year = 0 - month = 1 - day = 1 - - if not m: - raise ParserError("Invalid datetime string") - - if m.group("date"): - # A date has been specified - has_date = True - - year = int(m.group("year")) - - if not m.group("monthday"): - # No month and day - month = 1 - day = 1 - else: - if options["day_first"]: - month = int(m.group("day")) - day = int(m.group("month")) - else: - month = int(m.group("month")) - day = int(m.group("day")) - - if not m.group("time"): - return date(year, month, day) - - # Grabbing hh:mm:ss - hour = int(m.group("hour")) - - minute = int(m.group("minute")) - - if m.group("second"): - second = int(m.group("second")) - else: - second = 0 - - # Grabbing subseconds, if any - microsecond = 0 - if m.group("subsecondsection"): - # Limiting to 6 chars - subsecond = m.group("subsecond")[:6] - - microsecond = int(f"{subsecond:0<6}") - - if has_date: - return datetime(year, month, day, hour, minute, second, microsecond) - - return time(hour, minute, second, microsecond) - - -class _Interval: - """ - Special class to handle ISO 8601 intervals - """ - - def __init__( - self, - start: datetime | None = None, - end: datetime | None = None, - duration: Duration | None = None, - ) -> None: - self.start = start - self.end = end - self.duration = duration - - -def _parse_iso8601_interval(text: str) -> _Interval: - if "/" not in text: - raise ParserError("Invalid interval") - - first, last = text.split("/") - start = end = duration = None - - if first[0] == "P": - # duration/end - duration = parse_iso8601(first) - end = parse_iso8601(last) - elif last[0] == "P": - # start/duration - start = parse_iso8601(first) - duration = parse_iso8601(last) - else: - # start/end - start = parse_iso8601(first) - end = parse_iso8601(last) - - return _Interval( - cast(datetime, start), cast(datetime, end), cast(Duration, duration) - ) diff --git a/pendulum/parsing/_iso8601.c b/pendulum/parsing/_iso8601.c deleted file mode 100644 index 1322423..0000000 --- a/pendulum/parsing/_iso8601.c +++ /dev/null @@ -1,1361 +0,0 @@ -/* ------------------------------------------------------------------------- */ - -#include <Python.h> -#include <datetime.h> -#include <structmember.h> -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> - -#ifndef PyVarObject_HEAD_INIT -#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, -#endif - - -/* ------------------------------------------------------------------------- */ - -#define EPOCH_YEAR 1970 - -#define DAYS_PER_N_YEAR 365 -#define DAYS_PER_L_YEAR 366 - -#define USECS_PER_SEC 1000000 - -#define SECS_PER_MIN 60 -#define SECS_PER_HOUR (60 * SECS_PER_MIN) -#define SECS_PER_DAY (SECS_PER_HOUR * 24) - -// 400-year chunks always have 146097 days (20871 weeks). -#define DAYS_PER_400_YEARS 146097L -#define SECS_PER_400_YEARS ((int64_t)DAYS_PER_400_YEARS * (int64_t)SECS_PER_DAY) - -// The number of seconds in an aligned 100-year chunk, for those that -// do not begin with a leap year and those that do respectively. -const int64_t SECS_PER_100_YEARS[2] = { - (uint64_t)(76L * DAYS_PER_N_YEAR + 24L * DAYS_PER_L_YEAR) * SECS_PER_DAY, - (uint64_t)(75L * DAYS_PER_N_YEAR + 25L * DAYS_PER_L_YEAR) * SECS_PER_DAY -}; - -// The number of seconds in an aligned 4-year chunk, for those that -// do not begin with a leap year and those that do respectively. -const int32_t SECS_PER_4_YEARS[2] = { - (4 * DAYS_PER_N_YEAR + 0 * DAYS_PER_L_YEAR) * SECS_PER_DAY, - (3 * DAYS_PER_N_YEAR + 1 * DAYS_PER_L_YEAR) * SECS_PER_DAY -}; - -// The number of seconds in non-leap and leap years respectively. -const int32_t SECS_PER_YEAR[2] = { - DAYS_PER_N_YEAR * SECS_PER_DAY, - DAYS_PER_L_YEAR * SECS_PER_DAY -}; - -#define MONTHS_PER_YEAR 12 - -// The month lengths in non-leap and leap years respectively. -const int32_t DAYS_PER_MONTHS[2][13] = { - {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -}; - -// The day offsets of the beginning of each (1-based) month in non-leap -// and leap years respectively. -// For example, in a leap year there are 335 days before December. -const int32_t MONTHS_OFFSETS[2][14] = { - {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, - {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} -}; - -const int DAY_OF_WEEK_TABLE[12] = { - 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 -}; - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -// Parsing errors -const int PARSER_INVALID_ISO8601 = 0; -const int PARSER_INVALID_DATE = 1; -const int PARSER_INVALID_TIME = 2; -const int PARSER_INVALID_WEEK_DATE = 3; -const int PARSER_INVALID_WEEK_NUMBER = 4; -const int PARSER_INVALID_WEEKDAY_NUMBER = 5; -const int PARSER_INVALID_ORDINAL_DAY_FOR_YEAR = 6; -const int PARSER_INVALID_MONTH_OR_DAY = 7; -const int PARSER_INVALID_MONTH = 8; -const int PARSER_INVALID_DAY_FOR_MONTH = 9; -const int PARSER_INVALID_HOUR = 10; -const int PARSER_INVALID_MINUTE = 11; -const int PARSER_INVALID_SECOND = 12; -const int PARSER_INVALID_SUBSECOND = 13; -const int PARSER_INVALID_TZ_OFFSET = 14; -const int PARSER_INVALID_DURATION = 15; -const int PARSER_INVALID_DURATION_FLOAT_YEAR_MONTH_NOT_SUPPORTED = 16; - -const char PARSER_ERRORS[17][80] = { - "Invalid ISO 8601 string", - "Invalid date", - "Invalid time", - "Invalid week date", - "Invalid week number", - "Invalid weekday number", - "Invalid ordinal day for year", - "Invalid month and/or day", - "Invalid month", - "Invalid day for month", - "Invalid hour", - "Invalid minute", - "Invalid second", - "Invalid subsecond", - "Invalid timezone offset", - "Invalid duration", - "Float years and months are not supported" -}; - -/* ------------------------------------------------------------------------- */ - - -int p(int y) { - return y + y/4 - y/100 + y/400; -} - -int is_leap(int year) { - return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); -} - -int week_day(int year, int month, int day) { - int y; - int w; - - y = year - (month < 3); - - w = (p(y) + DAY_OF_WEEK_TABLE[month - 1] + day) % 7; - - if (!w) { - w = 7; - } - - return w; -} - -int days_in_year(int year) { - if (is_leap(year)) { - return DAYS_PER_L_YEAR; - } - - return DAYS_PER_N_YEAR; -} - -int is_long_year(int year) { - return (p(year) % 7 == 4) || (p(year - 1) % 7 == 3); -} - - -/* ------------------------ Custom Types ------------------------------- */ - - -/* - * class FixedOffset(tzinfo): - */ -typedef struct { - PyObject_HEAD - int offset; - char *tzname; -} FixedOffset; - -/* - * def __init__(self, offset): - * self.offset = offset -*/ -static int FixedOffset_init(FixedOffset *self, PyObject *args, PyObject *kwargs) { - int offset; - char *tzname = NULL; - - static char *kwlist[] = {"offset", "tzname", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|s", kwlist, &offset, &tzname)) - return -1; - - self->offset = offset; - self->tzname = tzname; - - return 0; -} - -/* - * def utcoffset(self, dt): - * return timedelta(seconds=self.offset * 60) - */ -static PyObject *FixedOffset_utcoffset(FixedOffset *self, PyObject *args) { - return PyDelta_FromDSU(0, self->offset, 0); -} - -/* - * def dst(self, dt): - * return timedelta(seconds=self.offset * 60) - */ -static PyObject *FixedOffset_dst(FixedOffset *self, PyObject *args) { - return PyDelta_FromDSU(0, self->offset, 0); -} - -/* - * def tzname(self, dt): - * sign = '+' - * if self.offset < 0: - * sign = '-' - * return f"{sign}{self.offset / 60}:{self.offset % 60}" - */ -static PyObject *FixedOffset_tzname(FixedOffset *self, PyObject *args) { - if (self->tzname != NULL) { - return PyUnicode_FromString(self->tzname); - } - - char sign = '+'; - int offset = self->offset; - - if (offset < 0) { - sign = '-'; - offset *= -1; - } - - return PyUnicode_FromFormat( - "%c%02d:%02d", - sign, - offset / SECS_PER_HOUR, - offset / SECS_PER_MIN % SECS_PER_MIN - ); -} - -/* - * def __repr__(self): - * return self.tzname() - */ -static PyObject *FixedOffset_repr(FixedOffset *self) { - return FixedOffset_tzname(self, NULL); -} - -/* - * Class member / class attributes - */ -static PyMemberDef FixedOffset_members[] = { - {"offset", T_INT, offsetof(FixedOffset, offset), 0, "UTC offset"}, - {NULL} -}; - -/* - * Class methods - */ -static PyMethodDef FixedOffset_methods[] = { - {"utcoffset", (PyCFunction)FixedOffset_utcoffset, METH_VARARGS, ""}, - {"dst", (PyCFunction)FixedOffset_dst, METH_VARARGS, ""}, - {"tzname", (PyCFunction)FixedOffset_tzname, METH_VARARGS, ""}, - {NULL} -}; - -static PyTypeObject FixedOffset_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "FixedOffset_type", /* tp_name */ - sizeof(FixedOffset), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)FixedOffset_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)FixedOffset_repr, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ - "TZInfo with fixed offset", /* tp_doc */ -}; - -/* - * Instantiate new FixedOffset_type object - * Skip overhead of calling PyObject_New and PyObject_Init. - * Directly allocate object. - */ -static PyObject *new_fixed_offset_ex(int offset, char *name, PyTypeObject *type) { - FixedOffset *self = (FixedOffset *) (type->tp_alloc(type, 0)); - - if (self != NULL) { - self->offset = offset; - self->tzname = name; - } - - return (PyObject *) self; -} - -#define new_fixed_offset(offset, name) new_fixed_offset_ex(offset, name, &FixedOffset_type) - - -/* - * class Duration(): - */ -typedef struct { - PyObject_HEAD - int years; - int months; - int weeks; - int days; - int hours; - int minutes; - int seconds; - int microseconds; -} Duration; - -/* - * def __init__(self, years, months, days, hours, minutes, seconds, microseconds): - * self.years = years - * # ... -*/ -static int Duration_init(Duration *self, PyObject *args, PyObject *kwargs) { - int years; - int months; - int weeks; - int days; - int hours; - int minutes; - int seconds; - int microseconds; - - if (!PyArg_ParseTuple(args, "iiiiiiii", &years, &months, &weeks, &days, &hours, &minutes, &seconds, µseconds)) - return -1; - - self->years = years; - self->months = months; - self->weeks = weeks; - self->days = days; - self->hours = hours; - self->minutes = minutes; - self->seconds = seconds; - self->microseconds = microseconds; - - return 0; -} - -/* - * def __repr__(self): - * return '{} years {} months {} days {} hours {} minutes {} seconds {} microseconds'.format( - * self.years, self.months, self.days, self.minutes, self.hours, self.seconds, self.microseconds - * ) - */ -static PyObject *Duration_repr(Duration *self) { - return PyUnicode_FromFormat( - "%d years %d months %d weeks %d days %d hours %d minutes %d seconds %d microseconds", - self->years, - self->months, - self->weeks, - self->days, - self->hours, - self->minutes, - self->seconds, - self->microseconds - ); -} - -/* - * Instantiate new Duration_type object - * Skip overhead of calling PyObject_New and PyObject_Init. - * Directly allocate object. - */ -static PyObject *new_duration_ex(int years, int months, int weeks, int days, int hours, int minutes, int seconds, int microseconds, PyTypeObject *type) { - Duration *self = (Duration *) (type->tp_alloc(type, 0)); - - if (self != NULL) { - self->years = years; - self->months = months; - self->weeks = weeks; - self->days = days; - self->hours = hours; - self->minutes = minutes; - self->seconds = seconds; - self->microseconds = microseconds; - } - - return (PyObject *) self; -} - -/* - * Class member / class attributes - */ -static PyMemberDef Duration_members[] = { - {"years", T_INT, offsetof(Duration, years), 0, "years in duration"}, - {"months", T_INT, offsetof(Duration, months), 0, "months in duration"}, - {"weeks", T_INT, offsetof(Duration, weeks), 0, "weeks in duration"}, - {"days", T_INT, offsetof(Duration, days), 0, "days in duration"}, - {"remaining_days", T_INT, offsetof(Duration, days), 0, "days in duration"}, - {"hours", T_INT, offsetof(Duration, hours), 0, "hours in duration"}, - {"minutes", T_INT, offsetof(Duration, minutes), 0, "minutes in duration"}, - {"seconds", T_INT, offsetof(Duration, seconds), 0, "seconds in duration"}, - {"remaining_seconds", T_INT, offsetof(Duration, seconds), 0, "seconds in duration"}, - {"microseconds", T_INT, offsetof(Duration, microseconds), 0, "microseconds in duration"}, - {NULL} -}; - -static PyTypeObject Duration_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "Duration", /* tp_name */ - sizeof(Duration), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)Duration_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)Duration_repr, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Duration", /* tp_doc */ -}; - -#define new_duration(years, months, weeks, days, hours, minutes, seconds, microseconds) new_duration_ex(years, months, weeks, days, hours, minutes, seconds, microseconds, &Duration_type) - -typedef struct { - int is_date; - int is_time; - int is_datetime; - int is_duration; - int is_period; - int ambiguous; - int year; - int month; - int day; - int hour; - int minute; - int second; - int microsecond; - int offset; - int has_offset; - char *tzname; - int years; - int months; - int weeks; - int days; - int hours; - int minutes; - int seconds; - int microseconds; - int error; -} Parsed; - - -Parsed* new_parsed() { - Parsed *parsed; - - if((parsed = malloc(sizeof *parsed)) != NULL) { - parsed->is_date = 0; - parsed->is_time = 0; - parsed->is_datetime = 0; - parsed->is_duration = 0; - parsed->is_period = 0; - - parsed->ambiguous = 0; - parsed->year = 0; - parsed->month = 1; - parsed->day = 1; - parsed->hour = 0; - parsed->minute = 0; - parsed->second = 0; - parsed->microsecond = 0; - parsed->offset = 0; - parsed->has_offset = 0; - parsed->tzname = NULL; - - parsed->years = 0; - parsed->months = 0; - parsed->weeks = 0; - parsed->days = 0; - parsed->hours = 0; - parsed->minutes = 0; - parsed->seconds = 0; - parsed->microseconds = 0; - - parsed->error = -1; - } - - return parsed; -} - - -/* -------------------------- Functions --------------------------*/ - -Parsed* _parse_iso8601_datetime(char *str, Parsed *parsed) { - char* c; - int monthday = 0; - int week = 0; - int weekday = 1; - int ordinal; - int tz_sign = 0; - int leap = 0; - int separators = 0; - int time = 0; - int i; - int j; - - // Assuming date only for now - parsed->is_date = 1; - - c = str; - - for (i = 0; i < 4; i++) { - if (*c >= '0' && *c <= '9') { - parsed->year = 10 * parsed->year + *c++ - '0'; - } else { - parsed->error = PARSER_INVALID_ISO8601; - - return NULL; - } - } - - leap = is_leap(parsed->year); - - // Optional separator - if (*c == '-') { - separators++; - c++; - } - - // Checking for week dates - if (*c == 'W') { - c++; - - i = 0; - while (*c != '\0' && *c != ' ' && *c != 'T') { - if (*c == '-') { - separators++; - c++; - continue; - } - - week = 10 * week + *c++ - '0'; - - i++; - } - - switch (i) { - case 2: - // Only week number - break; - case 3: - // Week with weekday - if (!(separators == 0 || separators == 2)) { - // We should have 2 or no separator - parsed->error = PARSER_INVALID_WEEK_DATE; - - return NULL; - } - - weekday = week % 10; - week /= 10; - - break; - default: - // Any other case is wrong - parsed->error = PARSER_INVALID_WEEK_DATE; - - return NULL; - } - - // Checks - if (week > 53 || (week > 52 && !is_long_year(parsed->year))) { - parsed->error = PARSER_INVALID_WEEK_NUMBER; - - return NULL; - } - - if (weekday > 7) { - parsed->error = PARSER_INVALID_WEEKDAY_NUMBER; - - return NULL; - } - - // Calculating ordinal day - ordinal = week * 7 + weekday - (week_day(parsed->year, 1, 4) + 3); - - if (ordinal < 1) { - // Previous year - ordinal += days_in_year(parsed->year - 1); - parsed->year -= 1; - leap = is_leap(parsed->year); - } - - if (ordinal > days_in_year(parsed->year)) { - // Next year - ordinal -= days_in_year(parsed->year); - parsed->year += 1; - leap = is_leap(parsed->year); - } - - for (j = 1; j < 14; j++) { - if (ordinal <= MONTHS_OFFSETS[leap][j]) { - parsed->day = ordinal - MONTHS_OFFSETS[leap][j - 1]; - parsed->month = j - 1; - - break; - } - } - } else { - // At this point we need to check the number - // of characters until the end of the date part - // (or the end of the string). - // - // If two, we have only a month if there is a separator, it may be a time otherwise. - // If three, we have an ordinal date. - // If four, we have a complete date - i = 0; - while (*c != '\0' && *c != ' ' && *c != 'T') { - if (*c == '-') { - separators++; - c++; - continue; - } - - if (!(*c >= '0' && *c <='9')) { - parsed->error = PARSER_INVALID_DATE; - - return NULL; - } - - monthday = 10 * monthday + *c++ - '0'; - - i++; - } - - switch (i) { - case 0: - // No month/day specified (only a year) - break; - case 2: - if (!separators) { - // The date looks like 201207 - // which is invalid for a date - // But it might be a time in the form hhmmss - parsed->ambiguous = 1; - } else if (separators > 1) { - parsed->error = PARSER_INVALID_DATE; - - return NULL; - } - - parsed->month = monthday; - break; - case 3: - // Ordinal day - if (separators > 1) { - parsed->error = PARSER_INVALID_DATE; - - return NULL; - } - - if (monthday < 1 || monthday > MONTHS_OFFSETS[leap][13]) { - parsed->error = PARSER_INVALID_ORDINAL_DAY_FOR_YEAR; - - return NULL; - } - - for (j = 1; j < 14; j++) { - if (monthday <= MONTHS_OFFSETS[leap][j]) { - parsed->day = monthday - MONTHS_OFFSETS[leap][j - 1]; - parsed->month = j - 1; - - break; - } - } - - break; - case 4: - // Month and day - parsed->month = monthday / 100; - parsed->day = monthday % 100; - - break; - default: - parsed->error = PARSER_INVALID_MONTH_OR_DAY; - - return NULL; - } - } - - // Checks - if (separators && !monthday && !week) { - parsed->error = PARSER_INVALID_DATE; - - return NULL; - } - - if (parsed->month > 12) { - parsed->error = PARSER_INVALID_MONTH; - - return NULL; - } - - if (parsed->day > DAYS_PER_MONTHS[leap][parsed->month]) { - parsed->error = PARSER_INVALID_DAY_FOR_MONTH; - - return NULL; - } - - separators = 0; - if (*c == 'T' || *c == ' ') { - if (parsed->ambiguous) { - parsed->error = PARSER_INVALID_DATE; - - return NULL; - } - - // We have time so we have a datetime - parsed->is_datetime = 1; - parsed->is_date = 0; - - c++; - - // Grabbing time information - i = 0; - while (*c != '\0' && *c != '.' && *c != ',' && *c != 'Z' && *c != '+' && *c != '-') { - if (*c == ':') { - separators++; - c++; - continue; - } - - if (!(*c >= '0' && *c <='9')) { - parsed->error = PARSER_INVALID_TIME; - - return NULL; - } - - time = 10 * time + *c++ - '0'; - i++; - } - - switch (i) { - case 2: - // Hours only - if (separators > 0) { - // Extraneous separators - parsed->error = PARSER_INVALID_TIME; - - return NULL; - } - - parsed->hour = time; - break; - case 4: - // Hours and minutes - if (separators > 1) { - // Extraneous separators - parsed->error = PARSER_INVALID_TIME; - - return NULL; - } - - parsed->hour = time / 100; - parsed->minute = time % 100; - break; - case 6: - // Hours, minutes and seconds - if (!(separators == 0 || separators == 2)) { - // We should have either two separators or none - parsed->error = PARSER_INVALID_TIME; - - return NULL; - } - - parsed->hour = time / 10000; - parsed->minute = time / 100 % 100; - parsed->second = time % 100; - break; - default: - // Any other case is wrong - parsed->error = PARSER_INVALID_TIME; - - return NULL; - } - - // Checks - if (parsed->hour > 23) { - parsed->error = PARSER_INVALID_HOUR; - - return NULL; - } - - if (parsed->minute > 59) { - parsed->error = PARSER_INVALID_MINUTE; - - return NULL; - } - - if (parsed->second > 59) { - parsed->error = PARSER_INVALID_SECOND; - - return NULL; - } - - // Subsecond - if (*c == '.' || *c == ',') { - c++; - - time = 0; - i = 0; - while (*c != '\0' && *c != 'Z' && *c != '+' && *c != '-') { - if (!(*c >= '0' && *c <='9')) { - parsed->error = PARSER_INVALID_SUBSECOND; - - return NULL; - } - - time = 10 * time + *c++ - '0'; - i++; - } - - // adjust to microseconds - if (i > 6) { - parsed->microsecond = time / pow(10, i - 6); - } else if (i <= 6) { - parsed->microsecond = time * pow(10, 6 - i); - } - } - - // Timezone - if (*c == 'Z') { - parsed->has_offset = 1; - parsed->tzname = "UTC"; - c++; - } else if (*c == '+' || *c == '-') { - tz_sign = 1; - if (*c == '-') { - tz_sign = -1; - } - - parsed->has_offset = 1; - c++; - - i = 0; - time = 0; - separators = 0; - while (*c != '\0') { - if (*c == ':') { - separators++; - c++; - continue; - } - - if (!(*c >= '0' && *c <= '9')) { - parsed->error = PARSER_INVALID_TZ_OFFSET; - - return NULL; - } - - time = 10 * time + *c++ - '0'; - i++; - } - - switch (i) { - case 2: - // hh Format - if (separators) { - // Extraneous separators - parsed->error = PARSER_INVALID_TZ_OFFSET; - - return NULL; - } - - parsed->offset = tz_sign * (time * 3600); - break; - case 4: - // hhmm Format - if (separators > 1) { - // Extraneous separators - parsed->error = PARSER_INVALID_TZ_OFFSET; - - return NULL; - } - - parsed->offset = tz_sign * ((time / 100 * 3600) + (time % 100 * 60)); - break; - default: - // Wrong format - parsed->error = PARSER_INVALID_TZ_OFFSET; - - return NULL; - } - } - } - - // At this point we should be at the end of the string - // If not, the string is invalid - if (*c != '\0') { - parsed->error = PARSER_INVALID_ISO8601; - - return NULL; - } - - return parsed; -} - - -Parsed* _parse_iso8601_duration(char *str, Parsed *parsed) { - char* c; - int value = 0; - int grabbed = 0; - int in_time = 0; - int in_fraction = 0; - int fraction_length = 0; - int has_fractional = 0; - int fraction = 0; - int has_ymd = 0; - int has_week = 0; - int has_month = 0; - int has_day = 0; - int has_hour = 0; - int has_minute = 0; - int has_second = 0; - - c = str; - - // Removing P operator - c++; - - parsed->is_duration = 1; - - for (; *c != '\0'; c++) { - switch (*c) { - case 'Y': - if (!grabbed || in_time || has_week || has_ymd) { - // No value grabbed - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (fraction) { - parsed->error = PARSER_INVALID_DURATION_FLOAT_YEAR_MONTH_NOT_SUPPORTED; - - return NULL; - } - - parsed->years = value; - - grabbed = 0; - value = 0; - fraction = 0; - in_fraction = 0; - has_ymd = 1; - - break; - case 'M': - if (!grabbed || has_week) { - // No value grabbed - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (in_time) { - if (has_second) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (has_fractional) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - parsed->minutes = value; - if (fraction) { - parsed->seconds = fraction * 6; - has_fractional = 1; - } - - has_minute = 1; - } else { - if (fraction) { - parsed->error = PARSER_INVALID_DURATION_FLOAT_YEAR_MONTH_NOT_SUPPORTED; - - return NULL; - } - - if (has_month || has_day) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - parsed->months = value; - has_ymd = 1; - has_month = 1; - } - - grabbed = 0; - value = 0; - fraction = 0; - in_fraction = 0; - - break; - case 'D': - if (!grabbed || in_time || has_week) { - // No value grabbed - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (has_day) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - parsed->days = value; - if (fraction) { - parsed->hours = fraction * 2.4; - has_fractional = 1; - } - - grabbed = 0; - value = 0; - fraction = 0; - in_fraction = 0; - has_ymd = 1; - has_day = 1; - - break; - case 'T': - if (grabbed) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - in_time = 1; - - break; - case 'H': - if (!grabbed || !in_time || has_week) { - // No value grabbed - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (has_hour || has_second || has_minute) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (has_fractional) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - parsed->hours = value; - if (fraction) { - parsed->minutes = fraction * 6; - has_fractional = 1; - } - - grabbed = 0; - value = 0; - fraction = 0; - in_fraction = 0; - has_hour = 1; - - break; - case 'S': - if (!grabbed || !in_time || has_week) { - // No value grabbed - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (has_second) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (has_fractional) { - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - if (fraction) { - parsed->seconds = value; - if (fraction_length > 6) { - parsed->microseconds = fraction / pow(10, fraction_length - 6); - } else { - parsed->microseconds = fraction * pow(10, 6 - fraction_length); - } - has_fractional = 1; - } else { - parsed->seconds = value; - } - - grabbed = 0; - value = 0; - fraction = 0; - in_fraction = 0; - has_second = 1; - - break; - case 'W': - if (!grabbed || in_time || has_ymd) { - // No value grabbed - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - parsed->weeks = value; - if (fraction) { - float days; - days = fraction * 0.7; - parsed->hours = (int) ((days - (int) days) * 24); - parsed->days = (int) days; - } - - grabbed = 0; - value = 0; - fraction = 0; - in_fraction = 0; - has_week = 1; - - break; - case '.': - if (!grabbed || has_fractional) { - // No value grabbed - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - in_fraction = 1; - - break; - case ',': - if (!grabbed || has_fractional) { - // No value grabbed - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - - in_fraction = 1; - - break; - default: - if (*c >= '0' && *c <='9') { - if (in_fraction) { - fraction = 10 * fraction + *c - '0'; - fraction_length++; - } else { - value = 10 * value + *c - '0'; - grabbed = 1; - } - break; - } - - parsed->error = PARSER_INVALID_DURATION; - - return NULL; - } - } - - return parsed; -} - - -PyObject* parse_iso8601(PyObject *self, PyObject *args) { - char* str; - PyObject *obj; - PyObject *tzinfo; - Parsed *parsed = new_parsed(); - - if (!PyArg_ParseTuple(args, "s", &str)) { - PyErr_SetString( - PyExc_ValueError, "Invalid parameters" - ); - free(parsed); - return NULL; - } - - if (*str == 'P') { - // Duration (or interval) - if (_parse_iso8601_duration(str, parsed) == NULL) { - PyErr_SetString( - PyExc_ValueError, PARSER_ERRORS[parsed->error] - ); - - free(parsed); - return NULL; - } - } else if (_parse_iso8601_datetime(str, parsed) == NULL) { - PyErr_SetString( - PyExc_ValueError, PARSER_ERRORS[parsed->error] - ); - - free(parsed); - return NULL; - } - - if (parsed->is_date) { - // Date only - if (parsed->ambiguous) { - // We can "safely" assume that the ambiguous - // date was actually a time in the form hhmmss - parsed->hour = parsed->year / 100; - parsed->minute = parsed->year % 100; - parsed->second = parsed->month; - - obj = PyDateTimeAPI->Time_FromTime( - parsed->hour, parsed->minute, parsed->second, parsed->microsecond, - Py_BuildValue(""), - PyDateTimeAPI->TimeType - ); - } else { - obj = PyDateTimeAPI->Date_FromDate( - parsed->year, parsed->month, parsed->day, - PyDateTimeAPI->DateType - ); - } - } else if (parsed->is_datetime) { - if (!parsed->has_offset) { - tzinfo = Py_BuildValue(""); - } else { - tzinfo = new_fixed_offset(parsed->offset, parsed->tzname); - } - - obj = PyDateTimeAPI->DateTime_FromDateAndTime( - parsed->year, - parsed->month, - parsed->day, - parsed->hour, - parsed->minute, - parsed->second, - parsed->microsecond, - tzinfo, - PyDateTimeAPI->DateTimeType - ); - - Py_DECREF(tzinfo); - } else if (parsed->is_duration) { - obj = new_duration( - parsed->years, parsed->months, parsed->weeks, parsed->days, - parsed->hours, parsed->minutes, parsed->seconds, parsed->microseconds - ); - } else { - free(parsed); - return NULL; - } - - free(parsed); - - return obj; -} - - -/* ------------------------------------------------------------------------- */ - -static PyMethodDef helpers_methods[] = { - { - "parse_iso8601", - (PyCFunction) parse_iso8601, - METH_VARARGS, - PyDoc_STR("Parses a ISO8601 string into a tuple.") - }, - {NULL} -}; - - -/* ------------------------------------------------------------------------- */ - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_iso8601", - NULL, - -1, - helpers_methods, - NULL, - NULL, - NULL, - NULL, -}; - -PyMODINIT_FUNC -PyInit__iso8601(void) -{ - PyObject *module; - - PyDateTime_IMPORT; - - module = PyModule_Create(&moduledef); - - if (module == NULL) - return NULL; - - // FixedOffset declaration - FixedOffset_type.tp_new = PyType_GenericNew; - FixedOffset_type.tp_base = PyDateTimeAPI->TZInfoType; - FixedOffset_type.tp_methods = FixedOffset_methods; - FixedOffset_type.tp_members = FixedOffset_members; - FixedOffset_type.tp_init = (initproc)FixedOffset_init; - - if (PyType_Ready(&FixedOffset_type) < 0) - return NULL; - - // Duration declaration - Duration_type.tp_new = PyType_GenericNew; - Duration_type.tp_members = Duration_members; - Duration_type.tp_init = (initproc)Duration_init; - - if (PyType_Ready(&Duration_type) < 0) - return NULL; - - Py_INCREF(&FixedOffset_type); - Py_INCREF(&Duration_type); - - PyModule_AddObject(module, "TZFixedOffset", (PyObject *)&FixedOffset_type); - PyModule_AddObject(module, "Duration", (PyObject *)&Duration_type); - - return module; -} diff --git a/pendulum/parsing/_iso8601.pyi b/pendulum/parsing/_iso8601.pyi deleted file mode 100644 index b9ce5d4..0000000 --- a/pendulum/parsing/_iso8601.pyi +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import annotations - -from datetime import date -from datetime import datetime -from datetime import time - -class Duration: - - years: int = 0 - months: int = 0 - weeks: int = 0 - days: int = 0 - remaining_days: int = 0 - hours: int = 0 - minutes: int = 0 - seconds: int = 0 - remaining_seconds: int = 0 - microseconds: int = 0 - -def parse_iso8601( - text: str, -) -> datetime | date | time | Duration: ... diff --git a/pendulum/parsing/exceptions/__init__.py b/pendulum/parsing/exceptions/__init__.py deleted file mode 100644 index 05195b5..0000000 --- a/pendulum/parsing/exceptions/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import annotations - - -class ParserError(ValueError): - - pass diff --git a/pendulum/parsing/iso8601.py b/pendulum/parsing/iso8601.py deleted file mode 100644 index 907cf13..0000000 --- a/pendulum/parsing/iso8601.py +++ /dev/null @@ -1,454 +0,0 @@ -from __future__ import annotations - -import datetime -import re - -from typing import cast - -from pendulum.constants import HOURS_PER_DAY -from pendulum.constants import MINUTES_PER_HOUR -from pendulum.constants import MONTHS_OFFSETS -from pendulum.constants import SECONDS_PER_MINUTE -from pendulum.duration import Duration -from pendulum.helpers import days_in_year -from pendulum.helpers import is_leap -from pendulum.helpers import is_long_year -from pendulum.helpers import week_day -from pendulum.parsing.exceptions import ParserError -from pendulum.tz.timezone import UTC -from pendulum.tz.timezone import FixedTimezone - -ISO8601_DT = re.compile( - # Date (optional) # noqa: E800 - "^" - "(?P<date>" - " (?P<classic>" # Classic date (YYYY-MM-DD) or ordinal (YYYY-DDD) - r" (?P<year>\d{4})" # Year - " (?P<monthday>" - r" (?P<monthsep>-)?(?P<month>\d{2})" # Month (optional) - r" ((?P<daysep>-)?(?P<day>\d{1,2}))?" # Day (optional) - " )?" - " )" - " |" - " (?P<isocalendar>" # Calendar date (2016-W05 or 2016-W05-5) - r" (?P<isoyear>\d{4})" # Year - " (?P<weeksep>-)?" # Separator (optional) - " W" # W separator - r" (?P<isoweek>\d{2})" # Week number - " (?P<weekdaysep>-)?" # Separator (optional) - r" (?P<isoweekday>\d)?" # Weekday (optional) - " )" - ")?" - # Time (optional) # noqa: E800 - "(?P<time>" - r" (?P<timesep>[T\ ])?" # Separator (T or space) - r" (?P<hour>\d{1,2})(?P<minsep>:)?(?P<minute>\d{1,2})?(?P<secsep>:)?(?P<second>\d{1,2})?" # HH:mm:ss (optional mm and ss) - # Subsecond part (optional) - " (?P<subsecondsection>" - " (?:[.,])" # Subsecond separator (optional) - r" (?P<subsecond>\d{1,9})" # Subsecond - " )?" - # Timezone offset - " (?P<tz>" - r" (?:[-+])\d{2}:?(?:\d{2})?|Z" # Offset (+HH:mm or +HHmm or +HH or Z) - " )?" - ")?" - "$", - re.VERBOSE, -) - -ISO8601_DURATION = re.compile( - "^P" # Duration P indicator - # Years, months and days (optional) # noqa: E800 - "(?P<w>" - r" (?P<weeks>\d+(?:[.,]\d+)?W)" - ")?" - "(?P<ymd>" - r" (?P<years>\d+(?:[.,]\d+)?Y)?" - r" (?P<months>\d+(?:[.,]\d+)?M)?" - r" (?P<days>\d+(?:[.,]\d+)?D)?" - ")?" - "(?P<hms>" - " (?P<timesep>T)" # Separator (T) - r" (?P<hours>\d+(?:[.,]\d+)?H)?" - r" (?P<minutes>\d+(?:[.,]\d+)?M)?" - r" (?P<seconds>\d+(?:[.,]\d+)?S)?" - ")?" - "$", - re.VERBOSE, -) - - -def parse_iso8601( - text: str, -) -> datetime.datetime | datetime.date | datetime.time | Duration: - """ - ISO 8601 compliant parser. - - :param text: The string to parse - :type text: str - - :rtype: datetime.datetime or datetime.time or datetime.date - """ - parsed = _parse_iso8601_duration(text) - if parsed is not None: - return parsed - - m = ISO8601_DT.match(text) - if not m: - raise ParserError("Invalid ISO 8601 string") - - ambiguous_date = False - is_date = False - is_time = False - year = 0 - month = 1 - day = 1 - minute = 0 - second = 0 - microsecond = 0 - tzinfo: FixedTimezone | None = None - - if m.group("date"): - # A date has been specified - is_date = True - - if m.group("isocalendar"): - # We have a ISO 8601 string defined - # by week number - if ( - m.group("weeksep") - and not m.group("weekdaysep") - and m.group("isoweekday") - ): - raise ParserError(f"Invalid date string: {text}") - - if not m.group("weeksep") and m.group("weekdaysep"): - raise ParserError(f"Invalid date string: {text}") - - try: - date = _get_iso_8601_week( - m.group("isoyear"), m.group("isoweek"), m.group("isoweekday") - ) - except ParserError: - raise - except ValueError: - raise ParserError(f"Invalid date string: {text}") - - year = date["year"] - month = date["month"] - day = date["day"] - else: - # We have a classic date representation - year = int(m.group("year")) - - if not m.group("monthday"): - # No month and day - month = 1 - day = 1 - else: - if m.group("month") and m.group("day"): - # Month and day - if not m.group("daysep") and len(m.group("day")) == 1: - # Ordinal day - ordinal = int(m.group("month") + m.group("day")) - leap = is_leap(year) - months_offsets = MONTHS_OFFSETS[leap] - - if ordinal > months_offsets[13]: - raise ParserError("Ordinal day is out of range") - - for i in range(1, 14): - if ordinal <= months_offsets[i]: - day = ordinal - months_offsets[i - 1] - month = i - 1 - - break - else: - month = int(m.group("month")) - day = int(m.group("day")) - else: - # Only month - if not m.group("monthsep"): - # The date looks like 201207 - # which is invalid for a date - # But it might be a time in the form hhmmss - ambiguous_date = True - - month = int(m.group("month")) - day = 1 - - if not m.group("time"): - # No time has been specified - if ambiguous_date: - # We can "safely" assume that the ambiguous date - # was actually a time in the form hhmmss - hhmmss = f"{str(year)}{str(month):0>2}" - - return datetime.time(int(hhmmss[:2]), int(hhmmss[2:4]), int(hhmmss[4:])) - - return datetime.date(year, month, day) - - if ambiguous_date: - raise ParserError(f"Invalid date string: {text}") - - if is_date and not m.group("timesep"): - raise ParserError(f"Invalid date string: {text}") - - if not is_date: - is_time = True - - # Grabbing hh:mm:ss - hour = int(m.group("hour")) - minsep = m.group("minsep") - - if m.group("minute"): - minute = int(m.group("minute")) - elif minsep: - raise ParserError("Invalid ISO 8601 time part") - - secsep = m.group("secsep") - if secsep and not minsep and m.group("minute"): - # minute/second separator but no hour/minute separator - raise ParserError("Invalid ISO 8601 time part") - - if m.group("second"): - if not secsep and minsep: - # No minute/second separator but hour/minute separator - raise ParserError("Invalid ISO 8601 time part") - - second = int(m.group("second")) - elif secsep: - raise ParserError("Invalid ISO 8601 time part") - - # Grabbing subseconds, if any - if m.group("subsecondsection"): - # Limiting to 6 chars - subsecond = m.group("subsecond")[:6] - - microsecond = int(f"{subsecond:0<6}") - - # Grabbing timezone, if any - tz = m.group("tz") - if tz: - if tz == "Z": - tzinfo = UTC - else: - negative = bool(tz.startswith("-")) - tz = tz[1:] - if ":" not in tz: - if len(tz) == 2: - tz = f"{tz}00" - - off_hour = tz[0:2] - off_minute = tz[2:4] - else: - off_hour, off_minute = tz.split(":") - - offset = ((int(off_hour) * 60) + int(off_minute)) * 60 - - if negative: - offset = -1 * offset - - tzinfo = FixedTimezone(offset) - - if is_time: - return datetime.time(hour, minute, second, microsecond) - - return datetime.datetime( - year, month, day, hour, minute, second, microsecond, tzinfo=tzinfo - ) - - -def _parse_iso8601_duration(text: str, **options: str) -> Duration | None: - m = ISO8601_DURATION.match(text) - if not m: - return None - - years = 0 - months = 0 - weeks = 0 - days: int | float = 0 - hours: int | float = 0 - minutes: int | float = 0 - seconds: int | float = 0 - microseconds: int | float = 0 - fractional = False - - _days: str | float - _hour: str | int | None - _minutes: str | int | None - _seconds: str | int | None - if m.group("w"): - # Weeks - if m.group("ymd") or m.group("hms"): - # Specifying anything more than weeks is not supported - raise ParserError("Invalid duration string") - - _weeks = m.group("weeks") - if not _weeks: - raise ParserError("Invalid duration string") - - _weeks = _weeks.replace(",", ".").replace("W", "") - if "." in _weeks: - _weeks, portion = _weeks.split(".") - weeks = int(_weeks) - _days = int(portion) / 10 * 7 - days, hours = int(_days // 1), int(_days % 1 * HOURS_PER_DAY) - else: - weeks = int(_weeks) - - if m.group("ymd"): - # Years, months and/or days - _years = m.group("years") - _months = m.group("months") - _days = m.group("days") - - # Checking order - years_start = m.start("years") if _years else -3 - months_start = m.start("months") if _months else years_start + 1 - days_start = m.start("days") if _days else months_start + 1 - - # Check correct order - if not (years_start < months_start < days_start): - raise ParserError("Invalid duration") - - if _years: - _years = _years.replace(",", ".").replace("Y", "") - if "." in _years: - raise ParserError("Float years in duration are not supported") - else: - years = int(_years) - - if _months: - if fractional: - raise ParserError("Invalid duration") - - _months = _months.replace(",", ".").replace("M", "") - if "." in _months: - raise ParserError("Float months in duration are not supported") - else: - months = int(_months) - - if _days: - if fractional: - raise ParserError("Invalid duration") - - _days = _days.replace(",", ".").replace("D", "") - - if "." in _days: - fractional = True - - _days, _hours = _days.split(".") - days = int(_days) - hours = int(_hours) / 10 * HOURS_PER_DAY - else: - days = int(_days) - - if m.group("hms"): - # Hours, minutes and/or seconds - _hours = m.group("hours") or 0 - _minutes = m.group("minutes") or 0 - _seconds = m.group("seconds") or 0 - - # Checking order - hours_start = m.start("hours") if _hours else -3 - minutes_start = m.start("minutes") if _minutes else hours_start + 1 - seconds_start = m.start("seconds") if _seconds else minutes_start + 1 - - # Check correct order - if not (hours_start < minutes_start < seconds_start): - raise ParserError("Invalid duration") - - if _hours: - if fractional: - raise ParserError("Invalid duration") - - _hours = cast(str, _hours).replace(",", ".").replace("H", "") - - if "." in _hours: - fractional = True - - _hours, _mins = _hours.split(".") - hours += int(_hours) - minutes += int(_mins) / 10 * MINUTES_PER_HOUR - else: - hours += int(_hours) - - if _minutes: - if fractional: - raise ParserError("Invalid duration") - - _minutes = cast(str, _minutes).replace(",", ".").replace("M", "") - - if "." in _minutes: - fractional = True - - _minutes, _secs = _minutes.split(".") - minutes += int(_minutes) - seconds += int(_secs) / 10 * SECONDS_PER_MINUTE - else: - minutes += int(_minutes) - - if _seconds: - if fractional: - raise ParserError("Invalid duration") - - _seconds = cast(str, _seconds).replace(",", ".").replace("S", "") - - if "." in _seconds: - _seconds, _microseconds = _seconds.split(".") - seconds += int(_seconds) - microseconds += int(f"{_microseconds[:6]:0<6}") - else: - seconds += int(_seconds) - - return Duration( - years=years, - months=months, - weeks=weeks, - days=days, - hours=hours, - minutes=minutes, - seconds=seconds, - microseconds=microseconds, - ) - - -def _get_iso_8601_week( - year: int | str, week: int | str, weekday: int | str -) -> dict[str, int]: - if not weekday: - weekday = 1 - else: - weekday = int(weekday) - - year = int(year) - week = int(week) - - if week > 53 or week > 52 and not is_long_year(year): - raise ParserError("Invalid week for week date") - - if weekday > 7: - raise ParserError("Invalid weekday for week date") - - # We can't rely on strptime directly here since - # it does not support ISO week date - ordinal = week * 7 + weekday - (week_day(year, 1, 4) + 3) - - if ordinal < 1: - # Previous year - ordinal += days_in_year(year - 1) - year -= 1 - - if ordinal > days_in_year(year): - # Next year - ordinal -= days_in_year(year) - year += 1 - - fmt = "%Y-%j" - string = f"{year}-{ordinal}" - - dt = datetime.datetime.strptime(string, fmt) - - return {"year": dt.year, "month": dt.month, "day": dt.day} diff --git a/pendulum/py.typed b/pendulum/py.typed deleted file mode 100644 index e69de29..0000000 --- a/pendulum/py.typed +++ /dev/null diff --git a/pendulum/testing/__init__.py b/pendulum/testing/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/testing/__init__.py +++ /dev/null diff --git a/pendulum/testing/traveller.py b/pendulum/testing/traveller.py deleted file mode 100644 index 3c1d885..0000000 --- a/pendulum/testing/traveller.py +++ /dev/null @@ -1,139 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING -from typing import cast - -from pendulum.datetime import DateTime -from pendulum.utils._compat import PYPY - -if TYPE_CHECKING: - from types import TracebackType - - -class BaseTraveller: - def __init__(self, datetime_class: type[DateTime] = DateTime) -> None: - self._datetime_class: type[DateTime] = datetime_class - - def freeze(self: BaseTraveller) -> BaseTraveller: - raise NotImplementedError() - - def travel_back(self: BaseTraveller) -> BaseTraveller: - raise NotImplementedError() - - def travel( - self, - years: int = 0, - months: int = 0, - weeks: int = 0, - days: int = 0, - hours: int = 0, - minutes: int = 0, - seconds: int = 0, - microseconds: int = 0, - ) -> BaseTraveller: - raise NotImplementedError() - - def travel_to(self, dt: DateTime) -> BaseTraveller: - raise NotImplementedError() - - -if not PYPY: - import time_machine - - class Traveller(BaseTraveller): - def __init__(self, datetime_class: type[DateTime] = DateTime) -> None: - super().__init__(datetime_class) - - self._started: bool = False - self._traveller: time_machine.travel | None = None - self._coordinates: time_machine.Coordinates | None = None - - def freeze(self) -> Traveller: - if self._started: - cast(time_machine.Coordinates, self._coordinates).move_to( - self._datetime_class.now(), tick=False - ) - else: - self._start(freeze=True) - - return self - - def travel_back(self) -> Traveller: - if not self._started: - return self - - cast(time_machine.travel, self._traveller).stop() - self._coordinates = None - self._traveller = None - self._started = False - - return self - - def travel( - self, - years: int = 0, - months: int = 0, - weeks: int = 0, - days: int = 0, - hours: int = 0, - minutes: int = 0, - seconds: int = 0, - microseconds: int = 0, - *, - freeze: bool = False, - ) -> Traveller: - self._start(freeze=freeze) - - cast(time_machine.Coordinates, self._coordinates).move_to( - self._datetime_class.now().add( - years=years, - months=months, - weeks=weeks, - days=days, - hours=hours, - minutes=minutes, - seconds=seconds, - microseconds=microseconds, - ) - ) - - return self - - def travel_to(self, dt: DateTime, *, freeze: bool = False) -> Traveller: - self._start(freeze=freeze) - - cast(time_machine.Coordinates, self._coordinates).move_to(dt) - - return self - - def _start(self, freeze: bool = False) -> None: - if self._started: - return - - if not self._traveller: - self._traveller = time_machine.travel( - self._datetime_class.now(), tick=not freeze - ) - - self._coordinates = self._traveller.start() - - self._started = True - - def __enter__(self) -> Traveller: - self._start() - - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType, - ) -> None: - self.travel_back() - -else: - - class Traveller(BaseTraveller): # type: ignore[no-redef] - - ... diff --git a/pendulum/time.py b/pendulum/time.py deleted file mode 100644 index f979e25..0000000 --- a/pendulum/time.py +++ /dev/null @@ -1,303 +0,0 @@ -from __future__ import annotations - -import datetime - -from datetime import time -from datetime import timedelta -from typing import TYPE_CHECKING -from typing import Optional -from typing import cast -from typing import overload - -import pendulum - -from pendulum.constants import SECS_PER_HOUR -from pendulum.constants import SECS_PER_MIN -from pendulum.constants import USECS_PER_SEC -from pendulum.duration import AbsoluteDuration -from pendulum.duration import Duration -from pendulum.mixins.default import FormattableMixin - -if TYPE_CHECKING: - from typing import Literal - - -class Time(FormattableMixin, time): - """ - Represents a time instance as hour, minute, second, microsecond. - """ - - # String formatting - def __repr__(self) -> str: - us = "" - if self.microsecond: - us = f", {self.microsecond}" - - tzinfo = "" - if self.tzinfo: - tzinfo = f", tzinfo={repr(self.tzinfo)}" - - return ( - f"{self.__class__.__name__}" - f"({self.hour}, {self.minute}, {self.second}{us}{tzinfo})" - ) - - # Comparisons - - def closest(self, dt1: Time | time, dt2: Time | time) -> Time: - """ - Get the closest time from the instance. - """ - dt1 = self.__class__(dt1.hour, dt1.minute, dt1.second, dt1.microsecond) - dt2 = self.__class__(dt2.hour, dt2.minute, dt2.second, dt2.microsecond) - - if self.diff(dt1).in_seconds() < self.diff(dt2).in_seconds(): - return dt1 - - return dt2 - - def farthest(self, dt1: Time | time, dt2: Time | time) -> Time: - """ - Get the farthest time from the instance. - """ - dt1 = self.__class__(dt1.hour, dt1.minute, dt1.second, dt1.microsecond) - dt2 = self.__class__(dt2.hour, dt2.minute, dt2.second, dt2.microsecond) - - if self.diff(dt1).in_seconds() > self.diff(dt2).in_seconds(): - return dt1 - - return dt2 - - # ADDITIONS AND SUBSTRACTIONS - - def add( - self, hours: int = 0, minutes: int = 0, seconds: int = 0, microseconds: int = 0 - ) -> Time: - """ - Add duration to the instance. - - :param hours: The number of hours - :param minutes: The number of minutes - :param seconds: The number of seconds - :param microseconds: The number of microseconds - """ - from pendulum.datetime import DateTime - - return ( - DateTime.EPOCH.at(self.hour, self.minute, self.second, self.microsecond) - .add( - hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds - ) - .time() - ) - - def subtract( - self, hours: int = 0, minutes: int = 0, seconds: int = 0, microseconds: int = 0 - ) -> Time: - """ - Add duration to the instance. - - :param hours: The number of hours - :type hours: int - - :param minutes: The number of minutes - :type minutes: int - - :param seconds: The number of seconds - :type seconds: int - - :param microseconds: The number of microseconds - :type microseconds: int - - :rtype: Time - """ - from pendulum.datetime import DateTime - - return ( - DateTime.EPOCH.at(self.hour, self.minute, self.second, self.microsecond) - .subtract( - hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds - ) - .time() - ) - - def add_timedelta(self, delta: datetime.timedelta) -> Time: - """ - Add timedelta duration to the instance. - - :param delta: The timedelta instance - """ - if delta.days: - raise TypeError("Cannot add timedelta with days to Time.") - - return self.add(seconds=delta.seconds, microseconds=delta.microseconds) - - def subtract_timedelta(self, delta: datetime.timedelta) -> Time: - """ - Remove timedelta duration from the instance. - - :param delta: The timedelta instance - """ - if delta.days: - raise TypeError("Cannot subtract timedelta with days to Time.") - - return self.subtract(seconds=delta.seconds, microseconds=delta.microseconds) - - def __add__(self, other: datetime.timedelta) -> Time: - if not isinstance(other, timedelta): - return NotImplemented - - return self.add_timedelta(other) - - @overload - def __sub__(self, other: time) -> pendulum.Duration: - ... - - @overload - def __sub__(self, other: datetime.timedelta) -> Time: - ... - - def __sub__(self, other: time | datetime.timedelta) -> pendulum.Duration | Time: - if not isinstance(other, (Time, time, timedelta)): - return NotImplemented - - if isinstance(other, timedelta): - return self.subtract_timedelta(other) - - if isinstance(other, time): - if other.tzinfo is not None: - raise TypeError("Cannot subtract aware times to or from Time.") - - other = self.__class__( - other.hour, other.minute, other.second, other.microsecond - ) - - return other.diff(self, False) - - @overload - def __rsub__(self, other: time) -> pendulum.Duration: - ... - - @overload - def __rsub__(self, other: datetime.timedelta) -> Time: - ... - - def __rsub__(self, other: time | datetime.timedelta) -> pendulum.Duration | Time: - if not isinstance(other, (Time, time)): - return NotImplemented - - if isinstance(other, time): - if other.tzinfo is not None: - raise TypeError("Cannot subtract aware times to or from Time.") - - other = self.__class__( - other.hour, other.minute, other.second, other.microsecond - ) - - return other.__sub__(self) - - # DIFFERENCES - - def diff(self, dt: time | None = None, abs: bool = True) -> Duration: - """ - Returns the difference between two Time objects as an Duration. - - :param dt: The time to subtract from - :param abs: Whether to return an absolute duration or not - """ - if dt is None: - dt = pendulum.now().time() - else: - dt = self.__class__(dt.hour, dt.minute, dt.second, dt.microsecond) - - us1 = ( - self.hour * SECS_PER_HOUR + self.minute * SECS_PER_MIN + self.second - ) * USECS_PER_SEC - - us2 = ( - dt.hour * SECS_PER_HOUR + dt.minute * SECS_PER_MIN + dt.second - ) * USECS_PER_SEC - - klass = Duration - if abs: - klass = AbsoluteDuration - - return klass(microseconds=us2 - us1) - - def diff_for_humans( - self, - other: time | None = None, - absolute: bool = False, - locale: str | None = None, - ) -> str: - """ - Get the difference in a human readable format in the current locale. - - :param dt: The time to subtract from - :param absolute: removes time difference modifiers ago, after, etc - :param locale: The locale to use for localization - """ - is_now = other is None - - if is_now: - other = pendulum.now().time() - - diff = self.diff(other) - - return pendulum.format_diff(diff, is_now, absolute, locale) - - # Compatibility methods - - def replace( - self, - hour: int | None = None, - minute: int | None = None, - second: int | None = None, - microsecond: int | None = None, - tzinfo: bool | datetime.tzinfo | Literal[True] | None = True, - fold: int = 0, - ) -> Time: - if tzinfo is True: - tzinfo = self.tzinfo - - hour = hour if hour is not None else self.hour - minute = minute if minute is not None else self.minute - second = second if second is not None else self.second - microsecond = microsecond if microsecond is not None else self.microsecond - - t = super().replace( - hour, - minute, - second, - microsecond, - tzinfo=cast(Optional[datetime.tzinfo], tzinfo), - fold=fold, - ) - return self.__class__( - t.hour, t.minute, t.second, t.microsecond, tzinfo=t.tzinfo - ) - - def __getnewargs__(self) -> tuple[Time]: - return (self,) - - def _get_state( - self, protocol: int = 3 - ) -> tuple[int, int, int, int, datetime.tzinfo | None]: - tz = self.tzinfo - - return self.hour, self.minute, self.second, self.microsecond, tz - - def __reduce__( - self, - ) -> tuple[type[Time], tuple[int, int, int, int, datetime.tzinfo | None]]: - return self.__reduce_ex__(2) - - def __reduce_ex__( # type: ignore[override] - self, protocol: int - ) -> tuple[type[Time], tuple[int, int, int, int, datetime.tzinfo | None]]: - return self.__class__, self._get_state(protocol) - - -Time.min = Time(0, 0, 0) -Time.max = Time(23, 59, 59, 999999) -Time.resolution = Duration(microseconds=1) diff --git a/pendulum/tz/__init__.py b/pendulum/tz/__init__.py deleted file mode 100644 index 45c9855..0000000 --- a/pendulum/tz/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -from __future__ import annotations - -import sys - -from pendulum.tz.local_timezone import get_local_timezone -from pendulum.tz.local_timezone import set_local_timezone -from pendulum.tz.local_timezone import test_local_timezone -from pendulum.tz.timezone import UTC -from pendulum.tz.timezone import FixedTimezone -from pendulum.tz.timezone import Timezone - -if sys.version_info >= (3, 9): - from importlib import resources -else: - import importlib_resources as resources - -PRE_TRANSITION = "pre" -POST_TRANSITION = "post" -TRANSITION_ERROR = "error" - -_timezones = None - -_tz_cache: dict[int, FixedTimezone] = {} - - -def timezones() -> tuple[str, ...]: - global _timezones - - if _timezones is None: - with resources.files("tzdata").joinpath("zones").open() as f: - _timezones = tuple(tz.strip() for tz in f.readlines()) - - return _timezones - - -def timezone(name: str | int) -> Timezone | FixedTimezone: - """ - Return a Timezone instance given its name. - """ - if isinstance(name, int): - return fixed_timezone(name) - - if name.lower() == "utc": - return UTC - - return Timezone(name) - - -def fixed_timezone(offset: int) -> FixedTimezone: - """ - Return a Timezone instance given its offset in seconds. - """ - if offset in _tz_cache: - return _tz_cache[offset] - - tz = FixedTimezone(offset) - _tz_cache[offset] = tz - - return tz - - -def local_timezone() -> Timezone | FixedTimezone: - """ - Return the local timezone. - """ - return get_local_timezone() - - -__all__ = [ - "UTC", - "Timezone", - "FixedTimezone", - "set_local_timezone", - "get_local_timezone", - "test_local_timezone", - "timezone", - "fixed_timezone", - "local_timezone", - "timezones", -] diff --git a/pendulum/tz/data/__init__.py b/pendulum/tz/data/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/tz/data/__init__.py +++ /dev/null diff --git a/pendulum/tz/data/windows.py b/pendulum/tz/data/windows.py deleted file mode 100644 index 65aa6c3..0000000 --- a/pendulum/tz/data/windows.py +++ /dev/null @@ -1,139 +0,0 @@ -from __future__ import annotations - -windows_timezones = { - "AUS Central Standard Time": "Australia/Darwin", - "AUS Eastern Standard Time": "Australia/Sydney", - "Afghanistan Standard Time": "Asia/Kabul", - "Alaskan Standard Time": "America/Anchorage", - "Aleutian Standard Time": "America/Adak", - "Altai Standard Time": "Asia/Barnaul", - "Arab Standard Time": "Asia/Riyadh", - "Arabian Standard Time": "Asia/Dubai", - "Arabic Standard Time": "Asia/Baghdad", - "Argentina Standard Time": "America/Buenos_Aires", - "Astrakhan Standard Time": "Europe/Astrakhan", - "Atlantic Standard Time": "America/Halifax", - "Aus Central W. Standard Time": "Australia/Eucla", - "Azerbaijan Standard Time": "Asia/Baku", - "Azores Standard Time": "Atlantic/Azores", - "Bahia Standard Time": "America/Bahia", - "Bangladesh Standard Time": "Asia/Dhaka", - "Belarus Standard Time": "Europe/Minsk", - "Bougainville Standard Time": "Pacific/Bougainville", - "Canada Central Standard Time": "America/Regina", - "Cape Verde Standard Time": "Atlantic/Cape_Verde", - "Caucasus Standard Time": "Asia/Yerevan", - "Cen. Australia Standard Time": "Australia/Adelaide", - "Central America Standard Time": "America/Guatemala", - "Central Asia Standard Time": "Asia/Almaty", - "Central Brazilian Standard Time": "America/Cuiaba", - "Central Europe Standard Time": "Europe/Budapest", - "Central European Standard Time": "Europe/Warsaw", - "Central Pacific Standard Time": "Pacific/Guadalcanal", - "Central Standard Time": "America/Chicago", - "Central Standard Time (Mexico)": "America/Mexico_City", - "Chatham Islands Standard Time": "Pacific/Chatham", - "China Standard Time": "Asia/Shanghai", - "Cuba Standard Time": "America/Havana", - "Dateline Standard Time": "Etc/GMT+12", - "E. Africa Standard Time": "Africa/Nairobi", - "E. Australia Standard Time": "Australia/Brisbane", - "E. Europe Standard Time": "Europe/Chisinau", - "E. South America Standard Time": "America/Sao_Paulo", - "Easter Island Standard Time": "Pacific/Easter", - "Eastern Standard Time": "America/New_York", - "Eastern Standard Time (Mexico)": "America/Cancun", - "Egypt Standard Time": "Africa/Cairo", - "Ekaterinburg Standard Time": "Asia/Yekaterinburg", - "FLE Standard Time": "Europe/Kiev", - "Fiji Standard Time": "Pacific/Fiji", - "GMT Standard Time": "Europe/London", - "GTB Standard Time": "Europe/Bucharest", - "Georgian Standard Time": "Asia/Tbilisi", - "Greenland Standard Time": "America/Godthab", - "Greenwich Standard Time": "Atlantic/Reykjavik", - "Haiti Standard Time": "America/Port-au-Prince", - "Hawaiian Standard Time": "Pacific/Honolulu", - "India Standard Time": "Asia/Calcutta", - "Iran Standard Time": "Asia/Tehran", - "Israel Standard Time": "Asia/Jerusalem", - "Jordan Standard Time": "Asia/Amman", - "Kaliningrad Standard Time": "Europe/Kaliningrad", - "Korea Standard Time": "Asia/Seoul", - "Libya Standard Time": "Africa/Tripoli", - "Line Islands Standard Time": "Pacific/Kiritimati", - "Lord Howe Standard Time": "Australia/Lord_Howe", - "Magadan Standard Time": "Asia/Magadan", - "Magallanes Standard Time": "America/Punta_Arenas", - "Marquesas Standard Time": "Pacific/Marquesas", - "Mauritius Standard Time": "Indian/Mauritius", - "Middle East Standard Time": "Asia/Beirut", - "Montevideo Standard Time": "America/Montevideo", - "Morocco Standard Time": "Africa/Casablanca", - "Mountain Standard Time": "America/Denver", - "Mountain Standard Time (Mexico)": "America/Chihuahua", - "Myanmar Standard Time": "Asia/Rangoon", - "N. Central Asia Standard Time": "Asia/Novosibirsk", - "Namibia Standard Time": "Africa/Windhoek", - "Nepal Standard Time": "Asia/Katmandu", - "New Zealand Standard Time": "Pacific/Auckland", - "Newfoundland Standard Time": "America/St_Johns", - "Norfolk Standard Time": "Pacific/Norfolk", - "North Asia East Standard Time": "Asia/Irkutsk", - "North Asia Standard Time": "Asia/Krasnoyarsk", - "North Korea Standard Time": "Asia/Pyongyang", - "Omsk Standard Time": "Asia/Omsk", - "Pacific SA Standard Time": "America/Santiago", - "Pacific Standard Time": "America/Los_Angeles", - "Pacific Standard Time (Mexico)": "America/Tijuana", - "Pakistan Standard Time": "Asia/Karachi", - "Paraguay Standard Time": "America/Asuncion", - "Romance Standard Time": "Europe/Paris", - "Russia Time Zone 10": "Asia/Srednekolymsk", - "Russia Time Zone 11": "Asia/Kamchatka", - "Russia Time Zone 3": "Europe/Samara", - "Russian Standard Time": "Europe/Moscow", - "SA Eastern Standard Time": "America/Cayenne", - "SA Pacific Standard Time": "America/Bogota", - "SA Western Standard Time": "America/La_Paz", - "SE Asia Standard Time": "Asia/Bangkok", - "Saint Pierre Standard Time": "America/Miquelon", - "Sakhalin Standard Time": "Asia/Sakhalin", - "Samoa Standard Time": "Pacific/Apia", - "Sao Tome Standard Time": "Africa/Sao_Tome", - "Saratov Standard Time": "Europe/Saratov", - "Singapore Standard Time": "Asia/Singapore", - "South Africa Standard Time": "Africa/Johannesburg", - "Sri Lanka Standard Time": "Asia/Colombo", - "Sudan Standard Time": "Africa/Khartoum", - "Syria Standard Time": "Asia/Damascus", - "Taipei Standard Time": "Asia/Taipei", - "Tasmania Standard Time": "Australia/Hobart", - "Tocantins Standard Time": "America/Araguaina", - "Tokyo Standard Time": "Asia/Tokyo", - "Tomsk Standard Time": "Asia/Tomsk", - "Tonga Standard Time": "Pacific/Tongatapu", - "Transbaikal Standard Time": "Asia/Chita", - "Turkey Standard Time": "Europe/Istanbul", - "Turks And Caicos Standard Time": "America/Grand_Turk", - "US Eastern Standard Time": "America/Indianapolis", - "US Mountain Standard Time": "America/Phoenix", - "UTC": "Etc/GMT", - "UTC+12": "Etc/GMT-12", - "UTC+13": "Etc/GMT-13", - "UTC-02": "Etc/GMT+2", - "UTC-08": "Etc/GMT+8", - "UTC-09": "Etc/GMT+9", - "UTC-11": "Etc/GMT+11", - "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", - "Venezuela Standard Time": "America/Caracas", - "Vladivostok Standard Time": "Asia/Vladivostok", - "W. Australia Standard Time": "Australia/Perth", - "W. Central Africa Standard Time": "Africa/Lagos", - "W. Europe Standard Time": "Europe/Berlin", - "W. Mongolia Standard Time": "Asia/Hovd", - "West Asia Standard Time": "Asia/Tashkent", - "West Bank Standard Time": "Asia/Hebron", - "West Pacific Standard Time": "Pacific/Port_Moresby", - "Yakutsk Standard Time": "Asia/Yakutsk", -} diff --git a/pendulum/tz/exceptions.py b/pendulum/tz/exceptions.py deleted file mode 100644 index b8833ac..0000000 --- a/pendulum/tz/exceptions.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from datetime import datetime - - -class TimezoneError(ValueError): - pass - - -class InvalidTimezone(TimezoneError): - pass - - -class NonExistingTime(TimezoneError): - message = "The datetime {} does not exist." - - def __init__(self, dt: datetime) -> None: - message = self.message.format(dt) - - super().__init__(message) - - -class AmbiguousTime(TimezoneError): - message = "The datetime {} is ambiguous." - - def __init__(self, dt: datetime) -> None: - message = self.message.format(dt) - - super().__init__(message) diff --git a/pendulum/tz/local_timezone.py b/pendulum/tz/local_timezone.py deleted file mode 100644 index 41cf81b..0000000 --- a/pendulum/tz/local_timezone.py +++ /dev/null @@ -1,260 +0,0 @@ -from __future__ import annotations - -import contextlib -import os -import re -import sys - -from contextlib import contextmanager -from typing import Iterator -from typing import cast - -from pendulum.tz.exceptions import InvalidTimezone -from pendulum.tz.timezone import FixedTimezone -from pendulum.tz.timezone import Timezone - -if sys.platform == "win32": - try: - import _winreg as winreg - except (ImportError, AttributeError): - import winreg - -_mock_local_timezone = None -_local_timezone = None - - -def get_local_timezone() -> Timezone | FixedTimezone: - global _local_timezone - - if _mock_local_timezone is not None: - return _mock_local_timezone - - if _local_timezone is None: - tz = _get_system_timezone() - - _local_timezone = tz - - return _local_timezone - - -def set_local_timezone(mock: str | Timezone | None = None) -> None: - global _mock_local_timezone - - _mock_local_timezone = mock - - -@contextmanager -def test_local_timezone(mock: Timezone) -> Iterator[None]: - set_local_timezone(mock) - - yield - - set_local_timezone() - - -def _get_system_timezone() -> Timezone: - if sys.platform == "win32": - return _get_windows_timezone() - elif "darwin" in sys.platform: - return _get_darwin_timezone() - - return _get_unix_timezone() - - -if sys.platform == "win32": - - def _get_windows_timezone() -> Timezone: - from pendulum.tz.data.windows import windows_timezones - - # Windows is special. It has unique time zone names (in several - # meanings of the word) available, but unfortunately, they can be - # translated to the language of the operating system, so we need to - # do a backwards lookup, by going through all time zones and see which - # one matches. - handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - - tz_local_key_name = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" - localtz = winreg.OpenKey(handle, tz_local_key_name) - - timezone_info = {} - size = winreg.QueryInfoKey(localtz)[1] - for i in range(size): - data = winreg.EnumValue(localtz, i) - timezone_info[data[0]] = data[1] - - localtz.Close() - - if "TimeZoneKeyName" in timezone_info: - # Windows 7 (and Vista?) - - # For some reason this returns a string with loads of NUL bytes at - # least on some systems. I don't know if this is a bug somewhere, I - # just work around it. - tzkeyname = timezone_info["TimeZoneKeyName"].split("\x00", 1)[0] - else: - # Windows 2000 or XP - - # This is the localized name: - tzwin = timezone_info["StandardName"] - - # Open the list of timezones to look up the real name: - tz_key_name = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" - tzkey = winreg.OpenKey(handle, tz_key_name) - - # Now, match this value to Time Zone information - tzkeyname = None - for i in range(winreg.QueryInfoKey(tzkey)[0]): - subkey = winreg.EnumKey(tzkey, i) - sub = winreg.OpenKey(tzkey, subkey) - - info = {} - size = winreg.QueryInfoKey(sub)[1] - for i in range(size): - data = winreg.EnumValue(sub, i) - info[data[0]] = data[1] - - sub.Close() - with contextlib.suppress(KeyError): - # This timezone didn't have proper configuration. - # Ignore it. - if info["Std"] == tzwin: - tzkeyname = subkey - break - - tzkey.Close() - handle.Close() - - if tzkeyname is None: - raise LookupError("Can not find Windows timezone configuration") - - timezone = windows_timezones.get(tzkeyname) - if timezone is None: - # Nope, that didn't work. Try adding "Standard Time", - # it seems to work a lot of times: - timezone = windows_timezones.get(tzkeyname + " Standard Time") - - # Return what we have. - if timezone is None: - raise LookupError("Unable to find timezone " + tzkeyname) - - return Timezone(timezone) - -else: - - def _get_windows_timezone() -> Timezone: - ... - - -def _get_darwin_timezone() -> Timezone: - # link will be something like /usr/share/zoneinfo/America/Los_Angeles. - link = os.readlink("/etc/localtime") - tzname = link[link.rfind("zoneinfo/") + 9 :] - - return Timezone(tzname) - - -def _get_unix_timezone(_root: str = "/") -> Timezone: - tzenv = os.environ.get("TZ") - if tzenv: - with contextlib.suppress(ValueError): - return _tz_from_env(tzenv) - - # Now look for distribution specific configuration files - # that contain the timezone name. - tzpath = os.path.join(_root, "etc/timezone") - if os.path.isfile(tzpath): - with open(tzpath, "rb") as tzfile: - tzfile_data = tzfile.read() - - # Issue #3 was that /etc/timezone was a zoneinfo file. - # That's a misconfiguration, but we need to handle it gracefully: - if tzfile_data[:5] != b"TZif2": - etctz = tzfile_data.strip().decode() - # Get rid of host definitions and comments: - if " " in etctz: - etctz, dummy = etctz.split(" ", 1) - if "#" in etctz: - etctz, dummy = etctz.split("#", 1) - - return Timezone(etctz.replace(" ", "_")) - - # CentOS has a ZONE setting in /etc/sysconfig/clock, - # OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and - # Gentoo has a TIMEZONE setting in /etc/conf.d/clock - # We look through these files for a timezone: - zone_re = re.compile(r'\s*ZONE\s*=\s*"') - timezone_re = re.compile(r'\s*TIMEZONE\s*=\s*"') - end_re = re.compile('"') - - for filename in ("etc/sysconfig/clock", "etc/conf.d/clock"): - tzpath = os.path.join(_root, filename) - if not os.path.isfile(tzpath): - continue - - with open(tzpath) as tzfile: - data = tzfile.readlines() - - for line in data: - # Look for the ZONE= setting. - match = zone_re.match(line) - if match is None: - # No ZONE= setting. Look for the TIMEZONE= setting. - match = timezone_re.match(line) - - if match is not None: - # Some setting existed - line = line[match.end() :] - etctz = line[ - : cast( - re.Match, end_re.search(line) # type: ignore[type-arg] - ).start() - ] - - parts = list(reversed(etctz.replace(" ", "_").split(os.path.sep))) - tzpath_parts: list[str] = [] - while parts: - tzpath_parts.insert(0, parts.pop(0)) - - with contextlib.suppress(InvalidTimezone): - return Timezone(os.path.join(*tzpath_parts)) - - # systemd distributions use symlinks that include the zone name, - # see manpage of localtime(5) and timedatectl(1) - tzpath = os.path.join(_root, "etc", "localtime") - if os.path.isfile(tzpath) and os.path.islink(tzpath): - parts = list( - reversed(os.path.realpath(tzpath).replace(" ", "_").split(os.path.sep)) - ) - tzpath_parts: list[str] = [] # type: ignore[no-redef] - while parts: - tzpath_parts.insert(0, parts.pop(0)) - with contextlib.suppress(InvalidTimezone): - return Timezone(os.path.join(*tzpath_parts)) - - # No explicit setting existed. Use localtime - for filename in ("etc/localtime", "usr/local/etc/localtime"): - tzpath = os.path.join(_root, filename) - - if not os.path.isfile(tzpath): - continue - - with open(tzpath, "rb") as f: - return cast(Timezone, Timezone.from_file(f)) - - raise RuntimeError("Unable to find any timezone configuration") - - -def _tz_from_env(tzenv: str) -> Timezone: - if tzenv[0] == ":": - tzenv = tzenv[1:] - - # TZ specifies a file - if os.path.isfile(tzenv): - with open(tzenv, "rb") as f: - return cast(Timezone, Timezone.from_file(f)) - - # TZ specifies a zoneinfo zone. - try: - return Timezone(tzenv) - except ValueError: - raise diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py deleted file mode 100644 index f689004..0000000 --- a/pendulum/tz/timezone.py +++ /dev/null @@ -1,217 +0,0 @@ -from __future__ import annotations - -import datetime as datetime_ - -from abc import ABC -from abc import abstractmethod -from typing import cast - -from pendulum.tz.exceptions import AmbiguousTime -from pendulum.tz.exceptions import InvalidTimezone -from pendulum.tz.exceptions import NonExistingTime -from pendulum.utils._compat import zoneinfo - -POST_TRANSITION = "post" -PRE_TRANSITION = "pre" -TRANSITION_ERROR = "error" - - -class PendulumTimezone(ABC): - @property - @abstractmethod - def name(self) -> str: - raise NotImplementedError - - @abstractmethod - def convert( - self, dt: datetime_.datetime, raise_on_unknown_times: bool = False - ) -> datetime_.datetime: - raise NotImplementedError - - @abstractmethod - def datetime( - self, - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - second: int = 0, - microsecond: int = 0, - ) -> datetime_.datetime: - raise NotImplementedError - - -class Timezone(zoneinfo.ZoneInfo, PendulumTimezone): # type: ignore[misc] - """ - Represents a named timezone. - - The accepted names are those provided by the IANA time zone database. - - >>> from pendulum.tz.timezone import Timezone - >>> tz = Timezone('Europe/Paris') - """ - - def __new__(cls, key: str) -> Timezone: - try: - return cast(Timezone, super().__new__(cls, key)) - except zoneinfo.ZoneInfoNotFoundError: - raise InvalidTimezone(key) - - @property - def name(self) -> str: - return cast(str, self.key) - - def convert( - self, dt: datetime_.datetime, raise_on_unknown_times: bool = False - ) -> datetime_.datetime: - """ - Converts a datetime in the current timezone. - - If the datetime is naive, it will be normalized. - - >>> from datetime import datetime - >>> from pendulum import timezone - >>> paris = timezone('Europe/Paris') - >>> dt = datetime(2013, 3, 31, 2, 30, fold=1) - >>> in_paris = paris.convert(dt) - >>> in_paris.isoformat() - '2013-03-31T03:30:00+02:00' - - If the datetime is aware, it will be properly converted. - - >>> new_york = timezone('America/New_York') - >>> in_new_york = new_york.convert(in_paris) - >>> in_new_york.isoformat() - '2013-03-30T21:30:00-04:00' - """ - if dt.tzinfo is None: - offset_before = ( - self.utcoffset(dt.replace(fold=0)) if dt.fold else self.utcoffset(dt) - ) - offset_after = ( - self.utcoffset(dt) if dt.fold else self.utcoffset(dt.replace(fold=1)) - ) - - if offset_after > offset_before: - # Skipped time - if raise_on_unknown_times: - raise NonExistingTime(dt) - - dt += ( - (offset_after - offset_before) - if dt.fold - else (offset_before - offset_after) - ) - elif offset_before > offset_after and raise_on_unknown_times: - # Repeated time - raise AmbiguousTime(dt) - - return dt.replace(tzinfo=self) - - return dt.astimezone(self) - - def datetime( - self, - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - second: int = 0, - microsecond: int = 0, - ) -> datetime_.datetime: - """ - Return a normalized datetime for the current timezone. - """ - return self.convert( - datetime_.datetime( - year, month, day, hour, minute, second, microsecond, fold=1 - ) - ) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}('{self.name}')" - - -class FixedTimezone(datetime_.tzinfo, PendulumTimezone): - def __init__(self, offset: int, name: str | None = None) -> None: - sign = "-" if offset < 0 else "+" - - minutes = offset / 60 - hour, minute = divmod(abs(int(minutes)), 60) - - if not name: - name = f"{sign}{hour:02d}:{minute:02d}" - - self._name = name - self._offset = offset - self._utcoffset = datetime_.timedelta(seconds=offset) - - @property - def name(self) -> str: - return self._name - - def convert( - self, dt: datetime_.datetime, raise_on_unknown_times: bool = False - ) -> datetime_.datetime: - if dt.tzinfo is None: - return dt.__class__( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tzinfo=self, - fold=0, - ) - - return dt.astimezone(self) - - def datetime( - self, - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - second: int = 0, - microsecond: int = 0, - ) -> datetime_.datetime: - return self.convert( - datetime_.datetime( - year, month, day, hour, minute, second, microsecond, fold=1 - ) - ) - - @property - def offset(self) -> int: - return self._offset - - def utcoffset(self, dt: datetime_.datetime | None) -> datetime_.timedelta: - return self._utcoffset - - def dst(self, dt: datetime_.datetime | None) -> datetime_.timedelta: - return datetime_.timedelta() - - def fromutc(self, dt: datetime_.datetime) -> datetime_.datetime: - # Use the stdlib datetime's add method to avoid infinite recursion - return (datetime_.datetime.__add__(dt, self._utcoffset)).replace(tzinfo=self) - - def tzname(self, dt: datetime_.datetime | None) -> str | None: - return self._name - - def __getinitargs__(self) -> tuple[int, str]: - return self._offset, self._name - - def __repr__(self) -> str: - name = "" - if self._name: - name = f', name="{self._name}"' - - return f"{self.__class__.__name__}({self._offset}{name})" - - -UTC = Timezone("UTC") diff --git a/pendulum/utils/__init__.py b/pendulum/utils/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/pendulum/utils/__init__.py +++ /dev/null diff --git a/pendulum/utils/_compat.py b/pendulum/utils/_compat.py deleted file mode 100644 index 8f32f9e..0000000 --- a/pendulum/utils/_compat.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -import sys - -PYPY = hasattr(sys, "pypy_version_info") -PY38 = sys.version_info[:2] >= (3, 8) - -try: - from backports import zoneinfo -except ImportError: - import zoneinfo # type: ignore[no-redef] - -__all__ = ["zoneinfo"] |