Python datetimes made easy

Overview

Pendulum

Pendulum Build status

Python datetimes made easy.

Supports Python 2.7 and 3.4+.

>>> import pendulum

>>> now_in_paris = pendulum.now('Europe/Paris')
>>> now_in_paris
'2016-07-04T00:49:58.502116+02:00'

# Seamless timezone switching
>>> now_in_paris.in_timezone('UTC')
'2016-07-03T22:49:58.502116+00:00'

>>> tomorrow = pendulum.now().add(days=1)
>>> last_week = pendulum.now().subtract(weeks=1)

>>> past = pendulum.now().subtract(minutes=2)
>>> past.diff_for_humans()
>>> '2 minutes ago'

>>> delta = past - last_week
>>> delta.hours
23
>>> delta.in_words(locale='en')
'6 days 23 hours 58 minutes'

# Proper handling of datetime normalization
>>> pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris')
'2013-03-31T03:30:00+02:00' # 2:30 does not exist (Skipped time)

# Proper handling of dst transitions
>>> just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz='Europe/Paris')
'2013-03-31T01:59:59.999999+01:00'
>>> just_before.add(microseconds=1)
'2013-03-31T03:00:00+02:00'

Why Pendulum?

Native datetime instances are enough for basic cases but when you face more complex use-cases they often show limitations and are not so intuitive to work with. Pendulum provides a cleaner and more easy to use API while still relying on the standard library. So it's still datetime but better.

Unlike other datetime libraries for Python, Pendulum is a drop-in replacement for the standard datetime class (it inherits from it), so, basically, you can replace all your datetime instances by DateTime instances in you code (exceptions exist for libraries that check the type of the objects by using the type function like sqlite3 or PyMySQL for instance).

It also removes the notion of naive datetimes: each Pendulum instance is timezone-aware and by default in UTC for ease of use.

Pendulum also improves the standard timedelta class by providing more intuitive methods and properties.

Why not Arrow?

Arrow is the most popular datetime library for Python right now, however its behavior and API can be erratic and unpredictable. The get() method can receive pretty much anything and it will try its best to return something while silently failing to handle some cases:

arrow.get('2016-1-17')
# <Arrow [2016-01-01T00:00:00+00:00]>

pendulum.parse('2016-1-17')
# <Pendulum [2016-01-17T00:00:00+00:00]>

arrow.get('20160413')
# <Arrow [1970-08-22T08:06:53+00:00]>

pendulum.parse('20160413')
# <Pendulum [2016-04-13T00:00:00+00:00]>

arrow.get('2016-W07-5')
# <Arrow [2016-01-01T00:00:00+00:00]>

pendulum.parse('2016-W07-5')
# <Pendulum [2016-02-19T00:00:00+00:00]>

# Working with DST
just_before = arrow.Arrow(2013, 3, 31, 1, 59, 59, 999999, 'Europe/Paris')
just_after = just_before.replace(microseconds=1)
'2013-03-31T02:00:00+02:00'
# Should be 2013-03-31T03:00:00+02:00

(just_after.to('utc') - just_before.to('utc')).total_seconds()
-3599.999999
# Should be 1e-06

just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, 'Europe/Paris')
just_after = just_before.add(microseconds=1)
'2013-03-31T03:00:00+02:00'

(just_after.in_timezone('utc') - just_before.in_timezone('utc')).total_seconds()
1e-06

Those are a few examples showing that Arrow cannot always be trusted to have a consistent behavior with the data you are passing to it.

Limitations

Even though the DateTime class is a subclass of datetime there are some rare cases where it can't replace the native class directly. Here is a list (non-exhaustive) of the reported cases with a possible solution, if any:

  • sqlite3 will use the type() function to determine the type of the object by default. To work around it you can register a new adapter:
from pendulum import DateTime
from sqlite3 import register_adapter

register_adapter(DateTime, lambda val: val.isoformat(' '))
  • mysqlclient (former MySQLdb) and PyMySQL will use the type() function to determine the type of the object by default. To work around it you can register a new adapter:
import MySQLdb.converters
import pymysql.converters

from pendulum import DateTime

MySQLdb.converters.conversions[DateTime] = MySQLdb.converters.DateTime2literal
pymysql.converters.conversions[DateTime] = pymysql.converters.escape_datetime
  • django will use the isoformat() method to store datetimes in the database. However since pendulum is always timezone aware the offset information will always be returned by isoformat() raising an error, at least for MySQL databases. To work around it you can either create your own DateTimeField or use the previous workaround for MySQLdb:
from django.db.models import DateTimeField as BaseDateTimeField
from pendulum import DateTime


class DateTimeField(BaseDateTimeField):

    def value_to_string(self, obj):
        val = self.value_from_object(obj)

        if isinstance(value, DateTime):
            return value.to_datetime_string()

        return '' if val is None else val.isoformat()

Resources

Contributing

Contributions are welcome, especially with localization.

Getting started

To work on the Pendulum codebase, you'll want to clone the project locally and install the required depedendencies via poetry.

$ git clone [email protected]:sdispater/pendulum.git
$ poetry install

Localization

If you want to help with localization, there are two different cases: the locale already exists or not.

If the locale does not exist you will need to create it by using the clock utility:

./clock locale create <your-locale>

It will generate a directory in pendulum/locales named after your locale, with the following structure:

<your-locale>/
    - custom.py
    - locale.py

The locale.py file must not be modified. It contains the translations provided by the CLDR database.

The custom.py file is the one you want to modify. It contains the data needed by Pendulum that are not provided by the CLDR database. You can take the en data as a reference to see which data is needed.

You should also add tests for the created or modified locale.

Comments
  • Could not build wheels for pendulum which use PEP 517

    Could not build wheels for pendulum which use PEP 517

    • [x] I am on the latest Pendulum version.

    • [x] I have searched the issues of this repo and believe that this is not a duplicate.

    • Alpine 3.11 in Docker

    • Pendulum 2.1.0

    Issue

    I get an error about PEP 517 when trying to install Pendulum. The following Dockerfile (run with docker build .) reproduces the issue.

    The error is ERROR: Could not build wheels for pendulum which use PEP 517 and cannot be installed directly

    FROM python:3.7-alpine3.11
    RUN apk update \
        && apk add gcc musl-dev python3-dev py3-setuptools libffi libffi-dev openssl-dev \
        && pip3 install pendulum==2.1.0
    
    opened by weskerfoot 35
  • datetime compatibility - timestamp int vs function

    datetime compatibility - timestamp int vs function

    Does pendulum aim for full api compatibility with datetime.datetime? It looks like pendulum's timestamp is a property, while datetime's timestamp is a function:

    from datetime import datetime, timezone
    import pendulum
    d = datetime.now(timezone.utc)
    p = pendulum.now(timezone.utc)
    
    d.timestamp
    # <function datetime.timestamp>
    
    d.timestamp()
    # 1478671704.494979
    
    p.timestamp
    # 1478671596
    

    I really wanted to use pendulum under the covers and expose the interface as datetime, but this is a property I expect users to depend on having the usual signature.

    enhancement 
    opened by numberoverzero 29
  • ERROR: Failed building wheel for pendulum

    ERROR: Failed building wheel for pendulum

    • [X] I am on the latest Pendulum version.
    • [X] I have searched the issues of this repo and believe that this is not a duplicate.
    • OS version and name: Windows 10
    • Pendulum version: 2.1.0

    Issue

    Python: Python 3.8.2 PIP: pip 20.0.2

    I am encountering an error installing Python Twitch IRC:

    pip install python-twitch-irc

    Full error message:

    Collecting python-twitch-irc
      Using cached python_twitch_irc-1.1.0-py3-none-any.whl (6.2 kB)
    Collecting pendulum
      Using cached pendulum-2.1.0.tar.gz (80 kB)
      Installing build dependencies ... done
      Getting requirements to build wheel ... done
        Preparing wheel metadata ... done
    Collecting pydle
      Using cached pydle-0.9.3-py3-none-any.whl (43 kB)
    Collecting python-dateutil<3.0,>=2.6
      Using cached python_dateutil-2.8.1-py2.py3-none-any.whl (227 kB)
    Collecting pytzdata>=2018.3
      Using cached pytzdata-2019.3-py2.py3-none-any.whl (489 kB)
    Requirement already satisfied: six>=1.5 in c:\users\nigel\appdata\local\packages\pythonsoftwarefoundation.python.3.8_qbz5n2kfra8p0\localcache\local-packages\python38\site-packages (from python-dateutil<3.0,>=2.6->pendulum->python-twitch-irc) (1.14.0)
    Building wheels for collected packages: pendulum
      Building wheel for pendulum (PEP 517) ... error
      ERROR: Command errored out with exit status 1:
       command: 'C:\Users\nigel\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\python.exe' 'C:\Users\nigel\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\pip\_vendor\pep517\_in_process.py' build_wheel 'C:\Users\nigel\AppData\Local\Temp\tmph3lnsr6j'
           cwd: C:\Users\nigel\AppData\Local\Temp\pip-install-vj7m5mv2\pendulum
      Complete output (24 lines):
      Traceback (most recent call last):
        File "setup.py", line 2, in <module>
          from setuptools import setup
      ModuleNotFoundError: No module named 'setuptools'
      Traceback (most recent call last):
        File "C:\Users\nigel\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\pip\_vendor\pep517\_in_process.py", line 257, in <module>
          main()
        File "C:\Users\nigel\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\pip\_vendor\pep517\_in_process.py", line 240, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
        File "C:\Users\nigel\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\pip\_vendor\pep517\_in_process.py", line 181, in build_wheel
          return _build_backend().build_wheel(wheel_directory, config_settings,
        File "C:\Users\nigel\AppData\Local\Temp\pip-build-env-6ft1gs0c\overlay\Lib\site-packages\poetry\core\masonry\api.py", line 57, in build_wheel
          return unicode(WheelBuilder.make_in(poetry, Path(wheel_directory)))
        File "C:\Users\nigel\AppData\Local\Temp\pip-build-env-6ft1gs0c\overlay\Lib\site-packages\poetry\core\masonry\builders\wheel.py", line 56, in make_in
          wb.build()
        File "C:\Users\nigel\AppData\Local\Temp\pip-build-env-6ft1gs0c\overlay\Lib\site-packages\poetry\core\masonry\builders\wheel.py", line 82, in build
          self._build(zip_file)
        File "C:\Users\nigel\AppData\Local\Temp\pip-build-env-6ft1gs0c\overlay\Lib\site-packages\poetry\core\masonry\builders\wheel.py", line 102, in _build
          self._run_build_command(setup)
        File "C:\Users\nigel\AppData\Local\Temp\pip-build-env-6ft1gs0c\overlay\Lib\site-packages\poetry\core\masonry\builders\wheel.py", line 130, in _run_build_command
          subprocess.check_call(
        File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.752.0_x64__qbz5n2kfra8p0\lib\subprocess.py", line 364, in check_call
          raise CalledProcessError(retcode, cmd)
      subprocess.CalledProcessError: Command '['C:\\Users\\nigel\\AppData\\Local\\Microsoft\\WindowsApps\\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\\python.exe', 'setup.py', 'build', '-b', 'build']' returned non-zero exit status 1.
      ----------------------------------------
      ERROR: Failed building wheel for pendulum
    Failed to build pendulum
    ERROR: Could not build wheels for pendulum which use PEP 517 and cannot be installed directly
    

    Projects are up-to-date:

    Command:

    python -m pip install --upgrade pip setuptools wheel

    Output:

    Requirement already up-to-date: pip in c:\users\nigel\appdata\local\packages\pythonsoftwarefoundation.python.3.8_qbz5n2kfra8p0\localcache\local-packages\python38\site-packages (20.0.2)
    Requirement already up-to-date: setuptools in c:\users\nigel\appdata\local\packages\pythonsoftwarefoundation.python.3.8_qbz5n2kfra8p0\localcache\local-packages\python38\site-packages (46.1.3)
    Requirement already up-to-date: wheel in c:\users\nigel\appdata\local\packages\pythonsoftwarefoundation.python.3.8_qbz5n2kfra8p0\localcache\local-packages\python38\site-packages (0.34.2)
    
    opened by Srizbi84 22
  • Parsing issue

    Parsing issue

    Still on that Debian Wheezy (old-stable), I get a strange behaviour:

     % python3
    Python 3.2.3 (default, Feb 20 2013, 17:02:41) 
    [GCC 4.7.2] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import pendulum
    >>> pendulum.parse('2016-09-04T15:30:09+02:00')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/local/lib/python3.2/dist-packages/pendulum/pendulum.py", line 242, in parse
        tzinfo=tz
      File "/usr/local/lib/python3.2/dist-packages/pendulum/pendulum.py", line 184, in __init__
        hour, minute, second, microsecond
      File "/usr/local/lib/python3.2/dist-packages/pendulum/tz/timezone.py", line 80, in convert
        return dt.__class__(*converted)
    ValueError: day is out of range for month
    >>> 
     % python
    Python 2.7.3 (default, Jun 20 2016, 16:18:47) 
    [GCC 4.7.2] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import pendulum
    >>> pendulum.parse('2016-09-04T15:30:09+02:00')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/local/lib/python2.7/dist-packages/pendulum/pendulum.py", line 242, in parse
        tzinfo=tz
      File "/usr/local/lib/python2.7/dist-packages/pendulum/pendulum.py", line 184, in __init__
        hour, minute, second, microsecond
      File "/usr/local/lib/python2.7/dist-packages/pendulum/tz/timezone.py", line 80, in convert
        return dt.__class__(*converted)
    ValueError: day is out of range for month
    >>> 
    

    This is a quite weird behaviour, on BOTH python versions. Obviously, exact same code works fine on another computer running python3.5 and debian jessie.

    bug 
    opened by guyzmo 19
  • Period length calculation problem

    Period length calculation problem

    I discovered something odd when experimenting with period length calculations around DST change time. Please consider this example:

    tz = pendulum.timezone('America/Los_Angeles')
    start_time = pendulum.parse('2018-03-11 09:00:00+00:00')
    end_time = pendulum.parse('2018-03-11 10:00:00+00:00')
    print((end_time - start_time).hours)     # 1
    print((end_time.in_tz(tz) - start_time).hours)  # 1
    print((end_time - start_time.in_tz(tz)).hours)  # 1
    print((end_time.in_tz(tz) - start_time.in_tz(tz)).hours)  # 2 ?!
    
    p1 = end_time - start_time
    print(start_time + p1 == end_time)   # True
    print(p1.total_hours())  # 1.0
    
    p2 = end_time.in_tz(tz) - start_time.in_tz(tz)
    print(start_time + p2 == end_time)  # False
    print(p2.total_hours())  # 1.0
    
    
    opened by dekoza 17
  • Fix #133: Allow deepcopy on Timezone classes.

    Fix #133: Allow deepcopy on Timezone classes.

    I've added default values to the Timezone classes so that they can be deepcopied. I think it's appropriate to provide these as datetime.tzinfo also has an empty constructor.

    opened by AndreasBackx 15
  • Loader.load portability broken for some pytz installs

    Loader.load portability broken for some pytz installs

    I don't know the full details that make my platform not work with Loader.load but I'm using a Fedora Core 24 box with its stock python3.5 interpretor. I believe that the pytz install I have was installed by pip3 but I'm not 100% sure.

    When I run tests most of them fail because Loader.load is trying to open zoneinfo files in a location that they don't exist. Looking at the code it looks like this is a possibly a non portable way of getting zoneinfo since it makes assumptions about where the zoneinfo files should live.

    I tweaked the exception to print the file location to illustrate..

    ======================================================================
    ERROR: wrap_with_test_now (tests.tz_tests.test_timezone.TimezoneTest)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/home/mayfield/project/pendulum/tests/__init__.py", line 13, in setUp
        LocalTimezone.set_local_timezone(timezone('America/Toronto'))
      File "/home/mayfield/project/pendulum/pendulum/tz/__init__.py", line 16, in timezone
        return Timezone.load(name)
      File "/home/mayfield/project/pendulum/pendulum/tz/timezone.py", line 50, in load
        default_transition_type) = Loader.load(name)
      File "/home/mayfield/project/pendulum/pendulum/tz/loader.py", line 28, in load
        raise TypeError(filepath)
    TypeError: /usr/lib/python3.5/site-packages/pytz/zoneinfo/America/Toronto
    

    In that case there is not a zoneinfo sub dir in /usr/lib/python3.5/site-packages/pytz. But on the same machine this works fine..

    >>> pytz.timezone('America/Toronto')
    <DstTzInfo 'America/Toronto' LMT-1 day, 18:42:00 STD>
    
    opened by mayfield 14
  • _get_unix_timezone incorrect timezone parsing on Amazon Linux AMI release 2018.03

    _get_unix_timezone incorrect timezone parsing on Amazon Linux AMI release 2018.03

    The following happens on Amazon Linux AMI release 2018.03 with pendulum 2.0.0 (but also with 1.5.1):

    Python 3.4.8 (default, Apr 25 2018, 23:50:36)
    [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from pendulum.tz.local_timezone import _get_unix_timezone
    >>> _get_unix_timezone()
    Traceback (most recent call last):
      File "/home/dmitrii/.local/lib/python3.4/site-packages/pendulum/tz/zoneinfo/reader.py", line 49, in read_for
        file_path = pytzdata.tz_path(timezone)
      File "/usr/local/lib/python3.4/site-packages/pytzdata/__init__.py", line 74, in tz_path
        raise TimezoneNotFound('Timezone {} not found at {}'.format(name, filepath))
    pytzdata.exceptions.TimezoneNotFound: Timezone /usr/share/zoneinfo/Europe/Zurich not found at /usr/local/lib/python3.4/site-packages/pytzdata/zoneinfo/usr/share/zoneinfo/Europe/Zurich
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/dmitrii/.local/lib/python3.4/site-packages/pendulum/tz/local_timezone.py", line 206, in _get_unix_timezone
        return Timezone(etctz.replace(' ', '_'))
      File "/home/dmitrii/.local/lib/python3.4/site-packages/pendulum/tz/timezone.py", line 30, in __init__
        tz = read(name, extend=extended)
      File "/home/dmitrii/.local/lib/python3.4/site-packages/pendulum/tz/zoneinfo/__init__.py", line 9, in read
        return Reader(extend=extend).read_for(name)
      File "/home/dmitrii/.local/lib/python3.4/site-packages/pendulum/tz/zoneinfo/reader.py", line 51, in read_for
        raise InvalidTimezone(timezone)
    pendulum.tz.zoneinfo.exceptions.InvalidTimezone: Invalid timezone "/usr/share/zoneinfo/Europe/Zurich"
    

    The issue seems to be that _get_unix_timezone is unable to parse the string 'ZONE="/usr/share/zoneinfo/Europe/Zurich"\n which is returned when the zonefile is read.

    I think the issue is here that you expect there Europe/Zurich.

    opened by Dmitrii-I 13
  • Can't install pendulum with pip install -t

    Can't install pendulum with pip install -t

    Pendulum package is missing when installed with pip install -t

    ~/tmp> pip install -t lib pendulum
    Collecting pendulum
    Collecting pytz (from pendulum)
      Using cached pytz-2016.6.1-py2.py3-none-any.whl
    Collecting tzlocal (from pendulum)
    Collecting python-dateutil (from pendulum)
      Using cached python_dateutil-2.5.3-py2.py3-none-any.whl
    Collecting six>=1.5 (from python-dateutil->pendulum)
      Using cached six-1.10.0-py2.py3-none-any.whl
    Installing collected packages: pytz, tzlocal, six, python-dateutil, pendulum
    Successfully installed pendulum python-dateutil-2.5.3 pytz-2016.6.1 six-1.10.0 tzlocal-1.2.2
    
    ~/tmp> ls -l lib/
    total 64
    drwxrwxr-x 1 lauris lauris   276 Aug 17 17:41 dateutil
    drwxrwxr-x 1 lauris lauris   154 Aug 17 17:41 python_dateutil-2.5.3.dist-info
    drwxrwxr-x 1 lauris lauris   272 Aug 17 17:41 pytz
    drwxrwxr-x 1 lauris lauris   154 Aug 17 17:41 pytz-2016.6.1.dist-info
    drwxrwxr-x 1 lauris lauris   138 Aug 17 17:41 six-1.10.0.dist-info
    -rw-rw-r-- 1 lauris lauris 30098 Aug 17 17:41 six.py
    -rw-rw-r-- 1 lauris lauris 29545 Aug 17 17:41 six.pyc
    drwxrwxr-x 1 lauris lauris   254 Aug 17 17:41 tzlocal
    drwxrwxr-x 1 lauris lauris   154 Aug 17 17:41 tzlocal-1.2.2.dist-info
    
    ~/tmp> pip --version
    pip 8.1.2 from /usr/lib/python2.7/site-packages (python 2.7)
    
    opened by alekna 13
  • Incorrect formatting inside of f-string

    Incorrect formatting inside of f-string

    datetime:

    >>> import datetime
    >>> now = datetime.datetime.now()
    >>> f'{now:%H:%M %d.%m.%Y}'
    '15:39 16.05.2018'
    

    pendulum 2.0.1:

    >>> import pendulum
    >>> now = pendulum.now()
    >>> f'{now:%H:%M %d.%m.%Y}'
    '%15:%5 %3.%39.%2018'
    
    bug formatting 
    opened by Cykooz 12
  • pendulum object has no attribute 'translate'.

    pendulum object has no attribute 'translate'.

    I am using apache airflow 1.10 and the insallation of airflow also installed pendulam. So while running the job it throws an error like pendulum object has no attribute 'translate'. i am using mysql

    opened by lijoev 11
  • Bump setuptools from 63.4.1 to 65.5.1

    Bump setuptools from 63.4.1 to 65.5.1

    Bumps setuptools from 63.4.1 to 65.5.1.

    Changelog

    Sourced from setuptools's changelog.

    v65.5.1

    Misc ^^^^

    • #3638: Drop a test dependency on the mock package, always use :external+python:py:mod:unittest.mock -- by :user:hroncok
    • #3659: Fixed REDoS vector in package_index.

    v65.5.0

    Changes ^^^^^^^

    • #3624: Fixed editable install for multi-module/no-package src-layout projects.
    • #3626: Minor refactorings to support distutils using stdlib logging module.

    Documentation changes ^^^^^^^^^^^^^^^^^^^^^

    • #3419: Updated the example version numbers to be compliant with PEP-440 on the "Specifying Your Project’s Version" page of the user guide.

    Misc ^^^^

    • #3569: Improved information about conflicting entries in the current working directory and editable install (in documentation and as an informational warning).
    • #3576: Updated version of validate_pyproject.

    v65.4.1

    Misc ^^^^

    v65.4.0

    Changes ^^^^^^^

    v65.3.0

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Update mypy and fix issues

    Update mypy and fix issues

    Pull Request Check List

    • [ ] Added tests for changed code.
    • [ ] Updated documentation for changed code.

    This upgrades mypy to 0.991 and fixes the errors associated with it. I've also removed the mypy check from pre-commit and added a type checking step during unit testing. This requires running poetry install twice to remove the type checking dependencies to ensure that there are no runtime dependencies on the type checking libraries (like typing_extensions). Another option would be to change the pre-commit config to use mypy via poetry, remove the third-party pre-commit PR hook, and add a Github action that invokes pre-commit with the proper poetry install. That can be done as a separate PR, though.

    opened by bryanforbes 0
  • Switch `METH_VARGS` to `METH_O`

    Switch `METH_VARGS` to `METH_O`

    What are you trying to accomplish?

    While working on ciso8601, I discovered that things run a lot faster if you define your methods as METH_O (i.e., method with a single object argument) you can skip the overhead of PyArg_ParseTuple.

    What approach did you choose and why?

    Switched the instances of METH_VARGS to be METH_O instead.

    The impact of these changes

    | Item | Performance of parse_iso8601 | |--------------|-----------| | master | 1000000, 0.2697502020164393 => #270 nsec | | This PR | 1000000, 0.22211680599139072 => #222 nsec | | v2.1.2 | 2000000, 0.3753614659945015 => #188 nsec |

    This PR shaves ~48 ns (17%) off the runtime of parse_iso8601.

    Testing

    Here's the script I used.

    import timeit
    
    UNITS = {"nsec": 1e-9, "usec": 1e-6, "msec": 1e-3, "sec": 1.0}
    SCALES = sorted([(scale, unit) for unit, scale in UNITS.items()], reverse=True)
    
    def format_duration(duration):
        # Based on cPython's `timeit` CLI formatting
        scale, unit = next(((scale, unit) for scale, unit in SCALES if duration >= scale), SCALES[-1])
        precision = 3
        return "%.*g %s" % (precision, duration / scale, unit)
    
    
    timer = timeit.Timer(stmt="parse_iso8601('2014-01-09T21:48:00')", setup="from pendulum.parsing import parse_iso8601")
    count, time_taken = timer.autorange()
    timing = float(time_taken) / int(count)
    print(f"{count}, {time_taken} => #{format_duration(timing)}")
    

    What should reviewers focus on?

    I didn't bother supporting Python 2.7 in these changes. It's fairly easy to do so, but I noticed that pendulum dropped support for Python < 3.7, so I didn't bother.

    I'm not sure why v2.1.2 performed so well in this test. I didn't look into it. 🤷

    Pull Request Check List

    • [ ] ~Added tests for changed code.~
      • Interface didn't change.
    • [ ] ~Updated documentation for changed code.~
      • Interface didn't change. No publicly facing changes. Just performance.
    opened by movermeyer 0
  • Mypy improvement

    Mypy improvement

    Pull Request Check List

    • [ ] Added tests for changed code. Not needed
    • [ ] Updated documentation for changed code. No changes require documenting

    Mypy 0.990 added show_error_codes as enabled by default and our # type: ignore comments had wrong codes which caused a failure in #674.

    Second commit moves mypy usage away from pre-commit, since when it's being run from pre-commit, it might miss some typing issues coming from dependencies. See https://github.com/python-poetry/cleo/pull/254#issuecomment-1267690650

    opened by Secrus 0
  • arithmetic calculation of datetime is not correct if a month has 31 days

    arithmetic calculation of datetime is not correct if a month has 31 days

    • [x] I am on the latest Pendulum version.
    • [x] I have searched the issues of this repo and believe that this is not a duplicate.
    • OS version and name: Linux 5.10.109-104.500.amzn2.x86_64 (AWS EC2)
    • Pendulum version: 2.1.2

    Issue

    When calculating datetime A-B+B, I can't get A. Please see below. (If A is a month what has 31 days)

    from pendulum import datetime
    datetime(2022, 11, 10, 0, 30, tz='Asis/Seoul') - datetime(2022, 10, 10, 1, 30, tz='Asis/Seoul') + datetime(2022, 10,10, 1, 30, tz='Asis/Seoul')
    # DateTime(2022, 11, 11, 0, 30, 0, tzinfo=Timezone('Asia/Seoul'))
    # Expected value : DateTime(2022, 11, 10, 0, 30, 0, tzinfo=Timezone('Asia/Seoul'))
    

    But I can get expected datetime if A is month what has 30 days.

    from pendulum import datetime
    datetime(2022, 10, 10, 0, 30, tz='Asis/Seoul') - datetime(2022, 9, 10, 1, 30, tz='Asis/Seoul') + datetime(2022, 9, 10, 1, 30, tz='Asis/Seoul')
    # DateTime(2022, 10, 10, 0, 30, 0, tzinfo=Timezone('Asia/Seoul'))
    
    opened by wizneo 0
Releases(3.0.0a1)
Owner
Sébastien Eustace
Software engineer, proud pythonista, open source lover. Creator of the Poetry package manager and the datetime library Pendulum.
Sébastien Eustace
Parse human-readable date/time strings

parsedatetime Parse human-readable date/time strings. Python 2.6 or greater is required for parsedatetime version 1.0 or greater. While we still test

Mike Taylor 651 Dec 23, 2022
UNIX time from NTP or short UtfN is a simple CLI tool to set the time from an NTP-Server.

UNIX ⌚ from NTP UNIX time from NTP or short UtfN is a simple CLI tool to set the time from an NTP-Server. Sets time and date using the date command pr

Alexander 1 Jan 02, 2022
A datetime parser in Python by Ari24-cb24 and NekoFantic

datetimeparser A datetime parser in Python by Ari24-cb24 and NekoFantic V 1.0 Erinnerung für den Parser Auf falsche Eingaben überprüfen Liste an Event

AriDevelopment 13 Dec 30, 2022
Useful extensions to the standard Python datetime features

dateutil - powerful extensions to datetime The dateutil module provides powerful extensions to the standard datetime module, available in Python. Inst

2k Dec 29, 2022
A Python module that tries to figure out what your local timezone is

tzlocal This Python module returns a tzinfo object with the local timezone information under Unix and Windows. It requires either Python 3.9+ or the b

Lennart Regebro 159 Dec 16, 2022
pytz Python historical timezone library and database

pytz Brings the IANA tz database into Python. This library allows accurate and cross platform timezone calculations. pytz contains generated code, and

Stub 236 Jan 03, 2023
A Python library for dealing with dates

moment A Python library for dealing with dates/times. Inspired by Moment.js and Kenneth Reitz's Requests library. Ideas were also taken from the Times

Zach Williams 709 Dec 09, 2022
Generate and work with holidays in Python

python-holidays A fast, efficient Python library for generating country, province and state specific sets of holidays on the fly. It aims to make dete

Maurizio Montel 881 Dec 29, 2022
Better dates & times for Python

Arrow: Better dates & times for Python Arrow is a Python library that offers a sensible and human-friendly approach to creating, manipulating, formatt

Arrow 8.2k Jan 05, 2023
Datetimes for Humans™

Maya: Datetimes for Humans™ Datetimes are very frustrating to work with in Python, especially when dealing with different locales on different systems

Timo Furrer 3.4k Dec 28, 2022
Friendly Python Dates

When.py: Friendly Dates and Times Production: Development: User-friendly functions to help perform common date and time actions. Usage To get the syst

Andy Dirnberger 191 Oct 14, 2022
An python based Timer and Digital Clock

Python-based-Timer- An python based Timer and Digital Clock How to contribute to this repo ❓ Step 1: Fork the this repository Step 2: Clone your fork

Bauddhik-Geeks 3 Sep 16, 2022
ISO 8601 date/time parser

ISO 8601 date/time parser This module implements ISO 8601 date, time and duration parsing. The implementation follows ISO8601:2004 standard, and imple

118 Dec 20, 2022
Make Python datetime formatting human readable

Make Python datetime formatting human readable

James Timmins 0 Oct 03, 2021
🏹 Better dates & times for Python

Arrow: Better dates & times for Python Arrow is a Python library that offers a sensible and human-friendly approach to creating, manipulating, formatt

Arrow 8.2k Jan 09, 2023
Cross Platform Application for Calculating Render Time

mdsanima-rt-go Cross Platform Application for Calculating Render Time. Testing This is a base application build on Windows Android and Linux. All buil

MDSANIMA DEV 2 Mar 29, 2022
Python datetimes made easy

Pendulum Python datetimes made easy. Supports Python 2.7 and 3.4+. import pendulum now_in_paris = pendulum.now('Europe/Paris') now_in_par

Sébastien Eustace 5.3k Jan 06, 2023
Croniter provides iteration for the datetime object with a cron like format

Introduction Contents Introduction Travis badge Usage About DST About second repeats Testing if a date matches a crontab Gaps between date matches Ite

kiorky 152 Dec 30, 2022
A simple in-process python scheduler library, designed to be integrated seamlessly with the `datetime` standard library.

scheduler A simple in-process python scheduler library, designed to be integrated seamlessly with the datetime standard library. Due to the support of

30 Dec 30, 2022
E-Ink Magic Calendar that automatically syncs to Google Calendar and runs off a battery powered Raspberry Pi Zero

E-Ink Magic Calendar that automatically syncs to Google Calendar and runs off a battery powered Raspberry Pi Zero

2.8k Jan 06, 2023