### Docs/Addition Subtraction # Addition and Subtraction To easily add and subtract time, you can use the `add()` and `subtract()` methods. Each method returns a new `DateTime` instance. ``` /* Detailed source-code truncated for AI context efficiency. */ ``` !!!note Passing negative values to `add()` is also possible and will act exactly like `subtract()` --- ### Docs/Attributes Properties # Attributes and Properties Pendulum gives access to more attributes and properties than the default ``datetime`` class. ``` /* Detailed source-code truncated for AI context efficiency. */ ``` --- ### Docs/Comparison # Comparison Simple comparison is offered up via the basic operators. Remember that the comparison is done in the UTC timezone so things aren't always as they seem. ```python >>> import pendulum >>> first = pendulum.datetime(2012, 9, 5, 23, 26, 11, 0, tz='America/Toronto') >>> second = pendulum.datetime(2012, 9, 5, 20, 26, 11, 0, tz='America/Vancouver') >>> first.to_datetime_string() '2012-09-05 23:26:11' >>> first.timezone_name 'America/Toronto' >>> second.to_datetime_string() '2012-09-05 20:26:11' >>> second.timezone_name 'America/Vancouver' >>> first == second True >>> first != second False >>> first > second False >>> first >= second True >>> first < second False >>> first <= second True >>> first = first.on(2012, 1, 1).at(0, 0, 0) >>> second = second.on(2012, 1, 1).at(0, 0, 0) # tz is still America/Vancouver for second >>> first == second False >>> first != second True >>> first > second False >>> first >= second False >>> first < second True >>> first <= second True ``` To handle the most used cases there are some simple helper functions. For the methods that compare to `now()` (ex. `is_today()`) in some manner the `now()` is created in the same timezone as the instance. ```python >>> import pendulum >>> dt = pendulum.now() >>> dt.is_past() >>> dt.is_leap_year() >>> born = pendulum.datetime(1987, 4, 23) >>> not_birthday = pendulum.datetime(2014, 9, 26) >>> birthday = pendulum.datetime(2014, 4, 23) >>> past_birthday = pendulum.now().subtract(years=50) >>> born.is_birthday(not_birthday) False >>> born.is_birthday(birthday) True >>> past_birthday.is_birthday() # Compares to now by default True ``` --- ### Docs/Difference # Difference 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. The `diff()` method has a default first parameter which is the `DateTime` instance to compare to, or `None` if you want to use `now()`. The 2nd parameter is optional and indicates if you want the return value to be the absolute value or a relative value that might have a `-` (negative) sign if the passed in date is less than the current instance. This will default to `True`, return the absolute value. ```python >>> import pendulum >>> dt_ottawa = pendulum.datetime(2000, 1, 1, tz='America/Toronto') >>> dt_vancouver = pendulum.datetime(2000, 1, 1, tz='America/Vancouver') >>> dt_ottawa.diff(dt_vancouver).in_hours() 3 >>> dt_ottawa.diff(dt_vancouver, False).in_hours() 3 >>> dt_vancouver.diff(dt_ottawa, False).in_hours() -3 >>> dt = pendulum.datetime(2012, 1, 31, 0) >>> dt.diff(dt.add(months=1)).in_days() 29 >>> dt.diff(dt.subtract(months=1), False).in_days() -31 >>> dt = pendulum.datetime(2012, 4, 30, 0) >>> dt.diff(dt.add(months=1)).in_days() 30 >>> dt.diff(dt.add(weeks=1)).in_days() 7 >>> dt = pendulum.datetime(2012, 1, 1, 0) >>> dt.diff(dt.add(seconds=59)).in_minutes() 0 >>> dt.diff(dt.add(seconds=60)).in_minutes() 1 >>> dt.diff(dt.add(seconds=119)).in_minutes() 1 >>> dt.diff(dt.add(seconds=120)).in_minutes() 2 ``` Difference for Humans --------------------- The `diff_for_humans()` method will add a phrase after the difference value relative to the instance and the passed in instance. There are 4 possibilities: * When comparing a value in the past to default now: * 1 hour ago * 5 months ago * When comparing a value in the future to default now: * 1 hour from now * 5 months from now * When comparing a value in the past to another value: * 1 hour before * 5 months before * When comparing a value in the future to another value: * 1 hour after * 5 months after You may also pass `True` as a 2nd parameter to remove the modifiers `ago`, `from now`, etc. ```python >>> import pendulum # The most typical usage is for comments # The instance is the date the comment was created # and its being compared to default now() >>> pendulum.now().subtract(days=1).diff_for_humans() '1 day ago' >>> pendulum.now().diff_for_humans(pendulum.now().subtract(years=1)) '1 year after' >>> dt = pendulum.datetime(2011, 8, 1) >>> dt.diff_for_humans(dt.add(months=1)) '1 month before' >>> dt.diff_for_humans(dt.subtract(months=1)) '1 month after' >>> pendulum.now().add(seconds=5).diff_for_humans() '5 seconds from now' >>> pendulum.now().subtract(days=24).diff_for_humans() '3 weeks ago' >>> pendulum.now().subtract(days=24).diff_for_humans(absolute=True) '3 weeks' ``` You can also change the locale of the string either globally by using `pendulum.set_locale('fr')` before the `diff_for_humans()` call or specifically for the call by passing the `locale` keyword argument. See the [Localization](#localization) section for more detail. ```python >>> import pendulum >>> pendulum.set_locale('de') >>> pendulum.now().add(years=1).diff_for_humans() 'in 1 Jahr' >>> pendulum.now().add(years=1).diff_for_humans(locale='fr') 'dans 1 an' ``` --- ### Docs/Duration # Duration The `Duration` class is inherited from the native `timedelta` class. It has many improvements over the base class. !!!note Even though, it inherits from the `timedelta` class, its behavior is slightly different. The more important to notice is that the native normalization does not happen, this is so that it feels more intuitive. ```python >>> import pendulum >>> import datetime >>> d1 = datetime.datetime(2012, 1, 1, 1, 2, 3, tzinfo=datetime.UTC) >>> d2 = datetime.datetime(2011, 12, 31, 22, 2, 3, tzinfo=datetime.UTC) >>> delta = d2 - d1 >>> delta.days -1 >>> delta.seconds 75600 >>> d1 = pendulum.datetime(2012, 1, 1, 1, 2, 3) >>> d2 = pendulum.datetime(2011, 12, 31, 22, 2, 3) >>> delta = d2 - d1 >>> delta.days 0 >>> delta.hours -3 ``` ## Instantiation To create a `Duration` instance, you can use the `duration()` helper: ```python >>> import pendulum >>> it = pendulum.duration(days=1177, seconds=7284, microseconds=1234) ``` !!!note Unlike the native `timedelta` class, durations support specifying years and months. ```python >>> import pendulum >>> it = pendulum.duration(years=2, months=3) ``` However, to maintain compatibility, native methods and properties will make approximations: ```python >>> it.days 820 >>> it.total_seconds() 70848000.0 ``` ## Properties and Duration Methods The `Duration` class brings more properties than the default `days`, `seconds` and `microseconds`. ```python >>> import pendulum >>> it = pendulum.duration( ... years=2, months=3, ... days=1177, seconds=7284, microseconds=1234 ... ) >>> it.years 2 >>> it.months 3 # Weeks are based on the total of days # It does not take into account years and months >>> it.weeks 168 # Days, just like in timedelta, represents the total of days # in the duration. If years and/or months are specified # it will use an approximation >>> it.days 1997 # If you want the remaining days not included in full weeks >>> it.remaining_days 1 >>> # The remaining number in each unit >>> it.hours 2 >>> it.minutes 1 # Seconds are, like days, a special case and the default # property will return the whole value of remaining # seconds just like the timedelta class for compatibility >>> it.seconds 7284 # If you want the number of seconds not included # in hours and minutes >>> it.remaining_seconds 24 >>> it.microseconds 1234 ``` If you want to get the duration in each supported unit you can use the appropriate methods. ```python # Each method returns a float like the native # total_seconds() method >>> it.total_weeks() 168.15490079569113 >>> it.total_days() 1177.0843055698379 >>> it.total_hours() 28250.02333367611 >>> it.total_minutes() 1695001.4000205665 >>> it.total_seconds() 101700084.001234 ``` Similarly, the `in_xxx()` methods return the total duration in each supported unit as a truncated integer. ```python >>> it.in_weeks() 168 >>> it.in_days() 1997 >>> it.in_hours() 28250 >>> it.in_minutes() 1695001 >>> it.in_seconds() 101700084 ``` It also has a handy `in_words()` method, which determines the duration representation when printed. ```python >>> import pendulum >>> pendulum.set_locale('fr') >>> it = pendulum.duration(days=1177, seconds=7284, microseconds=1234) >>> it.in_words() '168 semaines 1 jour 2 heures 1 minute 24 secondes' >>> print(it) '168 semaines 1 jour 2 heures 1 minute 24 secondes' >>> it.in_words(locale='de') '168 Wochen 1 Tag 2 Stunden 1 Minute 24 Sekunden' ``` --- ### Docs/Fluent Helpers # Fluent helpers Pendulum provides helpers that return a new instance with some attributes modified compared to the original instance. However, none of these helpers, with the exception of explicitly setting the timezone, will change the timezone of the instance. Specifically, setting the timestamp will not set the corresponding timezone to UTC. ```python >>> import pendulum >>> dt = pendulum.now() >>> dt.set(year=1975, month=5, day=21).to_datetime_string() '1975-05-21 13:45:18' >>> dt.set(hour=22, minute=32, second=5).to_datetime_string() '2016-11-16 22:32:05' ``` You can also use the `on()` and `at()` methods to change the date and the time respectively ```python >>> dt.on(1975, 5, 21).at(22, 32, 5).to_datetime_string() '1975-05-21 22:32:05' >>> dt.at(10).to_datetime_string() '2016-11-16 10:00:00' >>> dt.at(10, 30).to_datetime_string() '2016-11-16 10:30:00' ``` You can also modify the timezone. ```python >>> dt.set(tz='Europe/London') ``` Setting the timezone just modifies the timezone information without making any conversion, while `in_timezone()` (or `in_tz()`) converts the time in the appropriate timezone. ```python >>> import pendulum >>> dt = pendulum.datetime(2013, 3, 31, 2, 30) >>> print(dt) '2013-03-31T02:30:00+00:00' >>> dt = dt.set(tz='Europe/Paris') >>> print(dt) '2013-03-31T03:30:00+02:00' >>> dt = dt.in_tz('Europe/Paris') >>> print(dt) '2013-03-31T04:30:00+02:00' >>> dt = dt.set(tz='Europe/Paris').set(tz='UTC') >>> print(dt) '2013-03-31T03:30:00+00:00' >>> dt = dt.in_tz('Europe/Paris').in_tz('UTC') >>> print(dt) '2013-03-31T02:30:00+00:00' ``` --- ### Docs/Index {!docs/installation.md!} {!docs/introduction.md!} {!docs/instantiation.md!} {!docs/parsing.md!} {!docs/localization.md!} {!docs/attributes_properties.md!} {!docs/fluent_helpers.md!} {!docs/string_formatting.md!} {!docs/comparison.md!} {!docs/addition_subtraction.md!} {!docs/difference.md!} {!docs/modifiers.md!} {!docs/timezones.md!} {!docs/duration.md!} {!docs/interval.md!} {!docs/testing.md!} {!docs/limitations.md!} --- ### Docs/Installation # Installation Installing `pendulum` is quite simple: ```bash $ pip install pendulum ``` 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] ``` --- ### Docs/Instantiation # Instantiation There are several different methods available to create a new `DateTime` instance. First there is the main `datetime()` helper. ```python >>> import pendulum >>> dt = pendulum.datetime(2015, 2, 5) >>> isinstance(dt, datetime) True >>> dt.timezone.name 'UTC' ``` `datetime()` sets the time to `00:00:00` if it's not specified, and the timezone (the `tz` keyword argument) to `UTC`. Otherwise it can be a `Timezone` instance or simply a string timezone value. ```python >>> import pendulum >>> pendulum.datetime(2015, 2, 5, tz='Europe/Paris') >>> tz = pendulum.timezone('Europe/Paris') >>> pendulum.datetime(2015, 2, 5, tz=tz) ``` !!!note Supported strings for timezones are the one provided by the [IANA time zone database](https://www.iana.org/time-zones). The special `local` string is also supported and will return your current timezone. !!!warning The `tz` argument is keyword-only, unlike in version `1.x` The `local()` helper is similar to `datetime()` but automatically sets the timezone to the local timezone. ```python >>> import pendulum >>> dt = pendulum.local(2015, 2, 5) >>> print(dt.timezone.name) 'America/Toronto' ``` !!!note `local()` is just an alias for `datetime(..., tz='local')`. There is also the `now()` method. ```python >>> import pendulum >>> now = pendulum.now() >>> now_in_london_tz = pendulum.now('Europe/London') >>> now_in_london_tz.timezone_name 'Europe/London' ``` To accompany `now()`, a few other static instantiation helpers exist to create known instances. The only thing to really notice here is that `today()`, `tomorrow()` and `yesterday()`, besides behaving as expected, all accept a timezone parameter and each has their time value set to `00:00:00`. ```python >>> now = pendulum.now() >>> print(now) '2016-06-28T16:51:45.978473-05:00' >>> today = pendulum.today() >>> print(today) '2016-06-28T00:00:00-05:00' >>> tomorrow = pendulum.tomorrow('Europe/London') >>> print(tomorrow) '2016-06-29T00:00:00+01:00' >>> yesterday = pendulum.yesterday() >>> print(yesterday) '2016-06-27T00:00:00-05:00' ``` Pendulum enforces timezone aware datetimes, and using them is the preferred and recommended way of using the library. However, if you really need a **naive** `DateTime` object, the `naive()` helper is there for you. ```python >>> import pendulum >>> naive = pendulum.naive(2015, 2, 5) >>> naive.timezone None ``` The next helper, `from_format()`, is similar to the native `datetime.strptime()` function but uses custom tokens to create a `DateTime` instance. ```python >>> dt = pendulum.from_format('1975-05-21 22', 'YYYY-MM-DD HH') >>> print(dt) '1975-05-21T22:00:00+00:00' ``` !!!note To see all the available tokens, you can check the [Formatter](#formatter) section. It also accepts a `tz` keyword argument to specify the timezone: ```python >>> dt = pendulum.from_format('1975-05-21 22', 'YYYY-MM-DD HH', tz='Europe/London') '1975-05-21T22:00:00+01:00' ``` The final helper is for working with unix timestamps. `from_timestamp()` will create a `DateTime` instance equal to the given timestamp and will set the timezone as well or default it to `UTC`. ```python >>> dt = pendulum.from_timestamp(-1) >>> print(dt) '1969-12-31T23:59:59+00:00' >>> dt = pendulum.from_timestamp(-1, tz='Europe/London') >>> print(dt) '1970-01-01T00:59:59+01:00' ``` Finally, if you find yourself inheriting a `datetime.datetime` instance, you can create a `DateTime` instance via the `instance()` function. ```python >>> dt = datetime(2008, 1, 1) >>> p = pendulum.instance(dt) >>> print(p) '2008-01-01T00:00:00+00:00' ``` --- ### Docs/Interval # 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 ``` --- ### Docs/Introduction # Introduction Pendulum is a Python package to ease datetimes manipulation. It provides classes that are drop-in replacements for the native ones (they inherit from them). Special care has been taken to ensure timezones are handled correctly, and are based on the underlying `tzinfo` implementation. For example, all comparisons are done in `UTC` or in the timezone of the datetime being used. ```python >>> import pendulum >>> dt_toronto = pendulum.datetime(2012, 1, 1, tz='America/Toronto') >>> dt_vancouver = pendulum.datetime(2012, 1, 1, tz='America/Vancouver') >>> print(dt_vancouver.diff(dt_toronto).in_hours()) 3 ``` The default timezone, except when using the `now()` method, will always be `UTC`. --- ### Docs/Limitations # 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: ```python import pendulum from sqlite3 import register_adapter register_adapter(pendulum.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: ```python import pendulum import MySQLdb.converters import pymysql.converters MySQLdb.converters.conversions[pendulum.DateTime] = MySQLdb.converters.DateTime2literal pymysql.converters.conversions[pendulum.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`: ```python import pendulum from django.db.models import DateTimeField as BaseDateTimeField class DateTimeField(BaseDateTimeField): def value_to_string(self, obj): val = self.value_from_object(obj) if isinstance(value, pendulum.DateTime): return value.format('YYYY-MM-DD HH:mm:ss') return '' if val is None else val.isoformat() ``` --- ### Docs/Localization # Localization Localization occurs when using the `format()` method which accepts a `locale` keyword. ```python >>> import pendulum >>> dt = pendulum.datetime(1975, 5, 21) >>> dt.format('dddd DD MMMM YYYY', locale='de') 'Mittwoch 21 Mai 1975' >>> dt.format('dddd DD MMMM YYYY') 'Wednesday 21 May 1975' ``` `diff_for_humans()` is also localized, you can set the locale by using `pendulum.set_locale()`. ```python >>> import pendulum >>> pendulum.set_locale('de') >>> pendulum.now().add(years=1).diff_for_humans() 'in 1 Jahr' >>> pendulum.set_locale('en') ``` However, you might not want to set the locale globally. The `diff_for_humans()` method accepts a `locale` keyword argument to use a locale for a specific call. ```python >>> pendulum.set_locale('de') >>> dt = pendulum.now().add(years=1) >>> dt.diff_for_humans(locale='fr') 'dans 1 an' ``` --- ### Docs/Modifiers # Modifiers This group of methods performs helpful modifications to a copy of the current instance. You'll notice that the `start_of()`, `next()` and `previous()` methods set the time to `00:00:00` and the `end_of()` methods set the time to `23:59:59.999999`. The only one slightly different is the `average()` method. It returns the middle date between itself and the provided `DateTime` argument. ``` /* Detailed source-code truncated for AI context efficiency. */ ``` --- ### Docs/Parsing # Parsing The library natively supports the RFC 3339 format, most ISO 8601 formats and some other common formats. ```python >>> import pendulum >>> dt = pendulum.parse('1975-05-21T22:00:00') >>> print(dt) '1975-05-21T22:00:00+00:00 # You can pass a tz keyword to specify the timezone >>> dt = pendulum.parse('1975-05-21T22:00:00', tz='Europe/Paris') >>> print(dt) '1975-05-21T22:00:00+01:00' # Not ISO 8601 compliant but common >>> dt = pendulum.parse('1975-05-21 22:00:00') ``` If you pass a non-standard or more complicated string, it will raise an exception, so it is advised to use the `from_format()` helper instead. However, if you want the library to fall back on the [dateutil](https://dateutil.readthedocs.io) parser, you have to pass `strict=False`. ```python >>> import pendulum >>> dt = pendulum.parse('31-01-01') Traceback (most recent call last): ... ParserError: Unable to parse string [31-01-01] >>> dt = pendulum.parse('31-01-01', strict=False) >>> print(dt) '2031-01-01T00:00:00+00:00' ``` ## RFC 3339 | String | Output | | --------------------------------- | ------------------------------------------| | 1996-12-19T16:39:57-08:00 | 1996-12-19T16:39:57-08:00 | | 1990-12-31T23:59:59Z | 1990-12-31T23:59:59+00:00 | ## ISO 8601 ### Datetime | String | Output | | --------------------------------- | ----------------------------------------- | | 20161001T143028+0530 | 2016-10-01T14:30:28+05:30 | | 20161001T14 | 2016-10-01T14:00:00+00:00 | ### Date | String | Output | | --------------------------------- | ----------------------------------------- | | 2012 | 2012-01-01T00:00:00+00:00 | | 2012-05-03 | 2012-05-03T00:00:00+00:00 | | 20120503 | 2012-05-03T00:00:00+00:00 | | 2012-05 | 2012-05-01T00:00:00+00:00 | ### Ordinal day | String | Output | | ---------------------------------- | ----------------------------------------- | | 2012-007 | 2012-01-07T00:00:00+00:00 | | 2012007 | 2012-01-07T00:00:00+00:00 | ### Week number | String | Output | | --------------------------------- | ----------------------------------------- | | 2012-W05 | 2012-01-30T00:00:00+00:00 | | 2012W05 | 2012-01-30T00:00:00+00:00 | | 2012-W05-5 | 2012-02-03T00:00:00+00:00 | | 2012W055 | 2012-02-03T00:00:00+00:00 | ### Time When passing only time information the date will default to today. | String | Output | | --------------------------------- | ------------------------------------------ | | 00:00 | 2016-12-17T00:00:00+00:00 | | 12:04:23 | 2016-12-17T12:04:23+00:00 | | 120423 | 2016-12-17T12:04:23+00:00 | | 12:04:23.45 | 2016-12-17T12:04:23.450000+00:00 | ### Intervals | String | Output | | ----------------------------------------- | ------------------------------------------------------ | | 2007-03-01T13:00:00Z/2008-05-11T15:30:00Z | 2007-03-01T13:00:00+00:00 -> 2008-05-11T15:30:00+00:00 | | 2008-05-11T15:30:00Z/P1Y2M10DT2H30M | 2008-05-11T15:30:00+00:00 -> 2009-07-21T18:00:00+00:00 | | P1Y2M10DT2H30M/2008-05-11T15:30:00Z | 2007-03-01T13:00:00+00:00 -> 2008-05-11T15:30:00+00:00 | !!!note You can pass the ``exact`` keyword argument to ``parse()`` to get the exact type that the string represents: ```python >>> import pendulum >>> pendulum.parse('2012-05-03', exact=True) Date(2012, 05, 03) >>> pendulum.parse('12:04:23', exact=True) Time(12, 04, 23) ``` --- ### Docs/String Formatting # String formatting The `__str__` magic method is defined to allow `DateTime` instances to be printed as a pretty date string when used in a string context. The default string representation is the same as the one returned by the `isoformat()` method. ```python >>> import pendulum >>> dt = pendulum.datetime(1975, 12, 25, 14, 15, 16) >>> print(dt) '1975-12-25T14:15:16+00:00' >>> dt.to_date_string() '1975-12-25' >>> dt.to_formatted_date_string() 'Dec 25, 1975' >>> dt.to_time_string() '14:15:16' >>> dt.to_datetime_string() '1975-12-25 14:15:16' >>> dt.to_day_datetime_string() 'Thu, Dec 25, 1975 2:15 PM' # You can also use the format() method >>> dt.format('dddd Do [of] MMMM YYYY HH:mm:ss A') 'Thursday 25th of December 1975 02:15:16 PM' # Of course, the strftime method is still available >>> dt.strftime('%A %-d%t of %B %Y %I:%M:%S %p') 'Thursday 25th of December 1975 02:15:16 PM' ``` !!!note For localization support see the [Localization](#localization) section. ## Common Formats The following are methods to display a `DateTime` instance as a common format: ```python >>> import pendulum >>> dt = pendulum.now() >>> dt.to_atom_string() '1975-12-25T14:15:16-05:00' >>> dt.to_cookie_string() 'Thursday, 25-Dec-1975 14:15:16 EST' >>> dt.to_iso8601_string() '1975-12-25T14:15:16-0500' >>> dt.to_rfc822_string() 'Thu, 25 Dec 75 14:15:16 -0500' >>> dt.to_rfc850_string() 'Thursday, 25-Dec-75 14:15:16 EST' >>> dt.to_rfc1036_string() 'Thu, 25 Dec 75 14:15:16 -0500' >>> dt.to_rfc1123_string() 'Thu, 25 Dec 1975 14:15:16 -0500' >>> dt.to_rfc2822_string() 'Thu, 25 Dec 1975 14:15:16 -0500' >>> dt.to_rfc3339_string() '1975-12-25T14:15:16-05:00' >>> dt.to_rss_string() 'Thu, 25 Dec 1975 14:15:16 -0500' >>> dt.to_w3c_string() '1975-12-25T14:15:16-05:00' ``` ## Formatter Pendulum uses its own formatter when using the `format()` method. This format is more intuitive to use than the one used with `strftime()` and supports more directives. ```python >>> import pendulum >>> dt = pendulum.datetime(1975, 12, 25, 14, 15, 16) >>> dt.format('YYYY-MM-DD HH:mm:ss') '1975-12-25 14:15:16' ``` ### Tokens The following tokens are currently supported: | | Token | Output | | ------------------------------ | ------------- | ------------------------------------------ | | **Year** | YYYY | 2000, 2001, 2002 ... 2012, 2013 | | | YY | 00, 01, 02 ... 12, 13 | | | Y | 2000, 2001, 2002 ... 2012, 2013 | | **Quarter** | Q | 1 2 3 4 | | | Qo | 1st 2nd 3rd 4th | | **Month** | MMMM | January, February, March ... | | | MMM | Jan, Feb, Mar ... | | | MM | 01, 02, 03 ... 11, 12 | | | M | 1, 2, 3 ... 11, 12 | | | Mo | 1st 2nd ... 11th 12th | | **Day of Year** | DDDD | 001, 002, 003 ... 364, 365 | | | DDD | 1, 2, 3 ... 4, 5 | | **Day of Month** | DD | 01, 02, 03 ... 30, 31 | | | D | 1, 2, 3 ... 30, 31 | | | Do | 1st, 2nd, 3rd ... 30th, 31st | | **Day of Week** | dddd | Monday, Tuesday, Wednesday ... | | | ddd | Mon, Tue, Wed ... | | | dd | Mo, Tu, We ... | | | d | 0, 1, 2 ... 6 | | **Days of ISO Week** | E | 1, 2, 3 ... 7 | | **Hour** | HH | 00, 01, 02 ... 23 | | | H | 0, 1, 2 ... 23 | | | hh | 01, 02, 03 ... 11, 12 | | | h | 1, 2, 3 ... 11, 12 | | **Minute** | mm | 00, 01, 02 ... 58, 59 | | | m | 0, 1, 2 ... 58, 59 | | **Second** | ss | 00, 01, 02 ... 58, 59 | | | s | 0, 1, 2 ... 58, 59 | | **Fractional Second** | S | 0 1 ... 8 9 | | | SS | 00, 01, 02 ... 98, 99 | | | SSS | 000 001 ... 998 999 | | | SSSS ... | 000[0..] 001[0..] ... 998[0..] 999[0..] | | | SSSSSS | | | **AM / PM** | A | AM, PM | | **Timezone** | Z | -07:00, -06:00 ... +06:00, +07:00 | | | ZZ | -0700, -0600 ... +0600, +0700 | | | z | Asia/Baku, Europe/Warsaw, GMT ... | | | zz | EST CST ... MST PST | | **Seconds timestamp** | X | 1381685817, 1234567890.123 | | **Milliseconds timestamp** | x | 1234567890123 | ### Localized Formats Because preferred formatting differs based on locale, there are a few tokens that can be used to format an instance based on its locale. | | | | | ------------------------------------------------------ | ------------- | ------------------------------------------ | | **Time** | LT | 8:30 PM | | **Time with seconds** | LTS | 8:30:25 PM | | **Month numeral, day of month, year** | L | 09/04/1986 | | **Month name, day of month, year** | LL | September 4 1986 | | **Month name, day of month, year, time** | LLL | September 4 1986 8:30 PM | | **Month name, day of month, day of week, year, time** | LLLL | Thursday, September 4 1986 8:30 PM | ### Escaping Characters To escape characters in format strings, you can wrap the characters in square brackets. ```python >>> import pendulum >>> dt = pendulum.now() >>> dt.format('[today] dddd') 'today Sunday' ``` --- ### Docs/Testing # Testing 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). !!!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 >>> now = pendulum.now() >>> pendulum.travel(minutes=5) >>> pendulum.now().diff_for_humans(now) "5 minutes after" ``` 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: ```python >>> import pendulum >>> now = pendulum.now() >>> pendulum.travel(minutes=5, freeze=True) >>> pendulum.now().diff_for_humans(now) "5 minutes after" # This will stay like this indefinitely ``` ## Absolute time travel 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 >>> import pendulum >>> pendulum.travel_to(pendulum.yesterday()) ``` 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) ``` ## 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 >>> 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: ```python >>> import pendulum >>> 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" ``` --- ### Docs/Timezones # Timezones Timezones are an important part of every datetime library, and `pendulum` tries to provide an easy and accurate system to handle them properly. !!!note The timezone system works best inside the `pendulum` ecosystem but can also be used with the standard ``datetime`` library with a few limitations. See [Using the timezone library directly](#using-the-timezone-library-directly). ## Normalization When you create a `DateTime` instance, the library will normalize it for the given timezone to properly handle any transition that might have occurred. ```python >>> import pendulum >>> pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris') # 2:30 for the 31th of March 2013 does not exist # so pendulum will return the actual time which is 3:30+02:00 '2013-03-31T03:30:00+02:00' >>> pendulum.datetime(2013, 10, 27, 2, 30, tz='Europe/Paris') # Here, 2:30 exists twice in the day so pendulum will # assume that the transition already occurred '2013-10-27T02:30:00+01:00' ``` You can, however, control the normalization behavior: ```python >>> import pendulum >>> pendulum.datetime(2013, 3, 31, 2, 30, 0, 0, tz='Europe/Paris', dst_rule=pendulum.PRE_TRANSITION) '2013-03-31T01:30:00+01:00' >>> pendulum.datetime(2013, 10, 27, 2, 30, 0, 0, tz='Europe/Paris', dst_rule=pendulum.PRE_TRANSITION) '2013-10-27T02:30:00+02:00' >>> pendulum.datetime(2013, 3, 31, 2, 30, 0, 0, tz='Europe/Paris', dst_rule=pendulum.TRANSITION_ERROR) # NonExistingTime: The datetime 2013-03-31 02:30:00 does not exist >>> pendulum.datetime(2013, 10, 27, 2, 30, 0, 0, tz='Europe/Paris', dst_rule=pendulum.TRANSITION_ERROR) # AmbiguousTime: The datetime 2013-10-27 02:30:00 is ambiguous. ``` Note that it only affects instances at creation time. Shifting time around transition times still behaves the same. ## Shifting time to transition So, what happens when you add time to a `DateTime` instance and stumble upon a transition time? Well `pendulum`, provided with the context of the previous instance, will adopt the proper behavior and apply the transition accordingly. ```python >>> import pendulum >>> dt = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz='Europe/Paris') '2013-03-31T01:59:59.999999+01:00' >>> dt = dt.add(microseconds=1) '2013-03-31T03:00:00+02:00' >>> dt.subtract(microseconds=1) '2013-03-31T01:59:59.999999+01:00' >>> dt = pendulum.datetime(2013, 10, 27, 2, 59, 59, 999999, tz='Europe/Paris', dst_rule=pendulum.PRE_TRANSITION) '2013-10-27T02:59:59.999999+02:00' >>> dt = dt.add(microseconds=1) '2013-10-27T02:00:00+01:00' >>> dt = dt.subtract(microseconds=1) '2013-10-27T02:59:59.999999+02:00' ``` ## Switching timezones You can easily change the timezone of a `DateTime` instance with the `in_timezone()` method. !!!note You can also use the more concise ``in_tz()`` ```python >>> in_paris = pendulum.datetime(2016, 8, 7, 22, 24, 30, tz='Europe/Paris') '2016-08-07T22:24:30+02:00' >>> in_paris.in_timezone('America/New_York') '2016-08-07T16:24:30-04:00' >>> in_paris.in_tz('Asia/Tokyo') '2016-08-08T05:24:30+09:00' ``` ## Using the timezone library directly !!!warning **You should avoid using the timezone library in Python < 3.6.** This is due to the fact that Pendulum relies heavily on the presence of the `fold` attribute which was introduced in Python 3.6. The reason it works inside the Pendulum ecosystem is that it backports the `fold` attribute in the `DateTime` class. Like said in the introduction, you can use the timezone library directly with standard `datetime` objects but with limitations, especially when adding and subtracting time around transition times. The value of the `fold` attribute will be used by default to determine the transition rule. ```python >>> from datetime import datetime >>> from pendulum import timezone >>> paris = timezone('Europe/Paris') >>> dt = datetime(2013, 3, 31, 2, 30) # By default, fold is set to 0 >>> dt = paris.convert(dt) >>> dt.isoformat() '2013-03-31T01:30:00+01:00' >>> dt = datetime(2013, 3, 31, 2, 30, fold=1) >>> dt = paris.convert(dt) >>> dt.isoformat() '2013-03-31T03:30:00+02:00' ``` Instead of relying on the `fold` attribute, you can use the `dst_rule` keyword argument. This is especially useful if you want to raise errors on non-existing and ambiguous times. ```python >>> import pendulum >>> dt = datetime(2013, 3, 31, 2, 30) # By default, fold is set to 0 >>> dt = paris.convert(dt, dst_rule=pendulum.PRE_TRANSITION) >>> dt.isoformat() '2013-03-31T01:30:00+01:00' >>> dt = paris.convert(dt, dst_rule=pendulum.POST_TRANSITION) >>> dt.isoformat() '2013-03-31T03:30:00+02:00' >>> paris.convert(dt, dst_rule=pendulum.TRANSITION_ERROR) # NonExistingTime: The datetime 2013-03-31 02:30:00 does not exist ``` This works as expected. However, whenever we add or subtract a `timedelta` object, things get tricky. ```python >>> from datetime import datetime, timedelta >>> from pendulum import timezone >>> dt = datetime(2013, 3, 31, 1, 59, 59, 999999) >>> dt = paris.convert(dt) >>> dt.isoformat() '2013-03-31T01:59:59.999999+01:00' >>> dt = dt + timedelta(microseconds=1) >>> dt.isoformat() '2013-03-31T02:00:00+01:00' ``` This is not what we expect. It should be `2013-03-31T03:00:00+02:00`. It is actually easy to retrieve the proper datetime by using `convert()` again. ```python >>> dt = tz.convert(dt) >>> dt.isoformat() '2013-03-31T03:00:00+02:00' ``` You can also get a normalized `datetime` object from a `Timezone` by using the `datetime()` method: ```python >>> import pendulum >>> tz = pendulum.timezone('Europe/Paris') >>> dt = tz.datetime(2013, 3, 31, 2, 30) >>> dt.isoformat() '2013-03-31T03:30:00+02:00' ``` --- ### CHANGELOG # Change Log ## [3.2.0] - 2026-01-30 ### Added - Added support for Python 3.14 [#923](https://github.com/python-pendulum/pendulum/pull/923) - Added upper limit to `time-machine` dependency [#931](https://github.com/python-pendulum/pendulum/pull/931) ### Changed - **Dropped support for PyPy below PyPy3.11** [#937](https://github.com/python-pendulum/pendulum/pull/937) - Optimize usage of `re.` methods [#741](https://github.com/python-pendulum/pendulum/pull/741) - Fixed `pendulum.parse` not being marked as exported [#693](https://github.com/python-pendulum/pendulum/pull/693) - Fixed `pendulum.parse('now', tz='...')` ignoring the timezone [#701](https://github.com/python-pendulum/pendulum/pull/701) - Use `pathlib` to read Unix TZ data [#742](https://github.com/python-pendulum/pendulum/pull/742) - Fixed `Interval` deepcopying [#850](https://github.com/python-pendulum/pendulum/pull/850) - Fixed typo in `end_of('century')` docs [#910](https://github.com/python-pendulum/pendulum/pull/910) - Bumped PyO3 to 0.27 [#922](https://github.com/python-pendulum/pendulum/pull/922) - Fixed incorrect date offset calculation in Rust extensions [#918](https://github.com/python-pendulum/pendulum/pull/918) - Changed locales and `pytest` to be lazy loaded [#926](https://github.com/python-pendulum/pendulum/pull/926) - Fixed error of `Duration` deepcopy not including weeks [#933](https://github.com/python-pendulum/pendulum/pull/933) - Fixed empty `Duration`s not being an error in Python ISO8601 parser implementation [#903](https://github.com/python-pendulum/pendulum/pull/903) - Fixed parsing invalid interval string [#860](https://github.com/python-pendulum/pendulum/pull/860) - Fixed pluralization bug in `Duration.in_words()` [#826](https://github.com/python-pendulum/pendulum/pull/826) ### Locales - Added HI (Hindi) locale [#902](https://github.com/python-pendulum/pendulum/pull/902) ### Removed - Removed dependency on `pytz` [#911](https://github.com/python-pendulum/pendulum/pull/911) ## [3.1.0] - 2025-04-19 ### Added - Added support for Python 3.13 [#871](https://github.com/python-pendulum/pendulum/pull/871) ### Changed - Removed support for Python 3.8 [#863](https://github.com/python-pendulum/pendulum/pull/863) - Fixed pure Python wheels support [#889](https://github.com/python-pendulum/pendulum/pull/889) - Fixed `pendulum.tz.timezones()` to use system tzdata [#801](https://github.com/python-pendulum/pendulum/pull/801) - Fixed spelling of Kyiv [#885](https://github.com/python-pendulum/pendulum/pull/885) - Fixed `DeprecationWarning` from `utcfromtimestamp` [#887](https://github.com/python-pendulum/pendulum/pull/887) - Fixed parsing of invalid intervals [#843](https://github.com/python-pendulum/pendulum/pull/843) ### Locales - Added UA (Ukraine) locale [#793](https://github.com/python-pendulum/pendulum/pull/793) - Added BG (Bulgarian) locale [#812](https://github.com/python-pendulum/pendulum/pull/812) - Fixed KO (Korean) translations for `before` and `after` [#858](https://github.com/python-pendulum/pendulum/pull/858) ## [3.0.0] - 2023-12-16 ### Changed - Relaxed dependency constraints. [#760](https://github.com/python-pendulum/pendulum/pull/760) - The testing helpers are now optional and must be opted-in via the `test` extra. [#778](https://github.com/python-pendulum/pendulum/pull/778) ### Fixed - Removed remaining mentions of periods instead of intervals. [#757](https://github.com/python-pendulum/pendulum/pull/757) - Fixed the behavior of the `week_of_month` property for edge cases in January and December. [#774](https://github.com/python-pendulum/pendulum/pull/774) - Fixed the handling of the `fold` attribute when deep-copying a `DateTime` instance. [#776](https://github.com/python-pendulum/pendulum/pull/776) - Fixed errors where hours and days were not handled properly when adding durations. [#775](https://github.com/python-pendulum/pendulum/pull/775) - Fixed errors where hours and days were not handled properly when adding durations. [#775](https://github.com/python-pendulum/pendulum/pull/775) ## [3.0.0b1] - 2023-10-01 ### Added - Made `instance()` support all native types (date, time, datetime). [#732](https://github.com/python-pendulum/pendulum/pull/732) ### Changed - Dropped support for Python 3.7. [#734](https://github.com/python-pendulum/pendulum/pull/734) - Rewrote extensions in Rust. [#721](https://github.com/python-pendulum/pendulum/pull/721) - Made day of week convention more consistent across the codebase. [#731](https://github.com/python-pendulum/pendulum/pull/731) ### Fixed - Fixed datetime string representation to match the native library. [#733](https://github.com/python-pendulum/pendulum/pull/733) - Fixed issues on some system when retrieving the local timezone. [#733](https://github.com/python-pendulum/pendulum/pull/733) - Fixed DST handling in `start_of()/end_of()` methods. [#713](https://github.com/python-pendulum/pendulum/pull/713) ## [3.0.0a1] - 2022-11-23 ### Added - Added new testing helpers to time travel. [#626](https://github.com/python-pendulum/pendulum/pull/626) ### Changed - Dropped support for Python 2.7, 3.5 and 3.6. [#569](https://github.com/python-pendulum/pendulum/pull/569) - The `Timezone` class now relies on the native `zoneinfo.ZoneInfo` class. [#569](https://github.com/python-pendulum/pendulum/pull/569) - Renamed the `Period` class to `Interval`. [#676](https://github.com/python-pendulum/pendulum/pull/676) - Renamed the `period` helper to `interval`. [#676](https://github.com/python-pendulum/pendulum/pull/676) - Removed existing testing helpers: `test()` and `set_test_now()`. [#626](https://github.com/python-pendulum/pendulum/pull/626) ### Locales - Added the `sk` locale. [#575](https://github.com/python-pendulum/pendulum/pull/575) - Added the `ja` locale. [#610](https://github.com/python-pendulum/pendulum/pull/610) - Added the `he` locale. [#585](https://github.com/python-pendulum/pendulum/pull/585) - Added the `sv` locale. [#562](https://github.com/python-pendulum/pendulum/pull/562) ## [2.1.1] - 2020-07-13 ### Fixed - Fixed errors where invalid timezones were matched in `from_format()` ([#374](https://github.com/python-pendulum/pendulum/pull/374)). - Fixed errors when subtracting negative timedeltas ([#419](https://github.com/python-pendulum/pendulum/pull/419)). - Fixed errors in total units computation for durations with years and months ([#482](https://github.com/python-pendulum/pendulum/pull/482)). - Fixed an error where the `fold` attribute was overridden when using `replace()` ([#414](https://github.com/python-pendulum/pendulum/pull/414)). - Fixed an error where `now()` was not returning the correct result on DST transitions ([#483](https://github.com/python-pendulum/pendulum/pull/483)). - Fixed inconsistent typing annotation for the `parse()` function ([#452](https://github.com/python-pendulum/pendulum/pull/452)). ### Locales - Added the `pl` locale ([#459](https://github.com/python-pendulum/pendulum/pull/459)). ## [2.1.0] - 2020-03-07 ### Added - Added better typing and PEP-561 compliance ([#320](https://github.com/python-pendulum/pendulum/pull/320)). - Added the `is_anniversary()` method as an alias of `is_birthday()` ([#298](https://github.com/python-pendulum/pendulum/pull/298)). ### Changed - Dropped support for Python 3.4. - `is_utc()` will now return `True` for any datetime with an offset of 0, similar to the behavior in the `1.*` versions ([#295](https://github.com/python-pendulum/pendulum/pull/295)) - `Duration.in_words()` will now return `0 milliseconds` for empty durations. ### Fixed - Fixed various issues with timezone transitions for some edge cases ([#321](https://github.com/python-pendulum/pendulum/pull/321), ([#350](https://github.com/python-pendulum/pendulum/pull/350))). - Fixed out of bound detection for `nth_of("month")` ([#357](https://github.com/python-pendulum/pendulum/pull/357)). - Fixed an error where extra text was accepted in `from_format()` ([#372](https://github.com/python-pendulum/pendulum/pull/372)). - Fixed a recursion error when adding time to a `DateTime` with a fixed timezone ([#431](https://github.com/python-pendulum/pendulum/pull/431)). - Fixed errors where `Period` instances were not properly compared to other classes, especially `timedelta` instances ([#427](https://github.com/python-pendulum/pendulum/pull/427)). - Fixed deprecation warnings due to internal regexps ([#427](https://github.com/python-pendulum/pendulum/pull/427)). - Fixed an error where the `test()` helper would not unset the test instance when an exception was raised ([#445](https://github.com/python-pendulum/pendulum/pull/445)). - Fixed an error where the `week_of_month` attribute was not returning the correct value ([#446](https://github.com/python-pendulum/pendulum/pull/446)). - Fixed an error in the way the `Z` ISO-8601 UTC designator was not parsed as UTC ([#448](https://github.com/python-pendulum/pendulum/pull/448)). ### Locales - Added the `nl` locale. - Added the `it` locale. - Added the `id` locale. - Added the `nb` locale. - Added the `nn` locale. ## [2.0.5] - 2019-07-03 ### Fixed - Fixed ISO week dates not being parsed properly in `from_format()`. - Fixed loading of some timezones with empty posix spec. - Fixed deprecation warnings. ### Locales - Added RU locale. ## [2.0.4] - 2018-10-30 ### Fixed - Fixed `from_format()` not recognizing input strings when the specified pattern had escaped elements. - Fixed missing `x` token for string formatting. - Fixed reading timezone files. - Added support for parsing padded 2-digit days of the month with `from_format()` - Fixed `from_format()` trying to parse escaped tokens. - Fixed the `z` token timezone parsing in `from_format()` to allow underscores. - Fixed C extensions build errors. - Fixed `age` calculation for future dates. ## [2.0.3] - 2018-07-30 ### Fixed - Fixed handling of `pytz` timezones. - Fixed some formatter's tokens handling. - Fixed errors on some systems when retrieving timezone from localtime files. - Fixed `diff` methods. - Fixed `closest()/farthest()` methods. ## [2.0.2] - 2018-05-29 ### Fixed - Fixed the `weeks` property for negative `Period` instances. - Fixed `start_of()` methods not setting microseconds to 0. - Fixed errors on some systems when retrieving timezone from clock files. - Fixed parsing of partial time. - Fixed parsing not raising an error for week 53 for ordinary years. - Fixed string formatting not supporting `strftime` format. ## [2.0.1] - 2018-05-10 ### Fixed - Fixed behavior of the `YY` token in `from_format()`. - Fixed errors on some systems when retrieving timezone from clock files. ## [2.0.0] - 2018-05-08 ### Added - Added years and months support to durations. - Added the `test_local_timezone()` and `set_local_timezone()` helpers to ease testing. - Added support of ISO 8601 duration parsing. - Added support of ISO 8601 interval parsing. - Added a `local()` helper. - Added a `naive()` helper and a `naive()` method. - Added support for POSIX specification to extend timezones DST transitions. ### Changed - `Pendulum` class has been renamed to `DateTime`. - `Interval` class has been renamed to `Duration`. - Changed and improved the timezone system. - Removed the `create()` helper. - Removed the `utcnow()` helper. - `strict` keyword argument for `parse` has been renamed to `exact`. - `at()` now supports setting partial time. - `local`, `utc` and `is_dst` are now methods rather than properties (`is_local()`, `is_utc()`, `is_dst()`). - Changed the `repr` of most common objects. - Made the `strict` keyword argument for `parse` false by default, which means it will not fallback on the `dateutil` parser. - Improved performances of the `precise_diff()` helper. - The `alternative` formatter is now the default one. - `set_to_string_format()/reset_to_string_format()` methods have been removed. - `from_format()` now uses the alternative formatter tokens. - Removed `xrange()` method of the `Period` class and made `range()` a generator. - New locale system which uses CLDR data for most of the translations. - `diff_for_humans()` now returns `a few seconds` where appropriate. - Removed `Period.intersect()`. [Unreleased]: https://github.com/python-pendulum/pendulum/compare/3.1.0...master [3.2.0]: https://github.com/python-pendulum/pendulum/releases/tag/3.2.0 [3.1.0]: https://github.com/python-pendulum/pendulum/releases/tag/3.1.0 [3.1.0]: https://github.com/python-pendulum/pendulum/releases/tag/3.1.0 [3.0.0]: https://github.com/python-pendulum/pendulum/releases/tag/3.0.0 [3.0.0b1]: https://github.com/python-pendulum/pendulum/releases/tag/3.0.0b1 [3.0.0a1]: https://github.com/python-pendulum/pendulum/releases/tag/3.0.0a1 [2.1.1]: https://github.com/python-pendulum/pendulum/releases/tag/2.1.1 [2.1.0]: https://github.com/python-pendulum/pendulum/releases/tag/2.1.0 [2.0.5]: https://github.com/python-pendulum/pendulum/releases/tag/2.0.5 [2.0.4]: https://github.com/python-pendulum/pendulum/releases/tag/2.0.4 [2.0.3]: https://github.com/python-pendulum/pendulum/releases/tag/2.0.3 [2.0.2]: https://github.com/python-pendulum/pendulum/releases/tag/2.0.2 [2.0.1]: https://github.com/python-pendulum/pendulum/releases/tag/2.0.1 [2.0.0]: https://github.com/python-pendulum/pendulum/releases/tag/2.0.0 ---