PK!22pendulum/__init__.pyfrom __future__ import absolute_import import datetime as _datetime from typing import Union from .__version__ import __version__ # Types from .datetime import DateTime from .date import Date from .time import Time from .duration import Duration from .period import Period from .tz import timezone from .tz import PRE_TRANSITION, POST_TRANSITION, TRANSITION_ERROR from .tz.timezone import Timezone as _Timezone from .formatting import Formatter # Helpers from .helpers import ( test, set_test_now, has_test_now, get_test_now, set_locale, get_locale, locale, format_diff, week_starts_at, week_ends_at ) from .utils._compat import _HAS_FOLD from .tz import ( timezones, local_timezone, test_local_timezone, set_local_timezone, UTC ) from .parser import parse # Constants from .constants import ( MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY, YEARS_PER_CENTURY, YEARS_PER_DECADE, MONTHS_PER_YEAR, WEEKS_PER_YEAR, DAYS_PER_WEEK, HOURS_PER_DAY, MINUTES_PER_HOUR, SECONDS_PER_MINUTE, SECONDS_PER_HOUR, SECONDS_PER_DAY ) _TEST_NOW = None _LOCALE = 'en' _WEEK_STARTS_AT = MONDAY _WEEK_ENDS_AT = SUNDAY _formatter = Formatter() def _safe_timezone(obj): # type: (Union[str, int, float, _datetime.tzinfo]) -> _Timezone """ Creates a timezone instance from a string, Timezone, TimezoneInfo or integer offset. """ if isinstance(obj, _Timezone): 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): # pytz if hasattr(obj, 'localize'): obj = obj.zone else: offset = obj.utcoffset(None) if offset is None: offset = _datetime.timedelta(0) obj = int(offset.total_seconds()) return timezone(obj) # Public API def datetime(year, # type: int month, # type: int day, # type: int hour=0, # type: int minute=0, # type: int second=0, # type: int microsecond=0, # type: int tz=UTC, # type: Union[str, _Timezone] dst_rule=POST_TRANSITION, # type: str ): # type: (...) -> DateTime """ Creates a new DateTime instance from a specific date and time. """ if tz is not None: tz = _safe_timezone(tz) if not _HAS_FOLD: dt = naive( year, month, day, hour, minute, second, microsecond ) else: dt = _datetime.datetime( year, month, day, hour, minute, second, microsecond ) if tz is not None: dt = tz.convert(dt, dst_rule=dst_rule) return DateTime( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tzinfo=dt.tzinfo, fold=dt.fold ) def local(year, month, day, hour=0, minute=0, second=0, microsecond=0 ): # type: (int, int, int, int, int, int, int) -> DateTime """ Return a DateTime in the local timezone. """ return datetime(year, month, day, hour, minute, second, microsecond, tz=local_timezone()) def naive(year, month, day, hour=0, minute=0, second=0, microsecond=0 ): # type: (int, int, int, int, int, int, int) -> DateTime """ Return a naive DateTime. """ return DateTime( year, month, day, hour, minute, second, microsecond ) def date(year, month, day): # type: (int, int, int) -> Date """ Create a new Date instance. """ return Date(year, month, day) def time(hour, minute=0, second=0, microsecond=0 ): # type: (int, int, int, int) -> Time """ Create a new Time instance. """ return Time(hour, minute, second, microsecond) def instance(dt, # type: _datetime.datetime tz=UTC # type: Union[str, _Timezone, None] ): # type: (...) -> 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 # Checking for pytz/tzinfo if (isinstance(tz, _datetime.tzinfo) and not isinstance(tz, _Timezone)): # pytz if hasattr(tz, 'localize') and tz.zone: tz = tz.zone else: # We have no sure way to figure out # the timezone name, we fallback # on a fixed offset tz = tz.utcoffset(dt).total_seconds() / 3600 return datetime( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tz=tz ) def now(tz=None): # type: (Union[str, _Timezone, None]) -> DateTime """ Get a DateTime instance for the current date and time. """ if has_test_now(): test_instance = get_test_now() _tz = _safe_timezone(tz) if tz is not None and _tz != test_instance.timezone: test_instance = test_instance.in_tz(_tz) return test_instance 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 = _safe_timezone(tz) dt = tz.convert(dt) return instance(dt, tz) def today(tz='local'): # type: (Union[str, _Timezone]) -> DateTime """ Create a DateTime instance for today. """ return now(tz).start_of('day') def tomorrow(tz='local'): # type: (Union[str, _Timezone]) -> DateTime """ Create a DateTime instance for today. """ return today(tz).add(days=1) def yesterday(tz='local'): # type: (Union[str, _Timezone]) -> DateTime """ Create a DateTime instance for today. """ return today(tz).subtract(days=1) def from_format(string, # type: str fmt, # type: str tz=UTC, # type: Union[str, _Timezone] locale=None # type: Union[str, None] ): # type: (...) -> DateTime """ Creates a DateTime instance from a specific format. """ parts = _formatter.parse(string, fmt, now(), locale=locale) if parts['tz'] is None: parts['tz'] = tz return datetime(**parts) def from_timestamp(timestamp, # type: Union[int, float] tz=UTC # type: Union[str, _Timezone] ): # type: (...) -> 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=0, # type: float seconds=0, # type: float microseconds=0, # type: float milliseconds=0, # type: float minutes=0, # type: float hours=0, # type: float weeks=0, # type: float years=0, # type: float months=0 # type: float ): # type: (...) -> 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 period(start, # type: DateTime end, # type: DateTime absolute=False # type: bool ): # type: (...) -> Period """ Create a Period instance. """ return Period(start, end, absolute=absolute) PK!BApendulum/__version__.py__version__ = '2.0.3' PK! pendulum/_extensions/__init__.pyPK!vMxaapendulum/_extensions/_helpers.c/* ------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #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 /* ------------------------------------------------------------------------- */ 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, "name")) { // Pendulum timezone tz = PyUnicode_AsUTF8( PyObject_GetAttrString(tzinfo, "name") ); } else if (PyObject_HasAttrString(tzinfo, "zone")) { // pytz timezone tz = 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) { char repr[82] = {0}; sprintf( repr, "%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 ); return PyUnicode_FromString(repr); } /* * 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_FromLong(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; } PK!vnSS3pendulum/_extensions/_helpers.cpython-36m-darwin.so p(__TEXT00__text__TEXTpBp__stubs__TEXT)l)__stub_helper__TEXT * *__const__TEXT*P*__cstring__TEXT@,5@,__unwind_info__TEXTx/x/__DATA00__nl_symbol_ptr__DATA00__got__DATA0 0__la_symbol_ptr__DATA0000__data__DATA0p0__bss__DATA05H__LINKEDIT@ @"0@88@x@hBXDN PcczhM*zA?Zе9/%$ * 82/usr/lib/libSystem.B.dylib&0D()XDUH8HcHizHH?H%HiQHH?H']ff.UH1@u&HcHiQHH?HH%kd9t]H'iɐ19]UHSP1HcHi$IH ȉЍ))Ѹt81HcHi$IH ȉЍ))1H[]UH1)AAAAAHcHizII?H DHiQII?H DHcL!DA HcHi$IH ȉЍ))ѸE]DUH@u6HcHiQHH%HH?kdn9uH'iɐ)tm]fUH HcHiȫ*HH?H! I)LcIigfffHH?H")DimHcLizLH?I%AHiQHH?H'Ai2HcHigfffHH?H"DDȍD8]HHy(H;=t(UHH5H1HiAQA]1DUHG]fDUHSPH_(H;tLH5HFt H5H5H*tH5HHH[]HfH[]UHHH XHU1HHtEt+1H]HH8H5 1H]HcHiQHH?HH%kd9tGH]H'iɐ19@(H]f.UHSPH HU1HHt<]1JHHiȓ$IH э))Ѓu&WH H8H5J1H[]1HHiȓ$IH э))1@^H[]ffffff.UHHL HUHMLE1HLuDMDE1A)։HcHizHH?H HiQHH?H AMcH FDHcHiȓ$IH э))HHEH]HH8H5e1H]fUHHH HU1HH"tMEu6HcHiQHH%HH?kdn9uH'iɐ)tmH]H(H8H5g1H]ÐUHAWAVATSHH 4HU1HHH]sKDcSDSDKDCimH=:B<;6P*YlLcf: ,LcHcHiQHH?H%HcߍHcHiQHH?H'Hct1GH*H8H5i1fAIiQH%kd9uIiQH'iɐ9A HH)HLLH)HRIHkHH HMHNHpH!H8H5` 1 UD ]E HyH9t EHEH HHH#@)ADi2IH#DmD)ʉLcMizLH?I%AMiQLH?I'AAMHEP HH#<[)i2IH#})׃IHcHizHH?H'HiQHH?H%ljA)Ѻ+UEtA)AAH#A)I#AimAEEEDED"EAE<ALeUHEL= ExALL`(H L;%wt|H5 L. t4H5 L H% HDEHMHy(H;=5uOH5Y L DEtH5B L H DEHHMHy(H;=H5 H 1 DEDihQDhHExL`(L= L;%H5 LR t@H5 L9 HI IDEHMHy(H;=YuW{E1HExulH5q L DEtH5Z L H DEIHMHy(H;=t&H5 H 1 DEiHQHML9t1;UDAEHEPpxH DP!AA X"EEEL}Let ]EIcHiųH D iA)IcAHiH lj)k<)D)x'=|' E11E1E1L}LeQH xH5mHh HIE1L[A^]UHAWAVAUATSH(HL]L}LeLmLE1HUHMLELMĸHLASAWATAU H t2ECECECEĉCEȉC ẺC$EЉC(EԉC,ADH([A\A]A^A_]UHAWAVATSH`HHHEW)E)E)E)E)EfEDGDODWD_D Dg$_(HH rLuRLSATAWASARH0LpH H H;Mu H`[A\A^A_]`%x%z%|%~%%%%%%%%%%%%%%hhh/hGxh^nhudhZhPhFh<h2h(h,hEhb hLAAS%1hxh?΄3;Zx0Nm<[y1OnutcoffsetOnamezoneiInvalid parametersiiidiiNNNNNNNOOdatetime.datetime_CAPIPreciseDiffPrecise difference between two datetime objects%d years %d months %d days %d hours %d minutes %d seconds %d microseconds_helpersis_leapChecks if a year is a leap year.is_long_yearChecks if a year is a long year.week_dayReturns the weekday number.days_in_yearReturns the number of days in the given year.timestampReturns the timestamp of the given datetime.local_timeReturns a UNIX time as a broken down time for a particular transition type.precise_diffCalculate a precise difference between two datetimes.yearsyears in diffmonthsmonths in diffdaysdays in diffhourshours in diffminutesminutes in diffsecondsseconds in diffmicrosecondsmicroseconds in difftotal_daystotal days in diffiiiiiii,, XpDD)D < 0@! ***4*>*H*R*\*f*p*z*********-04......... //$/-/(:/O/,Z/,0)),%---N-[-|-p--p---.p.j.@w.!0`Epp(pRCRCRCRCRCRCRCp@p8p(p pRASASASASASASAQ>@_PyExc_ValueErrorQq@_PyType_GenericNew@__Py_NoneStruct@___stack_chk_guard@dyld_stub_binderq0>@_PyArg_ParseTupleq8>@_PyBool_FromLongq@>@_PyCapsule_ImportqH>@_PyErr_SetStringqP>@_PyLong_FromLongqX>@_PyModule_AddObjectq`>@_PyModule_Create2qh>@_PyObject_CallMethodqp>@_PyObject_GetAttrStringqx>@_PyObject_HasAttrStringq>@_PyType_IsSubtypeq>@_PyType_Readyq>@_PyUnicode_AsUTF8q>@_PyUnicode_FromStringq>@_Py_BuildValueq@___sprintf_chkq@___stack_chk_failq>@_p_ _is_lweek_daydays_in_yeartimestamplocal_timeprecise_diffPyInit__helpersSECS_PER_DAYMONTHS_OFFSETSpis_lweek_daydayget_has_tzinfoeapong_year !s_in_year_number"#offsettz_name$$%eapong_year%'(*,.8O100_YEARS4_YEARSYEARVVVS_PER_MONTHS_OF_WEEK_TABLEVWXPPP@p d<dGf4~_[.p$p$PNP.$$PNP.$$N.$$N.0$0$PNP.-$$N.0:$0$@N@.pG$p$N.T$$pNp.b$$N.k$$N.py$p$N.p$p$N.$$pNp.p$p$N.@$@$p Np .'$'$N.P($P($N.)$)$N    # 3 F& 0Q& 01_& 2j& 04{& 05dP() 0 01 2 04 050+,&+6'G+[ +m(+|00ppp p@'2p<N_q#5CUkzz{|}@~z{|} _DAYS_PER_MONTHS_DAY_OF_WEEK_TABLE_MONTHS_OFFSETS_PyInit__helpers_SECS_PER_100_YEARS_SECS_PER_4_YEARS_SECS_PER_YEAR__day_number__days_in_year__get_offset__get_tz_name__has_tzinfo__is_leap__is_long_year__p__week_day_days_in_year_is_leap_is_long_year_local_time_precise_diff_timestamp_week_day_PyArg_ParseTuple_PyBool_FromLong_PyCapsule_Import_PyErr_SetString_PyExc_ValueError_PyLong_FromLong_PyModule_AddObject_PyModule_Create2_PyObject_CallMethod_PyObject_GetAttrString_PyObject_HasAttrString_PyType_GenericNew_PyType_IsSubtype_PyType_Ready_PyUnicode_AsUTF8_PyUnicode_FromString_Py_BuildValue__Py_NoneStruct___sprintf_chk___stack_chk_fail___stack_chk_guard_pdyld_stub_binder/private/var/folders/gb/_jfrll295hx0rv6n7vx7l_lh0000gn/T/tmpp86g87rl/pendulum-2.0.3/pendulum/_extensions/_helpers.c/var/folders/gb/_jfrll295hx0rv6n7vx7l_lh0000gn/T/tmpp86g87rl/pendulum-2.0.3/build/temp.macosx-10.13-x86_64-3.6/pendulum/_extensions/_helpers.o__ppendulum/_extensions/_helpers.c__is_leap__is_long_year__week_day__days_in_year__day_number__get_offset__has_tzinfo__get_tz_name_is_leap_is_long_year_week_day_days_in_year_timestamp_local_time_precise_diff_PyInit__helpers_Diff_init_Diff_repr_SECS_PER_100_YEARS_SECS_PER_4_YEARS_SECS_PER_YEAR_DAYS_PER_MONTHS_MONTHS_OFFSETS_DAY_OF_WEEK_TABLE_moduledef_Diff_members_Diff_type_helpers_methods_PyDateTimeAPI_Diff_init_Diff_repr_moduledef_Diff_members_Diff_type_helpers_methods_PyDateTimeAPIPK!G!!pendulum/_extensions/helpers.pyimport math from collections import namedtuple import datetime from ..constants import ( EPOCH_YEAR, SECS_PER_DAY, SECS_PER_400_YEARS, SECS_PER_100_YEARS, SECS_PER_4_YEARS, SECS_PER_YEAR, SECS_PER_HOUR, SECS_PER_MIN, DAYS_PER_MONTHS, MONTHS_OFFSETS, TM_DECEMBER, TM_JANUARY, DAY_OF_WEEK_TABLE, DAYS_PER_L_YEAR, DAYS_PER_N_YEAR ) class PreciseDiff(namedtuple('PreciseDiff', 'years months days ' 'hours minutes seconds microseconds ' 'total_days')): def __repr__(self): return ( '{years} years ' '{months} months ' '{days} days ' '{hours} hours ' '{minutes} minutes ' '{seconds} seconds ' '{microseconds} microseconds' ).format( years=self.years, months=self.months, days=self.days, hours=self.hours, minutes=self.minutes, seconds=self.seconds, microseconds=self.microseconds ) def is_leap(year): return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) def is_long_year(year): def p(y): return y + y//4 - y//100 + y//400 return p(year) % 7 == 4 or p(year - 1) % 7 == 3 def week_day(year, month, day): 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): if is_leap(year): return DAYS_PER_L_YEAR return DAYS_PER_N_YEAR def timestamp(dt): # type: (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, utc_offset, microseconds): """ Returns a UNIX time as a broken down time for a particular transition type. :type unix_time: int :type utc_offset: int :type microseconds: int :rtype: tuple """ 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, d2): """ Calculate a precise difference between two datetimes. :param d1: The first datetime :type d1: datetime.datetime or datetime.date :param d2: The second datetime :type d2: datetime.datetime or datetime.date :rtype: PreciseDiff """ sign = 1 if d1 == d2: return PreciseDiff( 0, 0, 0, 0, 0, 0, 0, 0 ) tzinfo1 = d1.tzinfo if isinstance(d1, datetime.datetime) else None tzinfo2 = 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: if hasattr(tzinfo1, 'name'): # Pendulum timezone tz1 = tzinfo1.name elif hasattr(tzinfo1, 'zone'): # pytz timezone tz1 = tzinfo1.zone if hasattr(tzinfo2, 'name'): tz2 = tzinfo2.name elif hasattr(tzinfo2, 'zone'): tz2 = tzinfo2.zone 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, month, day): month = (month + 9) % 12 year = year - month // 10 return ( 365 * year + year // 4 - year // 100 + year // 400 + (month * 306 + 5) // 10 + (day - 1) ) PK! }  pendulum/constants.py# The day constants 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 PK!|^|^pendulum/date.pyfrom __future__ import absolute_import from __future__ import division import calendar import math import pendulum from datetime import date, timedelta from .helpers import add_duration from .period import Period from .mixins.default import FormattableMixing from .constants import ( DAYS_PER_WEEK, YEARS_PER_DECADE, YEARS_PER_CENTURY, MONTHS_PER_YEAR, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY ) from .exceptions import PendulumException class Date(FormattableMixing, 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=None, month=None, day=None): return self.replace( year=year, month=month, day=day ) @property def day_of_week(self): """ Returns the day of the week (0-6). :rtype: int """ return self.isoweekday() % 7 @property def day_of_year(self): """ Returns the day of the year (1-366). :rtype: int """ 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): return self.isocalendar()[1] @property def days_in_month(self): return calendar.monthrange(self.year, self.month)[1] @property def week_of_month(self): return int(math.ceil(self.day / DAYS_PER_WEEK)) @property def age(self): return self.diff().in_years() @property def quarter(self): return int(math.ceil(self.month / 3)) # String Formatting def to_date_string(self): """ Format the instance as date. :rtype: str """ return self.strftime('%Y-%m-%d') def to_formatted_date_string(self): """ Format the instance as a readable date. :rtype: str """ return self.strftime('%b %d, %Y') def __repr__(self): return ( '{klass}(' '{year}, {month}, {day}' ')'.format( klass=self.__class__.__name__, year=self.year, month=self.month, day=self.day, ) ) # COMPARISONS def closest(self, dt1, dt2): """ Get the closest date from the instance. :type dt1: Date or date :type dt2: Date or date :rtype: Date """ 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, dt2): """ Get the farthest date from the instance. :type dt1: Date or date :type dt2: Date or date :rtype: Date """ 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): """ Determines if the instance is in the future, ie. greater than now. :rtype: bool """ return self > self.today() def is_past(self): """ Determines if the instance is in the past, ie. less than now. :rtype: bool """ return self < self.today() def is_leap_year(self): """ Determines if the instance is a leap year. :rtype: bool """ return calendar.isleap(self.year) def is_long_year(self): """ Determines if the instance is a long year See link ``_ :rtype: bool """ return Date(self.year, 12, 28).isocalendar()[1] == 53 def is_same_day(self, dt): """ Checks if the passed in date is the same day as the instance current day. :type dt: Date or date :rtype: bool """ return self == dt def is_birthday(self, dt=None): """ Check if its the birthday. Compares the date/month values of the two dates. :rtype: bool """ if dt is None: dt = Date.today() instance = dt1 = self.__class__(dt.year, dt.month, dt.day) return (self.month, self.day) == (instance.month, instance.day) # ADDITIONS AND SUBSTRACTIONS def add(self, years=0, months=0, weeks=0, days=0): """ Add duration to the instance. :param years: The number of years :type years: int :param months: The number of months :type months: int :param weeks: The number of weeks :type weeks: int :param days: The number of days :type days: int :rtype: Date """ 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=0, months=0, weeks=0, days=0): """ Remove duration from the instance. :param years: The number of years :type years: int :param months: The number of months :type months: int :param weeks: The number of weeks :type weeks: int :param days: The number of days :type days: int :rtype: Date """ return self.add( years=-years, months=-months, weeks=-weeks, days=-days ) def _add_timedelta(self, delta): """ Add timedelta duration to the instance. :param delta: The timedelta instance :type delta: pendulum.Duration or datetime.timedelta :rtype: Date """ 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): """ Remove timedelta duration from the instance. :param delta: The timedelta instance :type delta: pendulum.Duration or datetime.timedelta :rtype: Date """ 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): if not isinstance(other, timedelta): return NotImplemented return self._add_timedelta(other) def __sub__(self, other): 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=None, abs=True): """ Returns the difference between two Date objects as a Period. :type dt: Date or None :param abs: Whether to return an absolute interval or not :type abs: bool :rtype: Period """ if dt is None: dt = self.today() return Period(self, Date(dt.year, dt.month, dt.day), absolute=abs) def diff_for_humans(self, other=None, absolute=False, locale=None): """ 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 :type other: Date :param absolute: removes time difference modifiers ago, after, etc :type absolute: bool :param locale: The locale to use for localization :type locale: str :rtype: str """ 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): """ 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 :type unit: str :rtype: Date """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError('Invalid unit "{}" for start_of()'.format(unit)) return getattr(self, '_start_of_{}'.format(unit))() def end_of(self, unit): """ 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 :type unit: str :rtype: Date """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError('Invalid unit "%s" for end_of()' % unit) return getattr(self, '_end_of_%s' % unit)() def _start_of_day(self): """ Compatibility method. :rtype: Date """ return self def _end_of_day(self): """ Compatibility method :rtype: Date """ return self def _start_of_month(self): """ Reset the date to the first day of the month. :rtype: Date """ return self.set(self.year, self.month, 1) def _end_of_month(self): """ Reset the date to the last day of the month. :rtype: Date """ return self.set( self.year, self.month, self.days_in_month ) def _start_of_year(self): """ Reset the date to the first day of the year. :rtype: Date """ return self.set(self.year, 1, 1) def _end_of_year(self): """ Reset the date to the last day of the year. :rtype: Date """ return self.set(self.year, 12, 31) def _start_of_decade(self): """ Reset the date to the first day of the decade. :rtype: Date """ year = self.year - self.year % YEARS_PER_DECADE return self.set(year, 1, 1) def _end_of_decade(self): """ Reset the date to the last day of the decade. :rtype: Date """ year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1 return self.set(year, 12, 31) def _start_of_century(self): """ Reset the date to the first day of the century. :rtype: Date """ year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1 return self.set(year, 1, 1) def _end_of_century(self): """ Reset the date to the last day of the century. :rtype: Date """ year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY return self.set(year, 12, 31) def _start_of_week(self): """ Reset the date to the first day of the week. :rtype: Date """ 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): """ Reset the date to the last day of the week. :rtype: Date """ 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=None): """ 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. :type day_of_week: int or None :rtype: Date """ 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=None): """ 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. :type day_of_week: int or None :rtype: Date """ 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, day_of_week=None): """ 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 :type unit: str :type day_of_week: int or None :rtype: Date """ if unit not in ['month', 'quarter', 'year']: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) return getattr(self, '_first_of_{}'.format(unit))(day_of_week) def last_of(self, unit, day_of_week=None): """ 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 :type unit: str :type day_of_week: int or None :rtype: Date """ if unit not in ['month', 'quarter', 'year']: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) return getattr(self, '_last_of_{}'.format(unit))(day_of_week) def nth_of(self, unit, nth, day_of_week): """ 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 :type unit: str :type nth: int :type day_of_week: int or None :rtype: Date """ if unit not in ['month', 'quarter', 'year']: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) dt = getattr(self, '_nth_of_{}'.format(unit))(nth, day_of_week) if dt is False: raise PendulumException('Unable to find occurence {} of {} in {}'.format( nth, self._days[day_of_week], unit)) return dt def _first_of_month(self, day_of_week): """ 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. :type day_of_week: int :rtype: Date """ 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=None): """ 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. :type day_of_week: int or None :rtype: Date """ 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, day_of_week): """ 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. :type nth: int :type day_of_week: int or None :rtype: Date """ if nth == 1: return self.first_of('month', day_of_week) dt = self.first_of('month') check = dt.format('YYYY-MM') for i 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 False def _first_of_quarter(self, day_of_week=None): """ 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. :type day_of_week: int or None :rtype: Date """ return self.set(self.year, self.quarter * 3 - 2, 1).first_of('month', day_of_week) def _last_of_quarter(self, day_of_week=None): """ 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. :type day_of_week: int or None :rtype: Date """ return self.set(self.year, self.quarter * 3, 1).last_of('month', day_of_week) def _nth_of_quarter(self, nth, day_of_week): """ 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. :type nth: int :type day_of_week: int or None :rtype: Date """ 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 i 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 False return self.set(self.year, dt.month, dt.day) def _first_of_year(self, day_of_week=None): """ 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. :type day_of_week: int or None :rtype: Date """ return self.set(month=1).first_of('month', day_of_week) def _last_of_year(self, day_of_week=None): """ 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. :type day_of_week: int or None :rtype: Date """ return self.set(month=MONTHS_PER_YEAR).last_of('month', day_of_week) def _nth_of_year(self, nth, day_of_week): """ 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. :type nth: int :type day_of_week: int or None :rtype: Date """ if nth == 1: return self.first_of('year', day_of_week) dt = self.first_of('year') year = dt.year for i 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 False return self.set(self.year, dt.month, dt.day) def average(self, dt=None): """ Modify the current instance to the average of a given instance (default now) and the current instance. :type dt: Date or date :rtype: Date """ 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): return pendulum.today().date() @classmethod def fromtimestamp(cls, t): dt = super(Date, cls).fromtimestamp(t) return cls(dt.year, dt.month, dt.day) @classmethod def fromordinal(cls, n): dt = super(Date, cls).fromordinal(n) return cls(dt.year, dt.month, dt.day) def replace(self, year=None, month=None, day=None): 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) PK!_Iccpendulum/datetime.py# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division import calendar import datetime import pendulum from typing import Union from .date import Date from .time import Time from .period import Period from .exceptions import PendulumException from .utils._compat import _HAS_FOLD from .tz import UTC from .tz.timezone import Timezone from .helpers import add_duration from .helpers import timestamp from .constants import ( YEARS_PER_CENTURY, YEARS_PER_DECADE, MONTHS_PER_YEAR, MINUTES_PER_HOUR, SECONDS_PER_MINUTE, SECONDS_PER_DAY, SUNDAY, SATURDAY, ATOM, COOKIE, RFC822, RFC850, RFC1036, RFC1123, RFC2822, RSS, W3C ) class DateTime(datetime.datetime, Date): # Formats _FORMATS = { '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 } _EPOCH = datetime.datetime(1970, 1, 1, tzinfo=UTC) _MODIFIERS_VALID_UNITS = [ 'second', 'minute', 'hour', 'day', 'week', 'month', 'year', 'decade', 'century' ] if not _HAS_FOLD: def __new__(cls, year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, fold=0): self = datetime.datetime.__new__( cls, year, month, day, hour, minute, second, microsecond, tzinfo=tzinfo ) self._fold = fold return self @classmethod def now(cls, tz=None): # type: (Union[str, Timezone, None]) -> DateTime """ Get a DateTime instance for the current date and time. """ return pendulum.now(tz) @classmethod def utcnow(cls): # type: () -> DateTime """ Get a DateTime instance for the current date and time in UTC. """ return pendulum.now(UTC) @classmethod def today(cls): # type: () -> DateTime return pendulum.now() @classmethod def strptime(cls, time, fmt): # type: (str, str) -> DateTime return pendulum.instance(datetime.datetime.strptime(time, fmt)) # Getters/Setters def set(self, year=None, month=None, day=None, hour=None, minute=None, second=None, microsecond=None, tz=None): 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 pendulum.datetime( year, month, day, hour, minute, second, microsecond, tz=tz ) if not _HAS_FOLD: @property def fold(self): return self._fold def timestamp(self): if self.tzinfo is None: s = timestamp(self) return s + self.microsecond / 1e6 else: kwargs = { 'tzinfo': self.tzinfo, } if _HAS_FOLD: kwargs['fold'] = self.fold dt = datetime.datetime( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, **kwargs ) return (dt - self._EPOCH).total_seconds() @property def float_timestamp(self): return self.timestamp() @property def int_timestamp(self): # 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 ) delta = dt - self._EPOCH return delta.days * SECONDS_PER_DAY + delta.seconds @property def offset(self): return self.get_offset() @property def offset_hours(self): return (self.get_offset() / SECONDS_PER_MINUTE / MINUTES_PER_HOUR) @property def timezone(self): # type: () -> Union[str, None] if not isinstance(self.tzinfo, Timezone): return return self.tzinfo @property def tz(self): # type: () -> Union[str, None] return self.timezone @property def timezone_name(self): # type: () -> Union[str, None] tz = self.timezone if self.timezone is None: return None return tz.name @property def age(self): return self.date().diff(self.now(self.tz).date()).in_years() def is_local(self): return self.offset == self.in_timezone(pendulum.local_timezone()).offset def is_utc(self): return self.timezone_name == 'UTC' def is_dst(self): return self.dst() != datetime.timedelta() def get_offset(self): return int(self.utcoffset().total_seconds()) def date(self): return Date(self.year, self.month, self.day) def time(self): return Time(self.hour, self.minute, self.second, self.microsecond) def naive(self): # type: () -> 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, month, day): """ Returns a new instance with the current date set to a different date. :param year: The year :type year: int :param month: The month :type month: int :param day: The day :type day: int :rtype: DateTime """ return self.set( year=int(year), month=int(month), day=int(day) ) def at(self, hour, minute=0, second=0, microsecond=0): """ Returns a new instance with the current time to a different time. :param hour: The hour :type hour: int :param minute: The minute :type minute: int :param second: The second :type second: int :param microsecond: The microsecond :type microsecond: int :rtype: DateTime """ return self.set( hour=hour, minute=minute, second=second, microsecond=microsecond ) def in_timezone(self, tz ): # type: (Union[str, Timezone]) -> DateTime """ Set the instance's timezone from a string or object. """ tz = pendulum._safe_timezone(tz) return tz.convert(self, dst_rule=pendulum.POST_TRANSITION) def in_tz(self, tz ): # type: (Union[str, Timezone]) -> DateTime """ Set the instance's timezone from a string or object. """ return self.in_timezone(tz) # STRING FORMATTING def to_time_string(self): """ Format the instance as time. :rtype: str """ return self.format('HH:mm:ss') def to_datetime_string(self): """ Format the instance as date and time. :rtype: str """ return self.format('YYYY-MM-DD HH:mm:ss') def to_day_datetime_string(self): """ Format the instance as day, date and time (in english). :rtype: str """ return self.format('ddd, MMM D, YYYY h:mm A', locale='en') def to_atom_string(self): """ Format the instance as ATOM. :rtype: str """ return self._to_string('atom') def to_cookie_string(self): """ Format the instance as COOKIE. :rtype: str """ return self._to_string('cookie', locale='en') def to_iso8601_string(self): """ Format the instance as ISO 8601. :rtype: str """ 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): """ Format the instance as RFC 822. :rtype: str """ return self._to_string('rfc822') def to_rfc850_string(self): """ Format the instance as RFC 850. :rtype: str """ return self._to_string('rfc850') def to_rfc1036_string(self): """ Format the instance as RFC 1036. :rtype: str """ return self._to_string('rfc1036') def to_rfc1123_string(self): """ Format the instance as RFC 1123. :rtype: str """ return self._to_string('rfc1123') def to_rfc2822_string(self): """ Format the instance as RFC 2822. :rtype: str """ return self._to_string('rfc2822') def to_rfc3339_string(self): """ Format the instance as RFC 3339. :rtype: str """ return self._to_string('rfc3339') def to_rss_string(self): """ Format the instance as RSS. :rtype: str """ return self._to_string('rss') def to_w3c_string(self): """ Format the instance as W3C. :rtype: str """ return self._to_string('w3c') def _to_string(self, fmt, locale=None): """ Format the instance to a common string format. :param fmt: The name of the string format :type fmt: string :param locale: The locale to use :type locale: str or None :rtype: str """ if fmt not in self._FORMATS: raise ValueError('Format [{}] is not supported'.format(fmt)) fmt = self._FORMATS[fmt] if callable(fmt): return fmt(self) return self.format(fmt, locale=locale) def __str__(self): return self.isoformat('T') def __repr__(self): us = '' if self.microsecond: us = ', {}'.format(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=self.tzinfo ) # Comparisons def closest(self, dt1, dt2, *dts): from functools import reduce """ Get the farthest date from the instance. :type dt1: datetime.datetime :type dt2: datetime.datetime :type dts: list[datetime.datetime,] :rtype: DateTime """ dt1 = pendulum.instance(dt1) dt2 = pendulum.instance(dt2) dts = [dt1, dt2] + [pendulum.instance(x) for x in dts] dts = [(abs(self - dt), dt) for dt in dts] return min(dts)[1] def farthest(self, dt1, dt2, *dts): from functools import reduce """ Get the farthest date from the instance. :type dt1: datetime.datetime :type dt2: datetime.datetime :type dts: list[datetime.datetime,] :rtype: DateTime """ dt1 = pendulum.instance(dt1) dt2 = pendulum.instance(dt2) dts = [dt1, dt2] + [pendulum.instance(x) for x in dts] dts = [(abs(self - dt), dt) for dt in dts] return max(dts)[1] def is_future(self): """ Determines if the instance is in the future, ie. greater than now. :rtype: bool """ return self > self.now(self.timezone) def is_past(self): """ Determines if the instance is in the past, ie. less than now. :rtype: bool """ return self < self.now(self.timezone) def is_long_year(self): """ Determines if the instance is a long year See link `https://en.wikipedia.org/wiki/ISO_8601#Week_dates`_ :rtype: bool """ return pendulum.datetime( self.year, 12, 28, 0, 0, 0, tz=self.tz ).isocalendar()[1] == 53 def is_same_day(self, dt): """ Checks if the passed in date is the same day as the instance current day. :type dt: DateTime or datetime or str or int :rtype: bool """ dt = pendulum.instance(dt) return self.to_date_string() == dt.to_date_string() def is_birthday(self, dt=None): """ Check if its the birthday. Compares the date/month values of the two dates. :rtype: bool """ 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=0, months=0, weeks=0, days=0, hours=0, minutes=0, seconds=0, microseconds=0 ): # type: (int, int, int, int, int, int, int) -> DateTime """ Add a duration to the instance. If we're adding units of variable length (i.e., years, months), move forward from curren 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.tzinfo is None: return pendulum.datetime( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tz=self.tz ) dt = self.__class__( 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=0, months=0, weeks=0, days=0, hours=0, minutes=0, seconds=0, microseconds=0): """ Remove duration from the instance. :param years: The number of years :type years: int :param months: The number of months :type months: int :param weeks: The number of weeks :type weeks: int :param days: The number of days :type days: int :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: DateTime """ return self.add( years=-years, months=-months, weeks=-weeks, days=-days, hours=-hours, minutes=-minutes, seconds=-seconds, microseconds=-microseconds ) def _add_timedelta(self, delta): """ Add timedelta duration to the instance. :param delta: The timedelta instance :type delta: pendulum.Duration or datetime.timedelta :rtype: DateTime """ if isinstance(delta, pendulum.Period): 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_seconds() ) return self.add(seconds=delta.total_seconds()) def _subtract_timedelta(self, delta): """ Remove timedelta duration from the instance. :param delta: The timedelta instance :type delta: pendulum.Duration or datetime.timedelta :rtype: DateTime """ if isinstance(delta, pendulum.Duration): return self.subtract( 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 ) return self.subtract(days=delta.days, seconds=delta.seconds, microseconds=delta.microseconds) # DIFFERENCES def diff(self, dt=None, abs=True): """ Returns the difference between two DateTime objects represented as a Duration. :type dt: DateTime or None :param abs: Whether to return an absolute interval or not :type abs: bool :rtype: Period """ if dt is None: dt = self.now(self.tz) return Period(self, dt, absolute=abs) def diff_for_humans(self, other=None, # type: Union['DateTime', None] absolute=False, # type: bool locale=None # type:Union[str, None] ): # type: (...) -> False """ 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): """ 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 :param unit: The unit to reset to :type unit: str :rtype: DateTime """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError('Invalid unit "{}" for start_of()'.format(unit)) return getattr(self, '_start_of_{}'.format(unit))() def end_of(self, unit): """ 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 :param unit: The unit to reset to :type unit: str :rtype: DateTime """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError('Invalid unit "%s" for end_of()' % unit) return getattr(self, '_end_of_%s' % unit)() def _start_of_second(self): """ Reset microseconds to 0. :rtype: DateTime """ return self.set(microsecond=0) def _end_of_second(self): """ Set microseconds to 999999. :rtype: DateTime """ return self.set(microsecond=999999) def _start_of_minute(self): """ Reset seconds and microseconds to 0. :rtype: DateTime """ return self.set(second=0, microsecond=0) def _end_of_minute(self): """ Set seconds to 59 and microseconds to 999999. :rtype: DateTime """ return self.set(second=59, microsecond=999999) def _start_of_hour(self): """ Reset minutes, seconds and microseconds to 0. :rtype: DateTime """ return self.set(minute=0, second=0, microsecond=0) def _end_of_hour(self): """ Set minutes and seconds to 59 and microseconds to 999999. :rtype: DateTime """ return self.set(minute=59, second=59, microsecond=999999) def _start_of_day(self): """ Reset the time to 00:00:00 :rtype: DateTime """ return self.at(0, 0, 0, 0) def _end_of_day(self): """ Reset the time to 23:59:59.999999 :rtype: DateTime """ return self.at(23, 59, 59, 999999) def _start_of_month(self): """ Reset the date to the first day of the month and the time to 00:00:00. :rtype: DateTime """ return self.set(self.year, self.month, 1, 0, 0, 0, 0) def _end_of_month(self): """ Reset the date to the last day of the month and the time to 23:59:59.999999. :rtype: DateTime """ return self.set( self.year, self.month, self.days_in_month, 23, 59, 59, 999999 ) def _start_of_year(self): """ Reset the date to the first day of the year and the time to 00:00:00. :rtype: DateTime """ return self.set(self.year, 1, 1, 0, 0, 0, 0) def _end_of_year(self): """ Reset the date to the last day of the year and the time to 23:59:59.999999 :rtype: DateTime """ return self.set( self.year, 12, 31, 23, 59, 59, 999999 ) def _start_of_decade(self): """ Reset the date to the first day of the decade and the time to 00:00:00. :rtype: DateTime """ year = self.year - self.year % YEARS_PER_DECADE return self.set(year, 1, 1, 0, 0, 0, 0) def _end_of_decade(self): """ Reset the date to the last day of the decade and the time to 23:59:59.999999. :rtype: DateTime """ 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): """ Reset the date to the first day of the century and the time to 00:00:00. :rtype: DateTime """ 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): """ Reset the date to the last day of the century and the time to 23:59:59.999999. :rtype: DateTime """ 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): """ Reset the date to the first day of the week and the time to 00:00:00. :rtype: DateTime """ 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): """ Reset the date to the last day of the week and the time to 23:59:59. :rtype: DateTime """ 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=None, keep_time=False): """ 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. :param day_of_week: The next day of week to reset to. :type day_of_week: int or None :param keep_time: Whether to keep the time information or not. :type keep_time: bool :rtype: DateTime """ 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=None, keep_time=False): """ 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. :param day_of_week: The previous day of week to reset to. :type day_of_week: int or None :param keep_time: Whether to keep the time information or not. :type keep_time: bool :rtype: DateTime """ 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, day_of_week=None): """ 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. :param unit: The unit to use :type unit: str :type day_of_week: int or None :rtype: DateTime """ if unit not in ['month', 'quarter', 'year']: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) return getattr(self, '_first_of_{}'.format(unit))(day_of_week) def last_of(self, unit, day_of_week=None): """ 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. :param unit: The unit to use :type unit: str :type day_of_week: int or None :rtype: DateTime """ if unit not in ['month', 'quarter', 'year']: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) return getattr(self, '_last_of_{}'.format(unit))(day_of_week) def nth_of(self, unit, nth, day_of_week): """ 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. :param unit: The unit to use :type unit: str :type nth: int :type day_of_week: int or None :rtype: DateTime """ if unit not in ['month', 'quarter', 'year']: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) dt = getattr(self, '_nth_of_{}'.format(unit))(nth, day_of_week) if dt is False: raise PendulumException('Unable to find occurence {} of {} in {}'.format( nth, self._days[day_of_week], unit)) return dt def _first_of_month(self, day_of_week): """ 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. :type day_of_week: int :rtype: DateTime """ 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=None): """ 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. :type day_of_week: int or None :rtype: DateTime """ 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, day_of_week): """ 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. :type nth: int :type day_of_week: int or None :rtype: DateTime """ if nth == 1: return self.first_of('month', day_of_week) dt = self.first_of('month') check = dt.format('%Y-%m') for i 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 False def _first_of_quarter(self, day_of_week=None): """ 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. :type day_of_week: int or None :rtype: DateTime """ return self.on(self.year, self.quarter * 3 - 2, 1).first_of('month', day_of_week) def _last_of_quarter(self, day_of_week=None): """ 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. :type day_of_week: int or None :rtype: DateTime """ return self.on(self.year, self.quarter * 3, 1).last_of('month', day_of_week) def _nth_of_quarter(self, nth, day_of_week): """ 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. :type nth: int :type day_of_week: int or None :rtype: DateTime """ 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 i 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 False return self.on(self.year, dt.month, dt.day).start_of('day') def _first_of_year(self, day_of_week=None): """ 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. :type day_of_week: int or None :rtype: DateTime """ return self.set(month=1).first_of('month', day_of_week) def _last_of_year(self, day_of_week=None): """ 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. :type day_of_week: int or None :rtype: DateTime """ return self.set(month=MONTHS_PER_YEAR).last_of('month', day_of_week) def _nth_of_year(self, nth, day_of_week): """ 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. :type nth: int :type day_of_week: int or None :rtype: DateTime """ if nth == 1: return self.first_of('year', day_of_week) dt = self.first_of('year') year = dt.year for i 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 False return self.on(self.year, dt.month, dt.day).start_of('day') def average(self, dt=None): """ Modify the current instance to the average of a given instance (default now) and the current instance. :type dt: DateTime or datetime :rtype: DateTime """ 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 ) def __sub__(self, other): 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): 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): if not isinstance(other, datetime.timedelta): return NotImplemented return self._add_timedelta(other) def __radd__(self, other): return self.__add__(other) # Native methods override @classmethod def fromtimestamp(cls, t, tz=None): return pendulum.instance( datetime.datetime.fromtimestamp(t, tz=tz), tz=tz ) @classmethod def utcfromtimestamp(cls, t): return pendulum.instance( datetime.datetime.utcfromtimestamp(t), tz=None ) @classmethod def fromordinal(cls, n): return pendulum.instance( datetime.datetime.fromordinal(n), tz=None ) @classmethod def combine(cls, date, time): return pendulum.instance( datetime.datetime.combine(date, time), tz=None ) def astimezone(self, tz=None): return pendulum.instance(super(DateTime, self).astimezone(tz)) def replace(self, year=None, month=None, day=None, hour=None, minute=None, second=None, microsecond=None, tzinfo=True, fold=None): 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 transition_rule = pendulum.POST_TRANSITION if fold is not None: transition_rule = pendulum.PRE_TRANSITION if fold: transition_rule = pendulum.POST_TRANSITION return pendulum.datetime( year, month, day, hour, minute, second, microsecond, tz=tzinfo, dst_rule=transition_rule ) def __getnewargs__(self): return(self, ) def _getstate(self, protocol=3): return ( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, self.tzinfo ) def __reduce__(self): return self.__reduce_ex__(2) def __reduce_ex__(self, protocol): return self.__class__, self._getstate(protocol) def _cmp(self, other, **kwargs): # Fix for pypy which compares using this method # which would lead to infinite recursion if we didn't override kwargs = { 'tzinfo': self.tz } if _HAS_FOLD: kwargs['fold'] = self.fold dt = datetime.datetime( self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, **kwargs ) return 0 if dt == other else 1 if dt > other else -1 DateTime.min = DateTime(1, 1, 1, 0, 0, tzinfo=UTC) DateTime.max = DateTime(9999, 12, 31, 23, 59, 59, 999999, tzinfo=UTC) DateTime.EPOCH = DateTime(1970, 1, 1) PK!gd//pendulum/duration.pyfrom __future__ import absolute_import from __future__ import division import pendulum from datetime import timedelta from pendulum.utils._compat import PYPY from pendulum.utils._compat import decode from .constants import ( SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE, US_PER_SECOND ) def _divide_and_round(a, b): """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) # 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. """ _y = None _m = None _w = None _d = None _h = None _i = None _s = None _invert = None def __new__(cls, days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0, years=0, months=0): 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 ) # Intuitive normalization total = self.total_seconds() 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 + (years * 365 + months * 30) 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): return self.total_seconds() / SECONDS_PER_MINUTE def total_hours(self): return self.total_seconds() / SECONDS_PER_HOUR def total_days(self): return self.total_seconds() / SECONDS_PER_DAY def total_weeks(self): return self.total_days() / 7 if PYPY: def total_seconds(self): 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): return self._years @property def months(self): return self._months @property def weeks(self): return self._weeks @property def days(self): return self._days @property def remaining_days(self): return self._remaining_days @property def hours(self): 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): 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): return self._seconds @property def remaining_seconds(self): 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): return self._microseconds @property def invert(self): if self._invert is None: self._invert = self.total_seconds() < 0 return self._invert def in_weeks(self): return int(self.total_weeks()) def in_days(self): return int(self.total_days()) def in_hours(self): return int(self.total_hours()) def in_minutes(self): return int(self.total_minutes()) def in_seconds(self): return int(self.total_seconds()) def in_words(self, locale=None, separator=' '): """ 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. :type locale: str :param separator: The separator to use between each unit :type separator: str :rtype: str """ 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() locale = pendulum.locale(locale) parts = [] for period in periods: unit, count = period if abs(count) > 0: translation = locale.translation( 'units.{}.{}'.format( unit, locale.plural(abs(count)) ) ) parts.append(translation.format(count)) if not parts and abs(self.microseconds) > 0: translation = locale.translation( 'units.second.{}'.format(locale.plural(1)) ) us = abs(self.microseconds) / 1e6 parts.append( translation.format('{:.2f}'.format(us)) ) return decode(separator.join(parts)) def _sign(self, value): if value < 0: return -1 return 1 def as_timedelta(self): """ Return the interval as a native timedelta. :rtype: timedelta """ return timedelta(seconds=self.total_seconds()) def __str__(self): return self.in_words() def __repr__(self): rep = '{}('.format(self.__class__.__name__) if self._years: rep += 'years={}, '.format(self._years) if self._months: rep += 'months={}, '.format(self._months) if self._weeks: rep += 'weeks={}, '.format(self._weeks) if self._days: rep += 'days={}, '.format(self._remaining_days) if self.hours: rep += 'hours={}, '.format(self.hours) if self.minutes: rep += 'minutes={}, '.format(self.minutes) if self.remaining_seconds: rep += 'seconds={}, '.format(self.remaining_seconds) if self.microseconds: rep += 'microseconds={}, '.format(self.microseconds) rep += ')' return rep.replace(', )', ')') def __add__(self, other): if isinstance(other, timedelta): return self.__class__(seconds=self.total_seconds() + other.total_seconds()) return NotImplemented __radd__ = __add__ def __sub__(self, other): if isinstance(other, timedelta): return self.__class__(seconds=self.total_seconds() - other.total_seconds()) return NotImplemented def __neg__(self): 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): return ((self._days * (24*3600) + self._seconds) * 1000000 + self._microseconds) def __mul__(self, other): if isinstance(other, int): return self.__class__( years=self._years * other, months=self._months * other, seconds=self.total_seconds() * 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__ def __floordiv__(self, other): if not isinstance(other, (int, timedelta)): return NotImplemented usec = self._to_microseconds() if isinstance(other, timedelta): return usec // other._to_microseconds() # Removing years/months approximation usec -= (self._years * 365 + self._months * 30) * SECONDS_PER_DAY * 1e6 if isinstance(other, int): return self.__class__( 0, 0, usec // other, years=self._years // other, months=self._months // other ) def __truediv__(self, other): if not isinstance(other, (int, float, timedelta)): return NotImplemented usec = self._to_microseconds() if isinstance(other, timedelta): return usec / other._to_microseconds() # Removing years/months approximation usec -= (self._years * 365 + self._months * 30) * SECONDS_PER_DAY * 1e6 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): if isinstance(other, timedelta): r = self._to_microseconds() % other._to_microseconds() return self.__class__(0, 0, r) return NotImplemented def __divmod__(self, other): if isinstance(other, timedelta): q, r = divmod(self._to_microseconds(), other._to_microseconds()) 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=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0, years=0, months=0): 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): return abs(self._total) @property def invert(self): if self._invert is None: self._invert = self._total < 0 return self._invert PK!-\\pendulum/exceptions.pyfrom .parsing.exceptions import ParserError class PendulumException(Exception): pass PK!/AApendulum/formatting/__init__.pyfrom .formatter import Formatter __all__ = [ 'Formatter' ] PK!q +pendulum/formatting/difference_formatter.pyfrom pendulum.utils._compat import decode from ..locales.locale import Locale class DifferenceFormatter(object): """ Handles formatting differences in text. """ def __init__(self, locale='en'): self._locale = Locale.load(locale) def format(self, diff, is_now=True, absolute=False, locale=None): """ Formats a difference. :param diff: The difference to format :type diff: pendulum.period.Period :param is_now: Whether the difference includes now :type is_now: bool :param absolute: Whether it's an absolute difference or not :type absolute: bool :param locale: The locale to use :type locale: str or None :rtype: str """ if locale is None: locale = self._locale else: locale = Locale.load(locale) count = diff.remaining_seconds 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 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 locale.get(key).format(time) else: unit = 'second' count = diff.remaining_seconds if count == 0: count = 1 if absolute: key = 'translations.units.{}'.format(unit) else: is_future = diff.invert if is_now: # Relative to now, so we can use # the CLDR data key = 'translations.relative.{}'.format(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 += '.{}.future'.format(unit) else: key += '.{}.past'.format(unit) trans = locale.get(key) if not trans: # No special rule time = locale.get( 'translations.units.{}.{}'.format( unit, locale.plural(count) ) ).format(count) else: time = trans[locale.plural(count)].format(count) key = 'custom' if is_future: key += '.after' else: key += '.before' return locale.get(key).format(decode(time)) key += '.{}'.format(locale.plural(count)) return decode(locale.get(key).format(count)) PK!dF T T pendulum/formatting/formatter.py# -*- coding: utf-8 -*- from __future__ import unicode_literals import re import datetime import pendulum import typing from pendulum.helpers import local_time from pendulum.locales.locale import Locale from pendulum.utils._compat import decode _MATCH_1 = '\d' _MATCH_2 = '\d\d' _MATCH_3 = '\d{3}' _MATCH_4 = '\d{4}' _MATCH_6 = '[+-]?\d{6}' _MATCH_1_TO_2 = '\d\d?' _MATCH_1_TO_3 = '\d{1,3}' _MATCH_1_TO_4 = '\d{1,4}' _MATCH_1_TO_6 = '[+-]?\d{1,6}' _MATCH_3_TO_4 = '\d{3}\d?' _MATCH_5_TO_6 = '\d{5}\d?' _MATCH_UNSIGNED = '\d+' _MATCH_SIGNED = '[+-]?\d+' _MATCH_OFFSET = '(?i)Z|[+-]\d\d:?\d\d' _MATCH_SHORT_OFFSET = '(?i)Z|[+-]\d\d(?::?\d\d)?' _MATCH_TIMESTAMP = '[+-]?\d+(\.\d{1,6})?' _MATCH_WORD = "(?i)[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}" _MATCH_TIMEZONE = '[A-za-z0-9-+]+(/[A-Za-z0-9-+]+)?' class Formatter: _TOKENS = '\[([^\[]*)\]|\\\(.)|' \ '(' \ '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.compile(_TOKENS) _LOCALIZABLE_TOKENS = { 'Qo': None, 'MMMM': 'months.wide', 'MMM': 'months.abbreviated', 'Mo': None, 'DDDo': None, 'Do': lambda locale: tuple( '\d+{}'.format(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 = { # Year 'YYYY': lambda dt: '{:d}'.format(dt.year), 'YY': lambda dt: '{:d}'.format(dt.year)[2:], 'Y': lambda dt: '{:d}'.format(dt.year), # Quarter 'Q': lambda dt: '{:d}'.format(dt.quarter), # Month 'MM': lambda dt: '{:02d}'.format(dt.month), 'M': lambda dt: '{:d}'.format(dt.month), # Day 'DD': lambda dt: '{:02d}'.format(dt.day), 'D': lambda dt: '{:d}'.format(dt.day), # Day of Year 'DDDD': lambda dt: '{:03d}'.format(dt.day_of_year), 'DDD': lambda dt: '{:d}'.format(dt.day_of_year), # Day of Week 'd': lambda dt: '{:d}'.format(dt.day_of_week), # Day of ISO Week 'E': lambda dt: '{:d}'.format(dt.isoweekday()), # Hour 'HH': lambda dt: '{:02d}'.format(dt.hour), 'H': lambda dt: '{:d}'.format(dt.hour), 'hh': lambda dt: '{:02d}'.format(dt.hour % 12 or 12), 'h': lambda dt: '{:d}'.format(dt.hour % 12 or 12), # Minute 'mm': lambda dt: '{:02d}'.format(dt.minute), 'm': lambda dt: '{:d}'.format(dt.minute), # Second 'ss': lambda dt: '{:02d}'.format(dt.second), 's': lambda dt: '{:d}'.format(dt.second), # Fractional second 'S': lambda dt: '{:01d}'.format(dt.microsecond // 100000), 'SS': lambda dt: '{:02d}'.format(dt.microsecond // 10000), 'SSS': lambda dt: '{:03d}'.format(dt.microsecond // 1000), 'SSSS': lambda dt: '{:04d}'.format(dt.microsecond // 100), 'SSSSS': lambda dt: '{:05d}'.format(dt.microsecond // 10), 'SSSSSS': lambda dt: '{:06d}'.format(dt.microsecond), # Timestamp 'X': lambda dt: '{:d}'.format(dt.int_timestamp), # Timezone 'zz': lambda dt: "{}".format(dt.tzname() if dt.tzinfo is not None else ''), 'z': lambda dt: "{}".format(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 = { '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, _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 = { '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, fmt, locale=None): """ Formats a DateTime instance with a given format and locale. :param dt: The instance to format :type dt: pendulum.DateTime :param fmt: The format to use :type fmt: str :param locale: The locale to use :type locale: str or Locale or None :rtype: str """ if not locale: locale = pendulum.get_locale() locale = Locale.load(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), locale), fmt ) return decode(result) def _format_token(self, dt, token, locale): """ Formats a DateTime instance with a given token and locale. :param dt: The instance to format :type dt: pendulum.DateTime :param token: The token to use :type token: str :param locale: The locale to use :type locale: Locale :rtype: str """ if token in self._DATE_FORMATS: fmt = locale.get('custom.date_formats.{}'.format(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 '{}{:02d}{}{:02d}'.format(sign, hour, separator, minute) def _format_localizable_token(self, dt, token, locale): """ Formats a DateTime instance with a given localizable token and locale. :param dt: The instance to format :type dt: pendulum.DateTime :param token: The token to use :type token: str :param locale: The locale to use :type locale: Locale :rtype: str """ if token == 'MMM': return locale.get('translations.months.abbreviated')[dt.month] elif token == 'MMMM': return locale.get('translations.months.wide')[dt.month] elif token == 'dd': return locale.get('translations.days.short')[dt.day_of_week] elif token == 'ddd': return locale.get('translations.days.abbreviated')[dt.day_of_week] elif token == 'dddd': return 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 locale.get(key) else: return token def parse(self, time, # type: str fmt, # type: str now, # type: pendulum.DateTime locale=None, # type: typing.Union[str, None] ): # type: (...) -> dict """ 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._FORMAT_RE.findall(escaped_fmt) if not tokens: return time if not locale: locale = pendulum.get_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._FORMAT_RE.sub( lambda m: self._replace_tokens(m.group(0), locale), escaped_fmt ) if not re.match(pattern, time): raise ValueError('String does not match format {}'.format(fmt)) re.sub(pattern, lambda m: self._get_parsed_values(m, parsed, locale, now), time) return self._check_parsed(parsed, now) def _check_parsed(self, parsed, now ): # type: (dict, pendulum.DateTime) -> dict """ Checks validity of parsed elements. :param parsed: The elements to parse. :return: The validated elements. """ validated = { '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( "{}".format(str_us.split('.')[1].ljust(6, '0')) ) else: microseconds = 0 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 = pendulum.parse( "{}-{}".format( validated['year'], parsed['day_of_year'] ) ) validated['month'] = dt.month validated['day'] = dt.day if parsed['day_of_week'] is not None: dt = pendulum.datetime( 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, parsed, locale, now ): # type: (..., dict, Locale, 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, value, parsed, now ): # type: (str, str, dict, 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 'Q' == token: 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 = True if value.startswith('-') else False tz = value[1:] if ':' not in tz: if len(tz) == 2: tz = '{}00'.format(tz) 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, value, parsed, locale ): # type: (str, str, dict, Locale) -> None if token == 'MMMM': unit = 'month' match = 'months.wide' elif token == 'MMM': unit = 'month' match = 'months.abbreviated' elif token == 'Do': parsed['day'] = int(re.match('(\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 = list(map(lambda x: x.lower(), valid_values)) if value not in valid_values: raise ValueError('Invalid date') parsed['meridiem'] = ['am', 'pm'][valid_values.index(value)] return else: raise ValueError('Invalid token "{}"'.format(token)) parsed[unit] = locale.match_translation(match, value) if value is None: raise ValueError('Invalid date') def _replace_tokens(self, token, locale): # type: (str, Locale) -> str if token.startswith('[') and token.endswith(']'): return token[1:-1] elif token.startswith('\\'): return token elif (token not in self._REGEX_TOKENS and token not in self._LOCALIZABLE_TOKENS): raise ValueError('Unsupported token: {}'.format(token)) if token in self._LOCALIZABLE_TOKENS: values = self._LOCALIZABLE_TOKENS[token] if callable(values): candidates = values(locale) else: candidates = tuple( locale.translation(self._LOCALIZABLE_TOKENS[token]).values() ) else: candidates = self._REGEX_TOKENS[token] if not candidates: raise ValueError('Unsupported token: {}'.format(token)) if not isinstance(candidates, tuple): candidates = (candidates,) pattern = '(?P<{}>{})'.format(token, '|'.join([decode(p) for p in candidates])) return pattern PK!Lmv  pendulum/helpers.pyfrom __future__ import absolute_import import pendulum from math import copysign from datetime import datetime, date, timedelta from contextlib import contextmanager from typing import Union try: from ._extensions._helpers import ( local_time, precise_diff, is_leap, is_long_year, week_day, days_in_year, timestamp ) except ImportError: from ._extensions.helpers import ( local_time, precise_diff, is_leap, is_long_year, week_day, days_in_year, timestamp ) from .constants import DAYS_PER_MONTHS from .formatting.difference_formatter import DifferenceFormatter from .locales.locale import Locale difference_formatter = DifferenceFormatter() def add_duration(dt, # type: Union[datetime, date] years=0, # type: int months=0, # type: int weeks=0, # type: int days=0, # type: int hours=0, # type: int minutes=0, # type: int seconds=0, # type: int microseconds=0 ): # type: (...) -> Union[datetime, date] """ 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) 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, is_now=True, absolute=False, locale=None): if locale is None: locale = get_locale() return difference_formatter.format(diff, is_now, absolute, locale) def _sign(x): return int(copysign(1, x)) # Global helpers @contextmanager def test(mock): set_test_now(mock) yield set_test_now() def set_test_now(test_now=None): pendulum._TEST_NOW = test_now def get_test_now(): # type: () -> pendulum.DateTime return pendulum._TEST_NOW def has_test_now(): # type: () -> bool return pendulum._TEST_NOW is not None def locale(name): return Locale.load(name) def set_locale(name): locale(name) pendulum._LOCALE = name def get_locale(): return pendulum._LOCALE def week_starts_at(wday): 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): if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY: raise ValueError('Invalid week day as start of week.') pendulum._WEEK_ENDS_AT = wday PK!pendulum/locales/__init__.pyPK!pendulum/locales/da/__init__.pyPK!!^pendulum/locales/da/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!_pendulum/locales/da/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ da locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/locales/de/__init__.pyPK!?Xpendulum/locales/de/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!:Siipendulum/locales/de/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ de locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/locales/en/__init__.pyPK!-Pϗpendulum/locales/en/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!C0zzpendulum/locales/en/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ en locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/locales/es/__init__.pyPK!Tvvpendulum/locales/es/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!bL sspendulum/locales/es/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ es locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/locales/fa/__init__.pyPK!9pendulum/locales/fa/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!&ۛ>pendulum/locales/fa/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ fa locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/locales/fo/__init__.pyPK!Npendulum/locales/fo/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!ipendulum/locales/fo/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ fo locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/locales/fr/__init__.py# -*- coding: utf-8 -*- PK!O9nnpendulum/locales/fr/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!o+pendulum/locales/fr/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ fr locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/locales/ko/__init__.pyPK!Z}{pendulum/locales/ko/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!ullpendulum/locales/ko/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ ko locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!,{ pendulum/locales/locale.py# -*- coding: utf-8 -*- import os import re from importlib import import_module from pendulum.utils._compat import basestring from pendulum.utils._compat import decode class Locale: """ Represent a specific locale. """ _cache = {} def __init__(self, locale, data): self._locale = locale self._data = data self._key_cache = {} @classmethod def load(cls, 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 = os.path.join(os.path.dirname(__file__), actual_locale) while not os.path.exists(locale_path): if actual_locale == locale: raise ValueError('Locale [{}] does not exist.'.format(locale)) actual_locale = actual_locale.split('_')[0] m = import_module('pendulum.locales.{}.locale'.format(actual_locale)) cls._cache[locale] = cls(locale, m.locale) return cls._cache[locale] @classmethod def normalize_locale(cls, locale): m = re.match('([a-z]{2})[-_]([a-z]{2})', locale, re.I) if m: return '{}_{}'.format(m.group(1).lower(), m.group(2).lower()) else: return locale.lower() def get(self, key, default=None): 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 if isinstance(result, basestring): result = decode(result) self._key_cache[key] = result return self._key_cache[key] def translation(self, key): return self.get('translations.{}'.format(key)) def plural(self, number): return decode(self._data['plural'](number)) def ordinal(self, number): return decode(self._data['ordinal'](number)) def ordinalize(self, number): ordinal = self.get('custom.ordinal.{}'.format(self.ordinal(number))) if not ordinal: return decode('{}'.format(number)) return decode('{}{}'.format(number, ordinal)) def match_translation(self, key, value): translations = self.translation(key) if value not in translations.values(): return None return {v: k for k, v in translations.items()}[value] def __repr__(self): return "{}('{}')".format(self.__class__.__name__, self._locale) PK!pendulum/locales/lt/__init__.pyPK!r.z z pendulum/locales/lt/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!oM pendulum/locales/lt/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ lt locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!"pendulum/locales/pt_br/__init__.pyPK!-ױD pendulum/locales/pt_br/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!ɍLL pendulum/locales/pt_br/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ pt_br locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/locales/zh/__init__.pyPK!Tpendulum/locales/zh/custom.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ 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', }, } PK!pendulum/locales/zh/locale.py# -*- coding: utf-8 -*- from __future__ import unicode_literals """ zh locale file. It has been generated automatically and must not be modified directly. """ from .custom import translations as custom_translations 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 } PK!pendulum/mixins/__init__.py# -*- coding: utf-8 -*- PK!φpendulum/mixins/default.pyfrom ..formatting import Formatter _formatter = Formatter() class FormattableMixing(object): _formatter = _formatter def format(self, fmt, locale=None): """ Formats the instance using the given format. :param fmt: The format to use :type fmt: str :param locale: The locale to use :type locale: str or None :rtype: str """ return self._formatter.format(self, fmt, locale) def for_json(self): """ Methods for automatic json serialization by simplejson :rtype: str """ return str(self) def __format__(self, format_spec): 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): return self.isoformat() PK!w쪛 pendulum/parser.pyfrom __future__ import absolute_import import pendulum import datetime from .parsing import parse as base_parse, _Interval try: from .parsing._iso8601 import Duration as CDuration except ImportError: CDuration = None from .tz import UTC def parse(text, **options): # Use the mock now value if it exists options['now'] = options.get('now', pendulum.get_test_now()) return _parse(text, **options) def _parse(text, **options): """ Parses a string with the given options. :param text: The string to parse. :type text: str :rtype: mixed """ # 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.period( 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(parsed.end, tz=options.get('tz', UTC)) return pendulum.period( 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.period( pendulum.instance(parsed.start, tz=options.get('tz', UTC)), pendulum.instance(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 PK!W_  pendulum/parsing/__init__.pyimport re import copy from datetime import datetime, date, time from dateutil import parser from .exceptions import ParserError try: from ._iso8601 import parse_iso8601 except ImportError: from .iso8601 import parse_iso8601 COMMON = re.compile( # Date (optional) '^' '(?P' ' (?P' # Classic date (YYYY-MM-DD) ' (?P\d{4})' # Year ' (?P' ' (?P[/:])?(?P\d{2})' # Month (optional) ' ((?P[/:])?(?P\d{2}))' # Day (optional) ' )?' ' )' ')?' # Time (optional) '(?P