From 665666d6f4213da8db57ebb480947b7caf1fe382 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 17 Dec 2023 15:36:26 +0100 Subject: Merging upstream version 3.0.0. Signed-off-by: Daniel Baumann --- docs/docs/difference.md | 2 +- docs/docs/index.md | 2 +- docs/docs/installation.md | 20 ++++++ docs/docs/interval.md | 168 ++++++++++++++++++++++++++++++++++++++++++++++ docs/docs/introduction.md | 2 +- docs/docs/limitations.md | 4 +- docs/docs/period.md | 168 ---------------------------------------------- docs/docs/testing.md | 94 +++++++++++++++++--------- docs/docs/timezones.md | 2 +- 9 files changed, 255 insertions(+), 207 deletions(-) create mode 100644 docs/docs/interval.md delete mode 100644 docs/docs/period.md (limited to 'docs') diff --git a/docs/docs/difference.md b/docs/docs/difference.md index 3a7f063..2653f01 100644 --- a/docs/docs/difference.md +++ b/docs/docs/difference.md @@ -1,6 +1,6 @@ # Difference -The `diff()` method returns a [Period](#period) instance that represents the total duration +The `diff()` method returns an [Interval](#interval) instance that represents the total duration between two `DateTime` instances. This interval can be then expressed in various units. These interval methods always return *the total difference expressed* in the specified time requested. All values are truncated and not rounded. diff --git a/docs/docs/index.md b/docs/docs/index.md index daca205..107e043 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -12,6 +12,6 @@ {!docs/modifiers.md!} {!docs/timezones.md!} {!docs/duration.md!} -{!docs/period.md!} +{!docs/interval.md!} {!docs/testing.md!} {!docs/limitations.md!} diff --git a/docs/docs/installation.md b/docs/docs/installation.md index bfa9641..9f80e87 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -11,3 +11,23 @@ or, if you are using [poetry](https://python-poetry.org): ```bash $ poetry add pendulum ``` + +## Optional features + +Pendulum provides optional features that you must explicitly require in order to use them. + +These optional features are: + +- `test`: Provides a set of helpers to make testing easier by allowing you to control the flow of time. + +You can install them by specifying them when installing Pendulum + +```bash +$ pip install pendulum[test] +``` + +or, if you are using [poetry](https://python-poetry.org): + +```bash +$ poetry add pendulum[test] +``` diff --git a/docs/docs/interval.md b/docs/docs/interval.md new file mode 100644 index 0000000..fc70fb3 --- /dev/null +++ b/docs/docs/interval.md @@ -0,0 +1,168 @@ +# Interval + +When you subtract a `DateTime` instance from another, or use the `diff()` method, it will return an `Interval` instance. +It inherits from the [Duration](#duration) class with the added benefit that it is aware of the +instances that generated it, so that it can give access to more methods and properties: + +```python +>>> import pendulum + +>>> start = pendulum.datetime(2000, 11, 20) +>>> end = pendulum.datetime(2016, 11, 5) + +>>> interval = end - start + +>>> interval.years +15 +>>> interval.months +11 +>>> interval.in_years() +15 +>>> interval.in_months() +191 + +# Note that the weeks property +# will change compared to the Duration class +>>> interval.weeks +2 # 832 for the duration + +# However the days property will still remain the same +# to keep the compatibility with the timedelta class +>>> interval.days +5829 +``` + +Be aware that an interval, just like an duration, is compatible with the `timedelta` class regarding +its attributes. However, its custom attributes (like `remaining_days`) will be aware of any DST +transitions that might have occurred and adjust accordingly. Let's take an example: + +```python +>>> import pendulum + +>>> start = pendulum.datetime(2017, 3, 7, tz='America/Toronto') +>>> end = start.add(days=6) + +>>> interval = end - start + +# timedelta properties +>>> interval.days +5 +>>> interval.seconds +82800 + +# interval properties +>>> interval.remaining_days +6 +>>> interval.hours +0 +>>> interval.remaining_seconds +0 +``` + +!!!warning + + Due to their nature (fixed duration between two datetimes), most arithmetic operations will + return a `Duration` instead of an `Interval`. + + ```python + >>> import pendulum + + >>> dt1 = pendulum.datetime(2016, 8, 7, 12, 34, 56) + >>> dt2 = dt1.add(days=6, seconds=34) + >>> interval = pendulum.interval(dt1, dt2) + >>> interval * 2 + Duration(weeks=1, days=5, minutes=1, seconds=8) + ``` + + +## Instantiation + +You can create an instance by using the `interval()` helper: + +```python + +>>> import pendulum + +>>> start = pendulum.datetime(2000, 1, 1) +>>> end = pendulum.datetime(2000, 1, 31) + +>>> interval = pendulum.interval(start, end) +``` + +You can also make an inverted interval: + +```python +>>> interval = pendulum.interval(end, start) +>>> interval.remaining_days +-2 +``` + +If you have inverted dates but want to make sure that the interval is positive, +you should set the `absolute` keyword argument to `True`: + +```python +>>> interval = pendulum.interval(end, start, absolute=True) +>>> interval.remaining_days +2 +``` + +## Range + +If you want to iterate over a interval, you can use the `range()` method: + +```python +>>> import pendulum + +>>> start = pendulum.datetime(2000, 1, 1) +>>> end = pendulum.datetime(2000, 1, 10) + +>>> interval = pendulum.interval(start, end) + +>>> for dt in interval.range('days'): +>>> print(dt) + +'2000-01-01T00:00:00+00:00' +'2000-01-02T00:00:00+00:00' +'2000-01-03T00:00:00+00:00' +'2000-01-04T00:00:00+00:00' +'2000-01-05T00:00:00+00:00' +'2000-01-06T00:00:00+00:00' +'2000-01-07T00:00:00+00:00' +'2000-01-08T00:00:00+00:00' +'2000-01-09T00:00:00+00:00' +'2000-01-10T00:00:00+00:00' +``` + +!!!note + + Supported units for `range()` are: `years`, `months`, `weeks`, + `days`, `hours`, `minutes`, `seconds` and `microseconds` + +You can pass an amount for the passed unit to control the length of the gap: + +```python +>>> for dt in interval.range('days', 2): +>>> print(dt) + +'2000-01-01T00:00:00+00:00' +'2000-01-03T00:00:00+00:00' +'2000-01-05T00:00:00+00:00' +'2000-01-07T00:00:00+00:00' +'2000-01-09T00:00:00+00:00' +``` + +You can also directly iterate over the `Interval` instance, +the unit will be `days` in this case: + +```python +>>> for dt in interval: +>>> print(dt) +``` + +You can check if a `DateTime` instance is inside a interval using the `in` keyword: + +```python +>>> dt = pendulum.datetime(2000, 1, 4) +>>> dt in interval +True +``` diff --git a/docs/docs/introduction.md b/docs/docs/introduction.md index fbbab97..0078b48 100644 --- a/docs/docs/introduction.md +++ b/docs/docs/introduction.md @@ -18,4 +18,4 @@ For example, all comparisons are done in `UTC` or in the timezone of the datetim 3 ``` -The default timezone, except when using the `now()`, method will always be `UTC`. +The default timezone, except when using the `now()` method, will always be `UTC`. diff --git a/docs/docs/limitations.md b/docs/docs/limitations.md index 7deff23..913aca1 100644 --- a/docs/docs/limitations.md +++ b/docs/docs/limitations.md @@ -4,7 +4,7 @@ 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 the `type()` function to determine the type of the object by default. To work around it you can register a new adapter: +* `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: ```python import pendulum @@ -13,7 +13,7 @@ Here is a list (non-exhaustive) of the reported cases with a possible solution, register_adapter(pendulum.DateTime, lambda val: val.isoformat(' ')) ``` -* `mysqlclient` (former `MySQLdb`) and `PyMySQL` will use the the `type()` function to determine the type of the object by default. To work around it you can register a new adapter: +* `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: ```python import pendulum diff --git a/docs/docs/period.md b/docs/docs/period.md deleted file mode 100644 index a4dde16..0000000 --- a/docs/docs/period.md +++ /dev/null @@ -1,168 +0,0 @@ -# Period - -When you subtract a `DateTime` instance from another, or use the `diff()` method, it will return a `Period` instance. -It inherits from the [Duration](#duration) class with the added benefit that it is aware of the -instances that generated it, so that it can give access to more methods and properties: - -```python ->>> import pendulum - ->>> start = pendulum.datetime(2000, 11, 20) ->>> end = pendulum.datetime(2016, 11, 5) - ->>> period = end - start - ->>> period.years -15 ->>> period.months -11 ->>> period.in_years() -15 ->>> period.in_months() -191 - -# Note that the weeks property -# will change compared to the Duration class ->>> period.weeks -2 # 832 for the duration - -# However the days property will still remain the same -# to keep the compatibility with the timedelta class ->>> period.days -5829 -``` - -Be aware that a period, just like an interval, is compatible with the `timedelta` class regarding -its attributes. However, its custom attributes (like `remaining_days`) will be aware of any DST -transitions that might have occurred and adjust accordingly. Let's take an example: - -```python ->>> import pendulum - ->>> start = pendulum.datetime(2017, 3, 7, tz='America/Toronto') ->>> end = start.add(days=6) - ->>> period = end - start - -# timedelta properties ->>> period.days -5 ->>> period.seconds -82800 - -# period properties ->>> period.remaining_days -6 ->>> period.hours -0 ->>> period.remaining_seconds -0 -``` - -!!!warning - - Due to their nature (fixed duration between two datetimes), most arithmetic operations will - return a `Duration` instead of a `Period`. - - ```python - >>> import pendulum - - >>> dt1 = pendulum.datetime(2016, 8, 7, 12, 34, 56) - >>> dt2 = dt1.add(days=6, seconds=34) - >>> period = pendulum.period(dt1, dt2) - >>> period * 2 - Duration(weeks=1, days=5, minutes=1, seconds=8) - ``` - - -## Instantiation - -You can create an instance by using the `period()` helper: - -```python - ->>> import pendulum - ->>> start = pendulum.datetime(2000, 1, 1) ->>> end = pendulum.datetime(2000, 1, 31) - ->>> period = pendulum.period(start, end) -``` - -You can also make an inverted period: - -```python ->>> period = pendulum.period(end, start) ->>> period.remaining_days --2 -``` - -If you have inverted dates but want to make sure that the period is positive, -you should set the `absolute` keyword argument to `True`: - -```python ->>> period = pendulum.period(end, start, absolute=True) ->>> period.remaining_days -2 -``` - -## Range - -If you want to iterate over a period, you can use the `range()` method: - -```python ->>> import pendulum - ->>> start = pendulum.datetime(2000, 1, 1) ->>> end = pendulum.datetime(2000, 1, 10) - ->>> period = pendulum.period(start, end) - ->>> for dt in period.range('days'): ->>> print(dt) - -'2000-01-01T00:00:00+00:00' -'2000-01-02T00:00:00+00:00' -'2000-01-03T00:00:00+00:00' -'2000-01-04T00:00:00+00:00' -'2000-01-05T00:00:00+00:00' -'2000-01-06T00:00:00+00:00' -'2000-01-07T00:00:00+00:00' -'2000-01-08T00:00:00+00:00' -'2000-01-09T00:00:00+00:00' -'2000-01-10T00:00:00+00:00' -``` - -!!!note - - Supported units for `range()` are: `years`, `months`, `weeks`, - `days`, `hours`, `minutes`, `seconds` and `microseconds` - -You can pass an amount for the passed unit to control the length of the gap: - -```python ->>> for dt in period.range('days', 2): ->>> print(dt) - -'2000-01-01T00:00:00+00:00' -'2000-01-03T00:00:00+00:00' -'2000-01-05T00:00:00+00:00' -'2000-01-07T00:00:00+00:00' -'2000-01-09T00:00:00+00:00' -``` - -You can also directly iterate over the `Period` instance, -the unit will be `days` in this case: - -```python ->>> for dt in period: ->>> print(dt) -``` - -You can check if a `DateTime` instance is inside a period using the `in` keyword: - -```python ->>> dt = pendulum.datetime(2000, 1, 4) ->>> dt in period -True -``` diff --git a/docs/docs/testing.md b/docs/docs/testing.md index dfca054..25aad8d 100644 --- a/docs/docs/testing.md +++ b/docs/docs/testing.md @@ -1,59 +1,87 @@ # Testing -The testing methods allow you to set a `DateTime` instance (real or mock) to be returned -when a "now" instance is created. -The provided instance will be returned specifically under the following conditions: +Pendulum provides a few helpers to help you control the flow of time in your tests. Note that +these helpers are only available if you opted in the `test` extra during [installation](#installation). -* A call to the `now()` method, ex. `pendulum.now()`. -* When the string "now" is passed to the `parse()` method, ex. `pendulum.parse('now')` +!!!warning + If you are migrating from Pendulum 2, note that the `set_test_now()` and `test()` + helpers have been removed. + + +## Relative time travel + +You can travel in time relatively to the current time ```python >>> import pendulum -# Create testing datetime ->>> known = pendulum.datetime(2001, 5, 21, 12) +>>> now = pendulum.now() +>>> pendulum.travel(minutes=5) +>>> pendulum.now().diff_for_humans(now) +"5 minutes after" +``` -# Set the mock ->>> pendulum.set_test_now(known) +Note that once you've travelled in time the clock **keeps ticking**. If you prefer to stop the time completely +you can use the `freeze` parameter: ->>> print(pendulum.now()) -'2001-05-21T12:00:00+00:00' +```python +>>> import pendulum ->>> print(pendulum.parse('now')) -'2001-05-21T12:00:00+00:00' +>>> now = pendulum.now() +>>> pendulum.travel(minutes=5, freeze=True) +>>> pendulum.now().diff_for_humans(now) +"5 minutes after" # This will stay like this indefinitely +``` -# Clear the mock ->>> pendulum.set_test_now() ->>> print(pendulum.now()) -'2016-07-10T22:10:33.954851-05:00' -``` +## Absolute time travel -Related methods will also return values mocked according to the **now** instance. +Sometimes, you may want to place yourself at a specific point in time. +This is possible by using the `travel_to()` helper. This helper accepts a `DateTime` instance +that represents the point in time where you want to travel to. ```python ->>> print(pendulum.today()) -'2001-05-21T00:00:00+00:00' +>>> import pendulum ->>> print(pendulum.tomorrow()) -'2001-05-22T00:00:00+00:00' +>>> pendulum.travel_to(pendulum.yesterday()) +``` ->>> print(pendulum.yesterday()) -'2001-05-20T00:00:00+00:00' +Similarly to `travel`, it's important to remember that, by default, the time keeps ticking so, if you prefer +stopping the time, use the `freeze` parameter: + +```python +>>> import pendulum + +>>> pendulum.travel_to(pendulum.yesterday(), freeze=True) ``` -If you don't want to manually clear the mock (or you are afraid of forgetting), -you can use the provided `test()` contextmanager. +## Travelling back to the present + +Using any of the travel helpers will keep you in the past, or future, until you decide +to travel back to the present time. To do so, you may use the `travel_back()` helper. ```python >>> import pendulum ->>> known = pendulum.datetime(2001, 5, 21, 12) +>>> now = pendulum.now() +>>> pendulum.travel(minutes=5, freeze=True) +>>> pendulum.now().diff_for_humans(now) +"5 minutes after" +>>> pendulum.travel_back() +>>> pendulum.now().diff_for_humans(now) +"a few seconds after" +``` + +However, it might be cumbersome to remember to travel back so, instead, you can use any of the helpers as a context +manager: ->>> with pendulum.test(known): ->>> print(pendulum.now()) -'2001-05-21T12:00:00+00:00' +```python +>>> import pendulum ->>> print(pendulum.now()) -'2016-07-10T22:10:33.954851-05:00' +>>> now = pendulum.now() +>>> with pendulum.travel(minutes=5, freeze=True): +>>> pendulum.now().diff_for_humans(now) +"5 minutes after" +>>> pendulum.now().diff_for_humans(now) +"a few seconds after" ``` diff --git a/docs/docs/timezones.md b/docs/docs/timezones.md index 85ff147..e70034e 100644 --- a/docs/docs/timezones.md +++ b/docs/docs/timezones.md @@ -67,7 +67,7 @@ adopt the proper behavior and apply the transition accordingly. >>> dt = dt.add(microseconds=1) '2013-03-31T03:00:00+02:00' >>> dt.subtract(microseconds=1) -'2013-03-31T01:59:59.999998+01:00' +'2013-03-31T01:59:59.999999+01:00' >>> dt = pendulum.datetime(2013, 10, 27, 2, 59, 59, 999999, tz='Europe/Paris', -- cgit v1.2.3