GithubHelp home page GithubHelp logo

sdispater / pendulum Goto Github PK

View Code? Open in Web Editor NEW
6.1K 70.0 362.0 2.66 MB

Python datetimes made easy

Home Page: https://pendulum.eustace.io

License: MIT License

Makefile 0.06% Python 92.24% Rust 7.69%
python datetime date time python3 timezones

pendulum's Introduction

Pendulum

image

image

Pendulum Build status

Python datetimes made easy.

Supports Python 3.8 and newer.

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

Resources

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 your 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.

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()

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 dependencies 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.

pendulum's People

Contributors

altendky avatar baryluk avatar bryanforbes avatar delgan avatar dependabot[bot] avatar edgarrmondragon avatar erosennin avatar eumiro avatar gordol avatar guyzmo avatar hugovk avatar jmcarp avatar klardotsh avatar kleschenko avatar maedox avatar nickfabry avatar njsmith avatar pr0ps avatar pre-commit-ci[bot] avatar redlickigrzegorz avatar regnarock avatar sdispater avatar secrus avatar senpos avatar timgates42 avatar tomage avatar willtheorangeguy avatar wonkyoc avatar yomofuno avatar zdenop avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pendulum's Issues

Proposal for new Pendulum methods for weekday numbering systems

Today, Pendulum objects have the weekday and the isoweekday methods that shows the first 2 lines of this Wikipedia table:

Line M T W T F S S
1    1 2 3 4 5 6 7
2    0 1 2 3 4 5 6
3    2 3 4 5 6 7 1
4    1 2 3 4 5 6 0

About each line, Wikipedia tells us:

  1. ISO 8601, usage: %_ISODOWI%, %@ISODOWI[]% (4DOS); DAYOFWEEK() (HP Prime)
  2. No more info at Wikipedia
  3. Usage: %NDAY OF WEEK% (NetWare, DR-DOS); %_DOWI%, %@DOWI[]% (4DOS)
  4. Usage: HP financial calculators

I think it would be useful more 2 methods, namely dosweekday and hpweekday, including that 3rd and 4th lines. Usually Brazilian people think on the weekday as the dosweekday (3rd line), since the day names follows our numbering system (e.g. "second day" = "segundo dia", and "monday" = "segunda").

I know that it's actually possible with things like dt.isoweekday() % 7 + 1 (3rd line), but maybe that could be part of the library. =)

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>

Impossible to change timezones

Hi,

I was just trying to localize a datetime to the correct timezone and I noticed it is not working as expected:

>>> import pendulum
>>> pendulum.create(2016, 4, 15, 0, 0 , 0, 0) 
<Pendulum [2016-04-15T00:00:00+00:00]> # so far, so good
>>> pendulum.create(2016, 4, 15, 0, 0 , 0, 0).timezone_('America/Sao_Paulo') 
 <Pendulum [2016-04-15T00:00:00+00:00]> # oops, should've been <Pendulum [2016-04-15T00:00:00-03:00]>

I am missing something or this is really an undesired behavior?

Can't convert a datetime object that have a tzinfo property to pendulum instance

Trying to convert a datetime instance which have a tzinfo property, to a pendulum instance returns an error.

import pendulum
import pytz

from datetime import datetime

date = datetime.now()
date = date.replace(tzinfo=pytz.utc)
pendulum_date = pendulum.instance(date)

returns:

AttributeError: 'UTC' object has no attribute 'upper'

Adding a week, crossing DST boundary, to a parsed ISO8601 string, raises TypeError

TypeError                                 Traceback (most recent call last)
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>()
----> 1 pendulum.parse('2015-03-08T01:00:00-06:00').add(weeks=1)

/usr/local/lib/python2.7/dist-packages/pendulum/pendulum.pyc in add(self, years, months, weeks, days, hours, minutes, seconds, microseconds)
   1414 
   1415         dt = self._datetime + delta
-> 1416         dt = self._tz.convert(dt)
   1417 
   1418         return self.instance(dt)

/usr/local/lib/python2.7/dist-packages/pendulum/tz/timezone.pyc in convert(self, dt, dst_rule)
     91 
     92         else:
---> 93             converted = self._convert(dt)
     94 
     95         if not isinstance(converted, tuple):

/usr/local/lib/python2.7/dist-packages/pendulum/tz/timezone.pyc in _convert(self, dt)
    216             )
    217 
--> 218         return dt.astimezone(self)
    219 
    220     def _to_local_time(self, unix_time, transition_type_index):

/usr/local/lib/python2.7/dist-packages/pendulum/tz/timezone.pyc in fromutc(self, dt)
    298         dt = dt.replace(tzinfo=None)
    299 
--> 300         idx = self._find_utc_index(dt)
    301         tr = self._transitions[idx]
    302         tzinfo = self._tzinfos[tr._transition_type_index]

/usr/local/lib/python2.7/dist-packages/pendulum/tz/timezone.pyc in _find_utc_index(self, dt)
    315                 lo = hint[1]
    316 
--> 317         idx = max(0, bisect_right(self._utc_transition_times, dt, lo, hi) - 1)
    318 
    319         self._local_hint['_utc'] = (dt, idx)

TypeError: can't compare datetime.datetime to int

Off-by-one microsecond when parsing with certain timezones

I found a curious behavior when parsing timestamps where the microsecond value is off by one depending on the timezone used.

As a minimal example I'm attempting to parse the string 2016-11-12T02:09:39.594000.

Parsing with the default timezone works as excepted:

In [3]: Pendulum.parse('2016-11-12T02:09:39.594000').microsecond
Out[3]: 594000

Using a particular timezone shows the off-by-one difference.

In [4]: Pendulum.parse('2016-11-12T02:09:39.594000', 'America/Panama').microsecond
Out[4]: 593999

Several timezones (but not all by any means) have this behavior

In [5]: len(pytz.common_timezones)
Out[5]: 436

In [6]: len({name for name in pytz.common_timezones if Pendulum.parse('2016-11-12T02:09:39.594000', name).microsecond != 594000})
Out[6]: 182

I don't think anything in the particular choice of time or timezone should result in a difference of a single microsecond?

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)

Make Pendulum class immutable

add()/sub(), start_of()/end_of() methods and fluent setters should return a new instance of Pendulum rather than modify it in-place.

Feature proposal: Basic default intervals

I propose to add some very simple default intervals to the library.
I propose to add an interval called day, hour, minute and so on to the lib. That way you can do stuff like this:

import pendulum as pm

now_in_paris = pm.now('Europe/Paris')
tomorrow_in_paris = now_in_paris + pm.day
next_week_in_paris = now_in_paris + 7*pm.day

It's inspired by the Ruby way of adding dates according to this comment, but modified to be Pythonic: https://news.ycombinator.com/item?id=12301965

I wouldn't mind creating a pull request for this if you agree that this would be nice to have.

Adapt with Django

As Django need a datetime object to be stored into a DateTimeField, I'm using 'getattribute('datetime')' to get the datetime object now, any better solutions?

Interval.in_words() support for sub second values

I like the in_words()/str() behavior of Interval objects. However I sometimes have intervals that are sub-second and it would be nice if those resolved to something like '25ms' instead of just an empty string ''.

I could submit a pull request with such a change if you like.

offset_hours assumes integral timezone

There are several timezones that don't have a integer offset from UTC: for instance Australia/Adelaide is +0930 (+1030 DST). This means that the following is incorrect:

>>> import pendulum
>>> pendulum.now('Australia/Adelaide).offset_hours
9

Is this intentional? The documentation say "integer offset".

Interval: store months separately

Currently, intervals are broken down into microseconds, seconds and days in Pendulum.

However, this might be not precise enough if you calculate with months. Adding months to a Pendulum instance is already supported by add(months=n) (with precise calculations), so we should be able to store months in Interval instances too (and calculate with them).

This would also allow a loss-free representation of the ISO8601 duration format in a Duration instance, and conversions from and to it ;-)

Of course, there might be some corner cases that need to be considered - like what happens when 1 month is compared with 30 days.

I'd suggest to peek at the PostgreSQL interval implementation (with added microseconds):

Internally interval values are stored as months, days, and seconds. This is done because the number of days in a month varies, and a day can have 23 or 25 hours if a daylight savings time adjustment is involved. 

Comparison functions raise exceptions

>>> Pendulum.now() == ''
...
ValueError: String does not contain a date.
>>> Pendulum.now() == 'aa'
...
ValueError: Unknown string format

This is also the case when checking if a Pendulum object is in a list:

>>> Pendulum.now() in ['']
...
ValueError: String does not contain a date.
>>> '' in [Pendulum.now()]
...
ValueError: String does not contain a date.
>>> Pendulum.now() in ['aa']
...
ValueError: Unknown string format
>>> 'aa' in [Pendulum.now()]
...
ValueError: Unknown string format

This prohibits the use of Pendulum objects in collections. A possible solution is to catch ValueErrors in _get_datetime when the instance is a string. You have a better understanding of the internals of Pendulum than I do, perhaps you think it's a better idea to catch exceptions in the comparison methods and return False in case of an exception.

Add option to control normalization behavior

Currently, Pendulum automatically normalizes the datetime on creation to apply any DST transition:

import pendulum

pendulum.create(2013, 3, 31, 2, 30, 0, 0, 'Europe/Paris')
# <Pendulum [2013-03-31T03:30:00+02:00]>

However, it might be confusing, so the introduction of an option to control this behavior could prove useful:

import pendulum

pendulum.set_transition_rule(pendulum.PRE_TRANSITION)
pendulum.create(2013, 3, 31, 2, 30, 0, 0, 'Europe/Paris')
# <Pendulum [2013-03-31T02:30:00+01:00]>

pendulum.set_transition_rule(pendulum.TRANSITION_ERROR)
pendulum.create(2013, 3, 31, 2, 30, 0, 0, 'Europe/Paris')
# NonExistingTime: The requested datetime does not exist.

pendulum.create(2013, 10, 27, 2, 30, 0, 0, 'Europe/Paris')
# AmbiguousTime: The requested datetime is ambiguous.

The default behavior would be the same as the existing one (POST_TRANSITION).

Note that when shifting time, the normalization will always occur regardless of the option.

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.

TypeError: invalid file: <_io.BufferedReader name='/etc/localtime'>

  • Fedora 24
  • Python3.5 (distro)
  • pip3 install pendulum (version 0.5)

I get this error on a fairly stock fedora box.

>>> pendulum.now()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.5/site-packages/pendulum/pendulum.py", line 270, in now
    return cls.instance(dt, tz)
  File "/usr/lib64/python3.5/site-packages/pendulum/pendulum.py", line 205, in instance
    tzinfo=tz
  File "/usr/lib64/python3.5/site-packages/pendulum/pendulum.py", line 180, in __init__
    self._tz = self._safe_create_datetime_zone(tzinfo)
  File "/usr/lib64/python3.5/site-packages/pendulum/pendulum.py", line 111, in _safe_create_datetime_zone
    return cls._local_timezone()
  File "/usr/lib64/python3.5/site-packages/pendulum/pendulum.py", line 130, in _local_timezone
    return local_timezone()
  File "/usr/lib64/python3.5/site-packages/pendulum/tz/__init__.py", line 25, in local_timezone
    return LocalTimezone.get()
  File "/usr/lib64/python3.5/site-packages/pendulum/tz/local_timezone.py", line 19, in get
    name = cls.get_local_tz_name()
  File "/usr/lib64/python3.5/site-packages/pendulum/tz/local_timezone.py", line 36, in get_local_tz_name
    return getattr(cls, 'get_tz_name_for_{}'.format(os))()
  File "/usr/lib64/python3.5/site-packages/pendulum/tz/local_timezone.py", line 145, in get_tz_name_for_unix
    return Timezone('', *Parser.parse(tzfile))
  File "/usr/lib64/python3.5/site-packages/pendulum/tz/parser.py", line 27, in parse
    with open(filepath, 'rb') as fp:
TypeError: invalid file: <_io.BufferedReader name='/etc/localtime'>

Period exclusion from a list of periods functionality

I have a small library written in Kotlin similar to Pendulum's Period functionality and added an exclusion function. It would 'remove' and edit periods from a list of periods based on the period that needs to be excluded. I would love to submit a PR and add the feature to Pendulum. Here's an example:

periods = [
    Period(
        start=yesterday,
        end=tomorrow
    )
]

excluded_periods = Period.exclude(
    periods=periods,
    exclusion=Period(
        start=today,
        end=tomorrow
    )
)
[
    Period(
        start=yesterday,
        end=today
    )
]

Could we perhaps talk on the Slack you mentioned in #42? Send me a message on Twitter for my email address.

add_XXX() or sub_XXX()

I don't quite understand why singular & plural are seperated (year/years, month/months ...)
I think integration function is more useful.

# for example, add_year() -> add_years().

Formatting string bug

I am using py3 and I tested the module in my locale (pt-br) but on formatting it returns a error:
ValueError: Invalid format string

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.

Adding/subtracting when there's a change on DST

I'm trying to do some maths with dates that results in objects with different DST timezone offsets, e.g.:

>>> pendulum.parse("2016-01-12", tz="America/Sao_Paulo").add(months=1)
<Pendulum [2016-02-12T00:00:00-02:00]>
>>>pendulum.parse("2016-01-12", tz="America/Sao_Paulo").add(months=4)
<Pendulum [2016-05-11T23:00:00-03:00]>
>>> pendulum.parse("2016-01-30", tz="America/Sao_Paulo").add(months=4)
<Pendulum [2016-05-29T23:00:00-03:00]>

That's a behavior that seems undesired. I was expecting that every result had a 00:00:00 as I'm adding months, not hours, and I think the day numbers (12 and 30 in the example above) shouldn't be lost (unless I'm trying to move from january to february, but that wasn't the case).

Support arbitrary units for start_of, end_of and range

It would be very useful if the .start_of(), .end_of() and Period.range() methods could support arbitrary units (e.g. things like 15 minutes, 5 seconds etc.)

A possible API for this would be to allow the those methods to receive Interval objects in addition to the predefined string names.

Error instantiating certain timezones

While investigating the possibility of porting an existing project from pytz to pendulum I noticed some of our tests failing when instantiating certain timezones, for example CET, EET.

I'm running the tests on macOS 10.12.1 with Python 3.5.2 (macports).

In [1]: import pytz, pendulum

In [2]: pytz.timezone('CET')
Out[2]: <DstTzInfo 'CET' CET+1:00:00 STD>

In [3]: pendulum.timezone('CET')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-b4d458d3915f> in <module>()
----> 1 pendulum.timezone('CET')

.../lib/python3.5/site-packages/pendulum/tz/__init__.py in timezone(name)
     14     :rtype: Timezone
     15     """
---> 16     return Timezone.load(name)
     17
     18

.../lib/python3.5/site-packages/pendulum/tz/timezone.py in load(cls, name)
     65              transition_types,
     66              default_transition_type_index,
---> 67              utc_transition_times) = Loader.load(name)
     68
     69             zone = cls(name,

.../lib/python3.5/site-packages/pendulum/tz/loader.py in load(cls, name)
     35         try:
     36             with pytz.open_resource(name) as f:
---> 37                 return cls._load(f)
     38         except _compat.FileNotFoundError:
     39             raise ValueError('Unknown timezone [{}]'.format(name))

.../lib/python3.5/site-packages/pendulum/tz/loader.py in _load(cls, fp)
    133             index = 0
    134             if transition_types[0].is_dst:
--> 135                 index = transition_types.index(transitions[0].transition_type)
    136                 while index != 0 and transition_types[index].is_dst:
    137                     index -= 1

AttributeError: 'Transition' object has no attribute 'transition_type'

Interval: allow comparing of Pendulum() and Period() objects

After having defined p1 (from now until tomorrow):

>>> now = pendulum.utcnow()
>>> p1 = pendulum.Period(utcnow, now.tomorrow())

I can ask whether a specific Pendulum object is inside p1:

>>> now.add(hours=1) in p1
True
>>> now.yesterday() in p1
False

But when a Pendulum object is outside a given Period, I can't ask whether its before or after the Period:

>>> now.yesterday() < p1
TypeError: unorderable types: Pendulum() < Period()

Of course, I could compare p1.start and p1.end, but this would make it more convenient.

pendulum.now invalid argument "unix_time"

pendulum unable to create any date time instances due to unix_time not being loaded correctly on windows, seems okay under unix.

Win7 x64 Ent

(.env) C:\projects\scripts\python\timescamp (develop)
λ pip freeze
pendulum==0.6.1
python-dateutil==2.5.3
pytz==2016.6.1
requests==2.11.1
six==1.10.0
tzlocal==1.2.2
(.env) C:\projects\scripts\python\timescamp (develop)
λ python
Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pendulum
>>> pendulum.now()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\pendulum.py", line 287, in now
    return cls.instance(dt, tz)
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\pendulum.py", line 223, in instance
    tzinfo=tz
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\pendulum.py", line 173, in __init__
    self._tz = self._safe_create_datetime_zone(tzinfo)
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\pendulum.py", line 100, in _safe_create_datetime_zone
    return cls._local_timezone()
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\pendulum.py", line 119, in _local_timezone
    return local_timezone()
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\tz\__init__.py", line 25, in local_timezone
    return LocalTimezone.get()
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\tz\local_timezone.py", line 29, in get
    cls._cache = Timezone.load(cls.get_local_tz_name())
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\tz\timezone.py", line 67, in load
    utc_transition_times) = Loader.load(name)
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\tz\loader.py", line 37, in load
    return cls._load(f)
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\tz\loader.py", line 122, in _load
    pre_transition_type_index
  File "C:\projects\scripts\python\timescamp\.env\lib\site-packages\pendulum\tz\transition.py", line 44, in __init__
    self._utc_time = datetime.utcfromtimestamp(unix_time)
OSError: [Errno 22] Invalid argument

Synology x86_64 (unix)

(test) scott@pandora:~/scripts/python/pendulum$ pip freeze
pendulum==0.6.1
python-dateutil==2.5.3
pytz==2016.6.1
six==1.10.0
tzlocal==1.2.2
(test) scott@pandora:~/scripts/python/pendulum$ python3
Python 3.5.1 (default, Feb 23 2016, 17:47:45)
[GCC 4.9.3 20150311 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pendulum
>>> pendulum.now()
<Pendulum [2016-09-21T09:33:08.239546+10:00]>
>>>

Python Version

Hi, need to writing python supporting version in README.md or Official Site.
python 2.7 and 3.5

Option for day the week starts on week_of_year

As you know,
A week can start either on a Monday or Sunday (maybe even other days).

Python has that feature already done with strftime:

  • %U Sunday as the first day of the week.
  • %W Monday as the first day of the week.
dt = datetime.datetime(2016, 10, 30)

print(dt.strftime("%U"))
 '44'

print(dt.strftime("%W"))
 '43'

Pendulum's week_of_year currently uses datetime's isocalandar (docs "week starts on a Monday and ends on a Sunday").

There needs to be a way to have both Sunday and Monday as an option because its not consistent across software (e.g. MySQL which uses Sunday). and also, why not have it if python supports it.

I would submit a pull request myself, but this is a design problem because its a property and not a function, so no args possible.

What do you think?

Pendulum.timezone_ causes out of sync with datetime.tzinfo

I'm a bit confused about the implementation of Pendulum._timezone. What I assume would happen if I change only the timezone is that the time and date remain the same but the timezone and offset would change. I'm not sure whether this is a bug or not, but changing the timezone using Pendulum.timezone_ does not change the datetime.tzinfo and therefore not the offset. It makes Pendulum.timezone and datetime.tzinfo out of sync.

>>> now = pendulum.now()
>>> now
# <Pendulum [2016-11-02T00:30:16.246721+01:00]>

>>> utc_ = now.timezone_('UTC')
>>> utc_
# <Pendulum [2016-11-02T00:30:16.246721+01:00]>
>>> utc_.tzinfo
# <TimezoneInfo [Europe/Brussels, 3600, False]>
>>> utc_.timezone
# <Timezone [UTC]>

>>> utc = now.in_timezone('UTC')
>>> utc
# <Pendulum [2016-11-01T23:30:16.246721+00:00]>
>>> utc.tzinfo
# <TimezoneInfo [UTC, 0, False]>
>>> utc.timezone
# <Timezone [UTC]>

Expected behaviour:

>>> now = pendulum.now()
>>> now
# <Pendulum [2016-11-02T00:34:59.665176+01:00]>
>>> now.timezone_('UTC')
# <Pendulum [2016-11-02T00:34:59.665176+00:00]>

Is this a bug in Pendulum or intended behaviour? Could you perhaps explain to me what the use case would be if it is intended behaviour?

Pendulum==0.5.4 on ArchLinux timezone broken

  • python 3.5.2
  • pendulum 0.5.2
import pendulum
pendulum.now()

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.5/site-packages/pendulum/pendulum.py", line 274, in now
    return cls.instance(dt, tz)
  File "/usr/lib/python3.5/site-packages/pendulum/pendulum.py", line 205, in instance
    tzinfo=tz
  File "/usr/lib/python3.5/site-packages/pendulum/pendulum.py", line 180, in __init__
    self._tz = self._safe_create_datetime_zone(tzinfo)
  File "/usr/lib/python3.5/site-packages/pendulum/pendulum.py", line 111, in _safe_create_datetime_zone
    return cls._local_timezone()
  File "/usr/lib/python3.5/site-packages/pendulum/pendulum.py", line 130, in _local_timezone
    return local_timezone()
  File "/usr/lib/python3.5/site-packages/pendulum/tz/__init__.py", line 25, in local_timezone
    return LocalTimezone.get()
  File "/usr/lib/python3.5/site-packages/pendulum/tz/local_timezone.py", line 25, in get
    name = cls.get_local_tz_name()
  File "/usr/lib/python3.5/site-packages/pendulum/tz/local_timezone.py", line 60, in get_local_tz_name
    return getattr(cls, 'get_tz_name_for_{}'.format(os))()
  File "/usr/lib/python3.5/site-packages/pendulum/tz/local_timezone.py", line 157, in get_tz_name_for_unix
    return Timezone.load(tzpath)
  File "/usr/lib/python3.5/site-packages/pendulum/tz/timezone.py", line 50, in load
    default_transition_type) = Loader.load(name)
  File "/usr/lib/python3.5/site-packages/pendulum/tz/loader.py", line 36, in load
    with pytz.open_resource(name) as f:
  File "/usr/lib/python3.5/site-packages/pytz-2016.6.1-py3.5.egg/pytz/__init__.py", line 96, in open_resource
  File "/usr/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1208, in resource_stream
    self, resource_name
  File "/usr/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1486, in get_resource_stream
    return io.BytesIO(self.get_resource_string(manager, resource_name))
  File "/usr/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1489, in get_resource_string
    return self._get(self._fn(self.module_path, resource_name))
  File "/usr/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1562, in _get
    return self.loader.get_data(path)
OSError: [Errno 0] Error: 'pytz/zoneinfo/usr/share/zoneinfo/Europe/Bratislava'

After reverting back to 0.5.2 it works as expected:

import pendulum
pendulum.now()
<Pendulum [2016-08-31T06:17:32.144487+02:00]>

Adding Interval to Pendulum produces incorrect results

from pendulum import Pendulum, Interval

p = Pendulum.now()
p2 = p + Interval(minutes=15)
assert p == p2

The reason is that Pendulum.add_timedelta() assumes a default python timedelta object and can't cope with the non-normalizing behavior of Interval.

How can get age in year, month, day

How can I get the age in this format: 32 years 3 months 45 days ?

I try to use:

age = pendulum.from_date(self.birth.year, self.birth.month, self.birth.day)

Issue with Python 3.2

I installed pendulum on Python 3.2 install, and when I run it, here's what I get:

 % 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
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.2/dist-packages/pendulum/__init__.py", line 3, in <module>
    from .pendulum import Pendulum
  File "/usr/local/lib/python3.2/dist-packages/pendulum/pendulum.py", line 19, in <module>
    from .tz import Timezone, UTC, FixedTimezone, local_timezone
  File "/usr/local/lib/python3.2/dist-packages/pendulum/tz/__init__.py", line 3, in <module>
    from .timezone import Timezone, FixedTimezone, UTC
  File "/usr/local/lib/python3.2/dist-packages/pendulum/tz/timezone.py", line 6, in <module>
    from .loader import Loader
  File "/usr/local/lib/python3.2/dist-packages/pendulum/tz/loader.py", line 10, in <module>
    from .. import _compat
  File "/usr/local/lib/python3.2/dist-packages/pendulum/_compat.py", line 19, in <module>
    FileNotFoundError = FileNotFoundError
NameError: name 'FileNotFoundError' is not defined

It looks like the FileNotFoundErrorexception, along with the rework of the OS/IO exceptions has been introduced in Python 3.3.
cf

I do not understand why FileNotFoundError could not defined in Python 3.2. cf PEP-3151

Instead of relying on major python versions to check for features, I'd suggest to check for the feature and fallback to the alternative. Such as:

try:
   FileNotFoundError = FileNotFoundError
except NameError:
   FileNotFoundError = IOError

The Python3.2.3 I'm using, is the one from Debian Wheezy (old-stable) and I installed pendulum v0.5.5 from Pypi. I tried with the suggested fix above, and it works.

Can't import pendulum inside a test file using pytest

Importing pendulum inside a test file and running pytest returns an error about importing language files.

Running the following code:

import pytest
import pendulum

def test_import_pendulum():
    date = pendulum.now(tz='America/Sao_Paulo')
    assert data.is_today() == True

returns the following message:

ImportError while importing test module '/home/decko/dev/temp/test_pendulum.py'.
Original error message:
'No module named no'
Make sure your test modules/packages have valid Python names.

UTC object in datetime timezones prevent instantiation

Using a lib I received a datetime object I wanted to convert to pendulum, so I did the following and it failed because of the tzinfo not being a string but an UTC instance:

>>> pendulum.instance(repo.updated_at)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "…/pendulum/pendulum.py", line 205, in instance
    tzinfo=tz
  File "…/pendulum/pendulum.py", line 180, in __init__
    self._tz = self._safe_create_datetime_zone(tzinfo)
  File "…/pendulum/pendulum.py", line 118, in _safe_create_datetime_zone
    tz = cls._timezone(obj)
  File "…/pendulum/pendulum.py", line 148, in _timezone
    return Timezone.load(zone)
  File "…/pendulum/tz/timezone.py", line 44, in load
    if name.upper() == 'UTC':
AttributeError: 'UTC' object has no attribute 'upper'
>>> repo.updated_at
datetime.datetime(2016, 10, 16, 10, 24, 5, tzinfo=UTC())

As a workaround I'm doing:

>>> pendulum.parse(repo.updated_at.isoformat())
<Pendulum [2016-10-16T10:24:05+00:00]>
>>>

but it sucks to be using a string as an intermediate format, when we could convert directly 😖

Applies timezone normalization for start_of()/end_of() methods

Currently, when using the start_of()/last_of() methods, timezone transitions are not properly applied:

import pendulum

dt = pendulum.create(2013, 8, 1, 0, 0, 0, 0, 'Europe/Paris')
# <Pendulum [2013-08-01T00:00:00+02:00]>
dt.start_of('year')
# <Pendulum [2013-01-01T00:00:00+02:00]>
# Should be <Pendulum [2013-01-01T00:00:00+01:00]>

AttributeError when using utcoffset

Sample code to reproduce error:

$> python

import pendulum
tz = pendulum.timezone('Europe/Paris')
tz.utcoffset(pendulum.utcnow())

Exception:

Traceback (most recent call last):
File "", line 1, in
File "/Users/thomaswilgenbus/.pyenv/versions/tex-connector-acs/lib/python2.7/site-packages/pendulum/tz/timezone.py", line 283, in utcoffset
return dt.adjusted_offset
AttributeError: 'Pendulum' object has no attribute 'adjusted_offset'

interval diff.in_years() is returning incorrect values

using the following code: ** Note this was tested on 11/5/2016, so result should be 15, instead it is returning 16

from pendulum import Pendulum as p
d1 = p(2000,11,20) 
diff = d1.diff()
diff.in_years()
16

Trial and error shows that it reported 15 when I set the date to 2000,11,25 meaning that it was 10 days off.

Cleanup API

  • Remove unnecessary methods (add_xxx(), sub_xxx())
  • Rename ambiguous methods (diff_for_humans() for instance)

Exception when local time set to "Etc/UTC"

OS X 10.11.6, Python 3.5.1, Pendulum 0.6.4

sudo ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime
python -c 'import pendulum; pendulum.now().add(seconds=3)'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/Users/gavin/Development/MarksAndSpencer/cfto-browse/venv/lib/python3.5/site-packages/pendulum/pendulum.py", line 1446, in add
    dt = self._tz.convert(dt)
  File "/Users/gavin/Development/MarksAndSpencer/cfto-browse/venv/lib/python3.5/site-packages/pendulum/tz/timezone.py", line 93, in convert
    converted = self._convert(dt)
  File "/Users/gavin/Development/MarksAndSpencer/cfto-browse/venv/lib/python3.5/site-packages/pendulum/tz/timezone.py", line 218, in _convert
    return dt.astimezone(self)
  File "/Users/gavin/Development/MarksAndSpencer/cfto-browse/venv/lib/python3.5/site-packages/pendulum/tz/timezone.py", line 301, in fromutc
    tr = self._transitions[idx]
IndexError: tuple index out of range

Operator actions on Interval/Period instances produce timedelta objects

Doing various math operator actions on timedelta subclasses produces timedelta instances instances. This is partially because of the frailty of datetime.timedelta code but it would be highly preferable to support operations like avg_perdio = total_period / some_count and get a Period/Interval object back instead of a timedelta object. It's also quite confusing since the APIs are very different between all 3 of these subclasses.

Example..

>>> import pendulum
>>> iobj = pendulum.Interval(seconds=10)
>>> x = iobj / 3
>>> x
datetime.timedelta(0, 3, 333333)
>>> type(x)
<class 'datetime.timedelta'>
>>> type(iobj)
<class 'pendulum.interval.Interval'>
>>> 

I took a look at the code and unfortunately it looks like you'd have to subclass all the relevant operator calls (__div__, etc) and patch them to produce new Interval or Period objects. Because Interval and Period have very different instantiation patterns it might not even be possible to implement in the base Interval class. However I'll leave it to you to review.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.