summaryrefslogtreecommitdiffstats
path: root/PKG-INFO
blob: ff329a6eac3c89ba2c16bf4a765ef2fd3cc13697 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
Metadata-Version: 2.1
Name: pendulum
Version: 2.1.2
Summary: Python datetimes made easy
Home-page: https://pendulum.eustace.io
License: MIT
Keywords: datetime,date,time
Author: Sébastien Eustace
Author-email: sebastien@eustace.io
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Requires-Dist: python-dateutil (>=2.6,<3.0)
Requires-Dist: pytzdata (>=2020.1)
Requires-Dist: typing (>=3.6,<4.0); python_version < "3.5"
Project-URL: Documentation, https://pendulum.eustace.io/docs
Project-URL: Repository, https://github.com/sdispater/pendulum
Description-Content-Type: text/x-rst

Pendulum
########

.. image:: https://img.shields.io/pypi/v/pendulum.svg
    :target: https://pypi.python.org/pypi/pendulum

.. image:: https://img.shields.io/pypi/l/pendulum.svg
    :target: https://pypi.python.org/pypi/pendulum

.. image:: https://img.shields.io/codecov/c/github/sdispater/pendulum/master.svg
    :target: https://codecov.io/gh/sdispater/pendulum/branch/master

.. image:: https://travis-ci.org/sdispater/pendulum.svg
    :alt: Pendulum Build status
    :target: https://travis-ci.org/sdispater/pendulum

Python datetimes made easy.

Supports Python **2.7** and **3.4+**.


.. code-block:: python

   >>> 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:

.. code-block:: python

    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:

.. code-block:: python

    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:

.. code-block:: python

    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``:

.. code-block:: python

    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
=========

* `Official Website <https://pendulum.eustace.io>`_
* `Documentation <https://pendulum.eustace.io/docs/>`_
* `Issue Tracker <https://github.com/sdispater/pendulum/issues>`_


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 <https://poetry.eustace.io>`_.

.. code-block:: bash

    $ git clone git@github.com: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:

.. code-block:: bash

    ./clock locale create <your-locale>

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

.. code-block:: text

    <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 <https://github.com/sdispater/pendulum/tree/master/pendulum/locales/en/custom.py>`_
data as a reference to see which data is needed.

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