isodate-0.6.0/0000755€YMd0€T]®j0000000000013170103163012777 5ustar s261679400000000000000isodate-0.6.0/CHANGES.txt0000644€YMd0€T]®j0000000433613170041016014614 0ustar s261679400000000000000 CHANGES ======= 0.6.0 (2017-10-13) ------------------ - support incomplete month date (Fabien Loffredo) - rely on duck typing when doing duration maths - support ':' as separator in fractional time zones (usrenmae) 0.5.4 (2015-08-06) ------------------ - Fix parsing of Periods (Fabien Bochu) - Make Duration objects hashable (Geoffrey Fairchild) - Add multiplication to duration (Reinoud Elhorst) 0.5.1 (2014-11-07) ------------------ - fixed pickling of Duration objects - raise ISO8601Error when there is no 'T' separator in datetime strings (Adrian Coveney) 0.5.0 (2014-02-23) ------------------ - ISO8601Error are subclasses of ValueError now (Michael Hrivnak) - improve compatibility across various python variants and versions - raise exceptions when using fractional years and months in date maths with durations - renamed method todatetime on Duraction objects to totimedelta 0.4.9 (2012-10-30) ------------------ - support pickling FixedOffset instances - make sure parsed fractional seconds are in microseconds - add leading zeros when formattig microseconds (Jarom Loveridge) 0.4.8 (2012-05-04) ------------------ - fixed incompatibility of unittests with python 2.5 and 2.6 (runs fine on 2.7 and 3.2) 0.4.7 (2012-01-26) ------------------ - fixed tzinfo formatting (never pass None into tzinfo.utcoffset()) 0.4.6 (2012-01-06) ------------------ - added Python 3 compatibility via 2to3 0.4.5 (2012-01-06) ------------------ - made setuptools dependency optional 0.4.4 (2011-04-16) ------------------ - Fixed formatting of microseconds for datetime objects 0.4.3 (2010-10-29) ------------------ - Fixed problem with %P formating and fractions (supplied by David Brooks) 0.4.2 (2010-10-28) ------------------ - Implemented unary - for Duration (supplied by David Brooks) - Output fractional seconds with '%P' format. (partly supplied by David Brooks) 0.4.1 (2010-10-13) ------------------ - fixed bug in comparison between timedelta and Duration. - fixed precision problem with microseconds (reported by Tommi Virtanen) 0.4.0 (2009-02-09) ------------------ - added method to parse ISO 8601 time zone strings - added methods to create ISO 8601 conforming strings 0.3.0 (2009-1-05) ------------------ - Initial release isodate-0.6.0/MANIFEST.in0000644€YMd0€T]®j0000000004512302221023014524 0ustar s261679400000000000000include CHANGES.txt include TODO.txt isodate-0.6.0/PKG-INFO0000644€YMd0€T]®j0000002625713170103163014110 0ustar s261679400000000000000Metadata-Version: 1.1 Name: isodate Version: 0.6.0 Summary: An ISO 8601 date/time/duration parser and formatter Home-page: https://github.com/gweis/isodate/ Author: Gerhard Weis Author-email: gerhard.weis@proclos.com License: BSD Description: ISO 8601 date/time parser ========================= .. image:: https://travis-ci.org/gweis/isodate.svg?branch=master :target: https://travis-ci.org/gweis/isodate :alt: Travis-CI .. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master :target: https://coveralls.io/r/gweis/isodate?branch=master :alt: Coveralls .. image:: https://img.shields.io/pypi/v/isodate.svg :target: https://pypi.python.org/pypi/isodate/ :alt: Latest Version .. image:: https://img.shields.io/pypi/l/isodate.svg :target: https://pypi.python.org/pypi/isodate/ :alt: License This module implements ISO 8601 date, time and duration parsing. The implementation follows ISO8601:2004 standard, and implements only date/time representations mentioned in the standard. If something is not mentioned there, then it is treated as non existent, and not as an allowed option. For instance, ISO8601:2004 never mentions 2 digit years. So, it is not intended by this module to support 2 digit years. (while it may still be valid as ISO date, because it is not explicitly forbidden.) Another example is, when no time zone information is given for a time, then it should be interpreted as local time, and not UTC. As this module maps ISO 8601 dates/times to standard Python data types, like *date*, *time*, *datetime* and *timedelta*, it is not possible to convert all possible ISO 8601 dates/times. For instance, dates before 0001-01-01 are not allowed by the Python *date* and *datetime* classes. Additionally fractional seconds are limited to microseconds. That means if the parser finds for instance nanoseconds it will round it to microseconds. Documentation ------------- Currently there are four parsing methods available. * parse_time: parses an ISO 8601 time string into a *time* object * parse_date: parses an ISO 8601 date string into a *date* object * parse_datetime: parses an ISO 8601 date-time string into a *datetime* object * parse_duration: parses an ISO 8601 duration string into a *timedelta* or *Duration* object. * parse_tzinfo: parses the time zone info part of an ISO 8601 string into a *tzinfo* object. As ISO 8601 allows to define durations in years and months, and *timedelta* does not handle years and months, this module provides a *Duration* class, which can be used almost like a *timedelta* object (with some limitations). However, a *Duration* object can be converted into a *timedelta* object. There are also ISO formatting methods for all supported data types. Each *xxx_isoformat* method accepts a format parameter. The default format is always the ISO 8601 expanded format. This is the same format used by *datetime.isoformat*: * time_isoformat: Intended to create ISO time strings with default format *hh:mm:ssZ*. * date_isoformat: Intended to create ISO date strings with default format *yyyy-mm-dd*. * datetime_isoformat: Intended to create ISO date-time strings with default format *yyyy-mm-ddThh:mm:ssZ*. * duration_isoformat: Intended to create ISO duration strings with default format *PnnYnnMnnDTnnHnnMnnS*. * tz_isoformat: Intended to create ISO time zone strings with default format *hh:mm*. * strftime: A re-implementation mostly compatible with Python's *strftime*, but supports only those format strings, which can also be used for dates prior 1900. This method also understands how to format *datetime* and *Duration* instances. Installation: ------------- This module can easily be installed with Python standard installation methods. Either use *python setup.py install* or in case you have *setuptools* or *distribute* available, you can also use *easy_install*. Limitations: ------------ * The parser accepts several date/time representation which should be invalid according to ISO 8601 standard. 1. for date and time together, this parser accepts a mixture of basic and extended format. e.g. the date could be in basic format, while the time is accepted in extended format. It also allows short dates and times in date-time strings. 2. For incomplete dates, the first day is chosen. e.g. 19th century results in a date of 1901-01-01. 3. negative *Duration* and *timedelta* value are not fully supported yet. Further information: -------------------- The doc strings and unit tests should provide rather detailed information about the methods and their limitations. The source release provides a *setup.py* script, which can be used to run the unit tests included. Source code is available at ``_. CHANGES ======= 0.6.0 (2017-10-13) ------------------ - support incomplete month date (Fabien Loffredo) - rely on duck typing when doing duration maths - support ':' as separator in fractional time zones (usrenmae) 0.5.4 (2015-08-06) ------------------ - Fix parsing of Periods (Fabien Bochu) - Make Duration objects hashable (Geoffrey Fairchild) - Add multiplication to duration (Reinoud Elhorst) 0.5.1 (2014-11-07) ------------------ - fixed pickling of Duration objects - raise ISO8601Error when there is no 'T' separator in datetime strings (Adrian Coveney) 0.5.0 (2014-02-23) ------------------ - ISO8601Error are subclasses of ValueError now (Michael Hrivnak) - improve compatibility across various python variants and versions - raise exceptions when using fractional years and months in date maths with durations - renamed method todatetime on Duraction objects to totimedelta 0.4.9 (2012-10-30) ------------------ - support pickling FixedOffset instances - make sure parsed fractional seconds are in microseconds - add leading zeros when formattig microseconds (Jarom Loveridge) 0.4.8 (2012-05-04) ------------------ - fixed incompatibility of unittests with python 2.5 and 2.6 (runs fine on 2.7 and 3.2) 0.4.7 (2012-01-26) ------------------ - fixed tzinfo formatting (never pass None into tzinfo.utcoffset()) 0.4.6 (2012-01-06) ------------------ - added Python 3 compatibility via 2to3 0.4.5 (2012-01-06) ------------------ - made setuptools dependency optional 0.4.4 (2011-04-16) ------------------ - Fixed formatting of microseconds for datetime objects 0.4.3 (2010-10-29) ------------------ - Fixed problem with %P formating and fractions (supplied by David Brooks) 0.4.2 (2010-10-28) ------------------ - Implemented unary - for Duration (supplied by David Brooks) - Output fractional seconds with '%P' format. (partly supplied by David Brooks) 0.4.1 (2010-10-13) ------------------ - fixed bug in comparison between timedelta and Duration. - fixed precision problem with microseconds (reported by Tommi Virtanen) 0.4.0 (2009-02-09) ------------------ - added method to parse ISO 8601 time zone strings - added methods to create ISO 8601 conforming strings 0.3.0 (2009-1-05) ------------------ - Initial release TODOs ===== This to do list contains some thoughts and ideas about missing features, and parts to think about, whether to implement them or not. This list is probably not complete. Missing features: ----------------- * time formating does not allow to create fractional representations. * parser for ISO intervals. * currently microseconds are always padded to a length of 6 characters. trailing 0s should be optional Documentation: -------------- * parse_datetime: - complete documentation to show what this function allows, but ISO forbids. and vice verse. - support other separators between date and time than 'T' * parse_date: - yeardigits should be always greater than 4 - dates before 0001-01-01 are not supported * parse_duration: - alternative formats are not fully supported due to parse_date restrictions - standard duration format is fully supported but not very restrictive. * Duration: - support fractional years and month in calculations - implement w3c order relation? (``_) - refactor to have duration mathematics only at one place. - localize __str__ method (does timedelta do this?) - when is a Duration negative? - normalize Durations. months [00-12] and years ]-inf,+inf[ Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Internet Classifier: Topic :: Software Development :: Libraries :: Python Modules isodate-0.6.0/README.rst0000644€YMd0€T]®j0000001126513170040020014463 0ustar s261679400000000000000 ISO 8601 date/time parser ========================= .. image:: https://travis-ci.org/gweis/isodate.svg?branch=master :target: https://travis-ci.org/gweis/isodate :alt: Travis-CI .. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master :target: https://coveralls.io/r/gweis/isodate?branch=master :alt: Coveralls .. image:: https://img.shields.io/pypi/v/isodate.svg :target: https://pypi.python.org/pypi/isodate/ :alt: Latest Version .. image:: https://img.shields.io/pypi/l/isodate.svg :target: https://pypi.python.org/pypi/isodate/ :alt: License This module implements ISO 8601 date, time and duration parsing. The implementation follows ISO8601:2004 standard, and implements only date/time representations mentioned in the standard. If something is not mentioned there, then it is treated as non existent, and not as an allowed option. For instance, ISO8601:2004 never mentions 2 digit years. So, it is not intended by this module to support 2 digit years. (while it may still be valid as ISO date, because it is not explicitly forbidden.) Another example is, when no time zone information is given for a time, then it should be interpreted as local time, and not UTC. As this module maps ISO 8601 dates/times to standard Python data types, like *date*, *time*, *datetime* and *timedelta*, it is not possible to convert all possible ISO 8601 dates/times. For instance, dates before 0001-01-01 are not allowed by the Python *date* and *datetime* classes. Additionally fractional seconds are limited to microseconds. That means if the parser finds for instance nanoseconds it will round it to microseconds. Documentation ------------- Currently there are four parsing methods available. * parse_time: parses an ISO 8601 time string into a *time* object * parse_date: parses an ISO 8601 date string into a *date* object * parse_datetime: parses an ISO 8601 date-time string into a *datetime* object * parse_duration: parses an ISO 8601 duration string into a *timedelta* or *Duration* object. * parse_tzinfo: parses the time zone info part of an ISO 8601 string into a *tzinfo* object. As ISO 8601 allows to define durations in years and months, and *timedelta* does not handle years and months, this module provides a *Duration* class, which can be used almost like a *timedelta* object (with some limitations). However, a *Duration* object can be converted into a *timedelta* object. There are also ISO formatting methods for all supported data types. Each *xxx_isoformat* method accepts a format parameter. The default format is always the ISO 8601 expanded format. This is the same format used by *datetime.isoformat*: * time_isoformat: Intended to create ISO time strings with default format *hh:mm:ssZ*. * date_isoformat: Intended to create ISO date strings with default format *yyyy-mm-dd*. * datetime_isoformat: Intended to create ISO date-time strings with default format *yyyy-mm-ddThh:mm:ssZ*. * duration_isoformat: Intended to create ISO duration strings with default format *PnnYnnMnnDTnnHnnMnnS*. * tz_isoformat: Intended to create ISO time zone strings with default format *hh:mm*. * strftime: A re-implementation mostly compatible with Python's *strftime*, but supports only those format strings, which can also be used for dates prior 1900. This method also understands how to format *datetime* and *Duration* instances. Installation: ------------- This module can easily be installed with Python standard installation methods. Either use *python setup.py install* or in case you have *setuptools* or *distribute* available, you can also use *easy_install*. Limitations: ------------ * The parser accepts several date/time representation which should be invalid according to ISO 8601 standard. 1. for date and time together, this parser accepts a mixture of basic and extended format. e.g. the date could be in basic format, while the time is accepted in extended format. It also allows short dates and times in date-time strings. 2. For incomplete dates, the first day is chosen. e.g. 19th century results in a date of 1901-01-01. 3. negative *Duration* and *timedelta* value are not fully supported yet. Further information: -------------------- The doc strings and unit tests should provide rather detailed information about the methods and their limitations. The source release provides a *setup.py* script, which can be used to run the unit tests included. Source code is available at ``_. isodate-0.6.0/setup.cfg0000644€YMd0€T]®j0000000010313170103163014612 0ustar s261679400000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 isodate-0.6.0/setup.py0000644€YMd0€T]®j0000000632313170041062014514 0ustar s261679400000000000000#!/usr/bin/env python ############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## import os from setuptools import setup def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() setup(name='isodate', version='0.6.0', packages=['isodate', 'isodate.tests'], package_dir={'': 'src'}, # dependencies: install_requires=[ 'six' ], # PyPI metadata author='Gerhard Weis', author_email='gerhard.weis@proclos.com', description='An ISO 8601 date/time/duration parser and formatter', license='BSD', # keywords = '', url='https://github.com/gweis/isodate/', long_description=(read('README.rst') + read('CHANGES.txt') + read('TODO.txt')), classifiers=['Development Status :: 4 - Beta', # 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Internet', ('Topic :: Software Development :' ': Libraries :: Python Modules'), ], test_suite='isodate.tests.test_suite') isodate-0.6.0/src/0000755€YMd0€T]®j0000000000013170103163013566 5ustar s261679400000000000000isodate-0.6.0/src/isodate/0000755€YMd0€T]®j0000000000013170103163015216 5ustar s261679400000000000000isodate-0.6.0/src/isodate/__init__.py0000644€YMd0€T]®j0000001000313127314601017324 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' Import all essential functions and constants to re-export them here for easy access. This module contains also various pre-defined ISO 8601 format strings. ''' from isodate.isodates import parse_date, date_isoformat from isodate.isotime import parse_time, time_isoformat from isodate.isodatetime import parse_datetime, datetime_isoformat from isodate.isoduration import parse_duration, duration_isoformat from isodate.isoerror import ISO8601Error from isodate.isotzinfo import parse_tzinfo, tz_isoformat from isodate.tzinfo import UTC, FixedOffset, LOCAL from isodate.duration import Duration from isodate.isostrf import strftime from isodate.isostrf import DATE_BAS_COMPLETE, DATE_BAS_ORD_COMPLETE from isodate.isostrf import DATE_BAS_WEEK, DATE_BAS_WEEK_COMPLETE from isodate.isostrf import DATE_CENTURY, DATE_EXT_COMPLETE from isodate.isostrf import DATE_EXT_ORD_COMPLETE, DATE_EXT_WEEK from isodate.isostrf import DATE_EXT_WEEK_COMPLETE, DATE_YEAR from isodate.isostrf import DATE_BAS_MONTH, DATE_EXT_MONTH from isodate.isostrf import TIME_BAS_COMPLETE, TIME_BAS_MINUTE from isodate.isostrf import TIME_EXT_COMPLETE, TIME_EXT_MINUTE from isodate.isostrf import TIME_HOUR from isodate.isostrf import TZ_BAS, TZ_EXT, TZ_HOUR from isodate.isostrf import DT_BAS_COMPLETE, DT_EXT_COMPLETE from isodate.isostrf import DT_BAS_ORD_COMPLETE, DT_EXT_ORD_COMPLETE from isodate.isostrf import DT_BAS_WEEK_COMPLETE, DT_EXT_WEEK_COMPLETE from isodate.isostrf import D_DEFAULT, D_WEEK, D_ALT_EXT, D_ALT_BAS from isodate.isostrf import D_ALT_BAS_ORD, D_ALT_EXT_ORD __all__ = ['parse_date', 'date_isoformat', 'parse_time', 'time_isoformat', 'parse_datetime', 'datetime_isoformat', 'parse_duration', 'duration_isoformat', 'ISO8601Error', 'parse_tzinfo', 'tz_isoformat', 'UTC', 'FixedOffset', 'LOCAL', 'Duration', 'strftime', 'DATE_BAS_COMPLETE', 'DATE_BAS_ORD_COMPLETE', 'DATE_BAS_WEEK', 'DATE_BAS_WEEK_COMPLETE', 'DATE_CENTURY', 'DATE_EXT_COMPLETE', 'DATE_EXT_ORD_COMPLETE', 'DATE_EXT_WEEK', 'DATE_EXT_WEEK_COMPLETE', 'DATE_YEAR', 'DATE_BAS_MONTH', 'DATE_EXT_MONTH', 'TIME_BAS_COMPLETE', 'TIME_BAS_MINUTE', 'TIME_EXT_COMPLETE', 'TIME_EXT_MINUTE', 'TIME_HOUR', 'TZ_BAS', 'TZ_EXT', 'TZ_HOUR', 'DT_BAS_COMPLETE', 'DT_EXT_COMPLETE', 'DT_BAS_ORD_COMPLETE', 'DT_EXT_ORD_COMPLETE', 'DT_BAS_WEEK_COMPLETE', 'DT_EXT_WEEK_COMPLETE', 'D_DEFAULT', 'D_WEEK', 'D_ALT_EXT', 'D_ALT_BAS', 'D_ALT_BAS_ORD', 'D_ALT_EXT_ORD'] isodate-0.6.0/src/isodate/duration.py0000644€YMd0€T]®j0000003023213170040127017415 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' This module defines a Duration class. The class Duration allows to define durations in years and months and can be used as limited replacement for timedelta objects. ''' from datetime import timedelta from decimal import Decimal, ROUND_FLOOR def fquotmod(val, low, high): ''' A divmod function with boundaries. ''' # assumes that all the maths is done with Decimals. # divmod for Decimal uses truncate instead of floor as builtin # divmod, so we have to do it manually here. a, b = val - low, high - low div = (a / b).to_integral(ROUND_FLOOR) mod = a - div * b # if we were not usig Decimal, it would look like this. # div, mod = divmod(val - low, high - low) mod += low return int(div), mod def max_days_in_month(year, month): ''' Determines the number of days of a specific month in a specific year. ''' if month in (1, 3, 5, 7, 8, 10, 12): return 31 if month in (4, 6, 9, 11): return 30 if ((year % 400) == 0) or ((year % 100) != 0) and ((year % 4) == 0): return 29 return 28 class Duration(object): ''' A class which represents a duration. The difference to datetime.timedelta is, that this class handles also differences given in years and months. A Duration treats differences given in year, months separately from all other components. A Duration can be used almost like any timedelta object, however there are some restrictions: * It is not really possible to compare Durations, because it is unclear, whether a duration of 1 year is bigger than 365 days or not. * Equality is only tested between the two (year, month vs. timedelta) basic components. A Duration can also be converted into a datetime object, but this requires a start date or an end date. The algorithm to add a duration to a date is defined at http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes ''' def __init__(self, days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0, months=0, years=0): ''' Initialise this Duration instance with the given parameters. ''' if not isinstance(months, Decimal): months = Decimal(str(months)) if not isinstance(years, Decimal): years = Decimal(str(years)) self.months = months self.years = years self.tdelta = timedelta(days, seconds, microseconds, milliseconds, minutes, hours, weeks) def __getstate__(self): return self.__dict__ def __setstate__(self, state): self.__dict__.update(state) def __getattr__(self, name): ''' Provide direct access to attributes of included timedelta instance. ''' return getattr(self.tdelta, name) def __str__(self): ''' Return a string representation of this duration similar to timedelta. ''' params = [] if self.years: params.append('%d years' % self.years) if self.months: fmt = "%d months" if self.months <= 1: fmt = "%d month" params.append(fmt % self.months) params.append(str(self.tdelta)) return ', '.join(params) def __repr__(self): ''' Return a string suitable for repr(x) calls. ''' return "%s.%s(%d, %d, %d, years=%d, months=%d)" % ( self.__class__.__module__, self.__class__.__name__, self.tdelta.days, self.tdelta.seconds, self.tdelta.microseconds, self.years, self.months) def __hash__(self): ''' Return a hash of this instance so that it can be used in, for example, dicts and sets. ''' return hash((self.tdelta, self.months, self.years)) def __neg__(self): """ A simple unary minus. Returns a new Duration instance with all it's negated. """ negduration = Duration(years=-self.years, months=-self.months) negduration.tdelta = -self.tdelta return negduration def __add__(self, other): ''' Durations can be added with Duration, timedelta, date and datetime objects. ''' if isinstance(other, Duration): newduration = Duration(years=self.years + other.years, months=self.months + other.months) newduration.tdelta = self.tdelta + other.tdelta return newduration try: # try anything that looks like a date or datetime # 'other' has attributes year, month, day # and relies on 'timedelta + other' being implemented if (not(float(self.years).is_integer() and float(self.months).is_integer())): raise ValueError('fractional years or months not supported' ' for date calculations') newmonth = other.month + self.months carry, newmonth = fquotmod(newmonth, 1, 13) newyear = other.year + self.years + carry maxdays = max_days_in_month(newyear, newmonth) if other.day > maxdays: newday = maxdays else: newday = other.day newdt = other.replace(year=newyear, month=newmonth, day=newday) # does a timedelta + date/datetime return self.tdelta + newdt except AttributeError: # other probably was not a date/datetime compatible object pass try: # try if other is a timedelta # relies on timedelta + timedelta supported newduration = Duration(years=self.years, months=self.months) newduration.tdelta = self.tdelta + other return newduration except AttributeError: # ignore ... other probably was not a timedelta compatible object pass # we have tried everything .... return a NotImplemented return NotImplemented __radd__ = __add__ def __mul__(self, other): if isinstance(other, int): newduration = Duration( years=self.years * other, months=self.months * other) newduration.tdelta = self.tdelta * other return newduration return NotImplemented __rmul__ = __mul__ def __sub__(self, other): ''' It is possible to subtract Duration and timedelta objects from Duration objects. ''' if isinstance(other, Duration): newduration = Duration(years=self.years - other.years, months=self.months - other.months) newduration.tdelta = self.tdelta - other.tdelta return newduration try: # do maths with our timedelta object .... newduration = Duration(years=self.years, months=self.months) newduration.tdelta = self.tdelta - other return newduration except TypeError: # looks like timedelta - other is not implemented pass return NotImplemented def __rsub__(self, other): ''' It is possible to subtract Duration objecs from date, datetime and timedelta objects. TODO: there is some weird behaviour in date - timedelta ... if timedelta has seconds or microseconds set, then date - timedelta != date + (-timedelta) for now we follow this behaviour to avoid surprises when mixing timedeltas with Durations, but in case this ever changes in the stdlib we can just do: return -self + other instead of all the current code ''' if isinstance(other, timedelta): tmpdur = Duration() tmpdur.tdelta = other return tmpdur - self try: # check if other behaves like a date/datetime object # does it have year, month, day and replace? if (not(float(self.years).is_integer() and float(self.months).is_integer())): raise ValueError('fractional years or months not supported' ' for date calculations') newmonth = other.month - self.months carry, newmonth = fquotmod(newmonth, 1, 13) newyear = other.year - self.years + carry maxdays = max_days_in_month(newyear, newmonth) if other.day > maxdays: newday = maxdays else: newday = other.day newdt = other.replace(year=newyear, month=newmonth, day=newday) return newdt - self.tdelta except AttributeError: # other probably was not compatible with data/datetime pass return NotImplemented def __eq__(self, other): ''' If the years, month part and the timedelta part are both equal, then the two Durations are considered equal. ''' if isinstance(other, Duration): if (((self.years * 12 + self.months) == (other.years * 12 + other.months) and self.tdelta == other.tdelta)): return True return False # check if other con be compared against timedelta object # will raise an AssertionError when optimisation is off if self.years == 0 and self.months == 0: return self.tdelta == other return False def __ne__(self, other): ''' If the years, month part or the timedelta part is not equal, then the two Durations are considered not equal. ''' if isinstance(other, Duration): if (((self.years * 12 + self.months) != (other.years * 12 + other.months) or self.tdelta != other.tdelta)): return True return False # check if other can be compared against timedelta object # will raise an AssertionError when optimisation is off if self.years == 0 and self.months == 0: return self.tdelta != other return True def totimedelta(self, start=None, end=None): ''' Convert this duration into a timedelta object. This method requires a start datetime or end datetimem, but raises an exception if both are given. ''' if start is None and end is None: raise ValueError("start or end required") if start is not None and end is not None: raise ValueError("only start or end allowed") if start is not None: return (start + self) - start return end - (end - self) isodate-0.6.0/src/isodate/isodates.py0000644€YMd0€T]®j0000002457413127314601017422 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' This modules provides a method to parse an ISO 8601:2004 date string to a python datetime.date instance. It supports all basic, extended and expanded formats as described in the ISO standard. The only limitations it has, are given by the Python datetime.date implementation, which does not support dates before 0001-01-01. ''' import re from datetime import date, timedelta from isodate.isostrf import strftime, DATE_EXT_COMPLETE from isodate.isoerror import ISO8601Error DATE_REGEX_CACHE = {} # A dictionary to cache pre-compiled regular expressions. # A set of regular expressions is identified, by number of year digits allowed # and whether a plus/minus sign is required or not. (This option is changeable # only for 4 digit years). def build_date_regexps(yeardigits=4, expanded=False): ''' Compile set of regular expressions to parse ISO dates. The expressions will be created only if they are not already in REGEX_CACHE. It is necessary to fix the number of year digits, else it is not possible to automatically distinguish between various ISO date formats. ISO 8601 allows more than 4 digit years, on prior agreement, but then a +/- sign is required (expanded format). To support +/- sign for 4 digit years, the expanded parameter needs to be set to True. ''' if yeardigits != 4: expanded = True if (yeardigits, expanded) not in DATE_REGEX_CACHE: cache_entry = [] # ISO 8601 expanded DATE formats allow an arbitrary number of year # digits with a leading +/- sign. if expanded: sign = 1 else: sign = 0 # 1. complete dates: # YYYY-MM-DD or +- YYYYYY-MM-DD... extended date format cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" r"-(?P[0-9]{2})-(?P[0-9]{2})" % (sign, yeardigits))) # YYYYMMDD or +- YYYYYYMMDD... basic date format cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" r"(?P[0-9]{2})(?P[0-9]{2})" % (sign, yeardigits))) # 2. complete week dates: # YYYY-Www-D or +-YYYYYY-Www-D ... extended week date cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" r"-W(?P[0-9]{2})-(?P[0-9]{1})" % (sign, yeardigits))) # YYYYWwwD or +-YYYYYYWwwD ... basic week date cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})W" r"(?P[0-9]{2})(?P[0-9]{1})" % (sign, yeardigits))) # 3. ordinal dates: # YYYY-DDD or +-YYYYYY-DDD ... extended format cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" r"-(?P[0-9]{3})" % (sign, yeardigits))) # YYYYDDD or +-YYYYYYDDD ... basic format cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" r"(?P[0-9]{3})" % (sign, yeardigits))) # 4. week dates: # YYYY-Www or +-YYYYYY-Www ... extended reduced accuracy week date cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" r"-W(?P[0-9]{2})" % (sign, yeardigits))) # YYYYWww or +-YYYYYYWww ... basic reduced accuracy week date cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})W" r"(?P[0-9]{2})" % (sign, yeardigits))) # 5. month dates: # YYY-MM or +-YYYYYY-MM ... reduced accuracy specific month cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" r"-(?P[0-9]{2})" % (sign, yeardigits))) # YYYMM or +-YYYYYYMM ... basic incomplete month date format cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" r"(?P[0-9]{2})" % (sign, yeardigits))) # 6. year dates: # YYYY or +-YYYYYY ... reduced accuracy specific year cache_entry.append(re.compile(r"(?P[+-]){%d}(?P[0-9]{%d})" % (sign, yeardigits))) # 7. century dates: # YY or +-YYYY ... reduced accuracy specific century cache_entry.append(re.compile(r"(?P[+-]){%d}" r"(?P[0-9]{%d})" % (sign, yeardigits - 2))) DATE_REGEX_CACHE[(yeardigits, expanded)] = cache_entry return DATE_REGEX_CACHE[(yeardigits, expanded)] def parse_date( datestring, yeardigits=4, expanded=False, defaultmonth=1, defaultday=1): ''' Parse an ISO 8601 date string into a datetime.date object. As the datetime.date implementation is limited to dates starting from 0001-01-01, negative dates (BC) and year 0 can not be parsed by this method. For incomplete dates, this method chooses the first day for it. For instance if only a century is given, this method returns the 1st of January in year 1 of this century. supported formats: (expanded formats are shown with 6 digits for year) YYYYMMDD +-YYYYYYMMDD basic complete date YYYY-MM-DD +-YYYYYY-MM-DD extended complete date YYYYWwwD +-YYYYYYWwwD basic complete week date YYYY-Www-D +-YYYYYY-Www-D extended complete week date YYYYDDD +-YYYYYYDDD basic ordinal date YYYY-DDD +-YYYYYY-DDD extended ordinal date YYYYWww +-YYYYYYWww basic incomplete week date YYYY-Www +-YYYYYY-Www extended incomplete week date YYYMM +-YYYYYYMM basic incomplete month date YYY-MM +-YYYYYY-MM incomplete month date YYYY +-YYYYYY incomplete year date YY +-YYYY incomplete century date @param datestring: the ISO date string to parse @param yeardigits: how many digits are used to represent a year @param expanded: if True then +/- signs are allowed. This parameter is forced to True, if yeardigits != 4 @return: a datetime.date instance represented by datestring @raise ISO8601Error: if this function can not parse the datestring @raise ValueError: if datestring can not be represented by datetime.date ''' if yeardigits != 4: expanded = True isodates = build_date_regexps(yeardigits, expanded) for pattern in isodates: match = pattern.match(datestring) if match: groups = match.groupdict() # sign, century, year, month, week, day, # FIXME: negative dates not possible with python standard types sign = (groups['sign'] == '-' and -1) or 1 if 'century' in groups: return date( sign * (int(groups['century']) * 100 + 1), defaultmonth, defaultday) if 'month' not in groups: # weekdate or ordinal date ret = date(sign * int(groups['year']), 1, 1) if 'week' in groups: isotuple = ret.isocalendar() if 'day' in groups: days = int(groups['day'] or 1) else: days = 1 # if first week in year, do weeks-1 return ret + timedelta(weeks=int(groups['week']) - (((isotuple[1] == 1) and 1) or 0), days=-isotuple[2] + days) elif 'day' in groups: # ordinal date return ret + timedelta(days=int(groups['day']) - 1) else: # year date return ret.replace(month=defaultmonth, day=defaultday) # year-, month-, or complete date if 'day' not in groups or groups['day'] is None: day = defaultday else: day = int(groups['day']) return date(sign * int(groups['year']), int(groups['month']) or defaultmonth, day) raise ISO8601Error('Unrecognised ISO 8601 date format: %r' % datestring) def date_isoformat(tdate, format=DATE_EXT_COMPLETE, yeardigits=4): ''' Format date strings. This method is just a wrapper around isodate.isostrf.strftime and uses Date-Extended-Complete as default format. ''' return strftime(tdate, format, yeardigits) isodate-0.6.0/src/isodate/isodatetime.py0000644€YMd0€T]®j0000000561312426740002020106 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' This module defines a method to parse an ISO 8601:2004 date time string. For this job it uses the parse_date and parse_time methods defined in date and time module. ''' from datetime import datetime from isodate.isostrf import strftime from isodate.isostrf import DATE_EXT_COMPLETE, TIME_EXT_COMPLETE, TZ_EXT from isodate.isodates import parse_date from isodate.isoerror import ISO8601Error from isodate.isotime import parse_time def parse_datetime(datetimestring): ''' Parses ISO 8601 date-times into datetime.datetime objects. This function uses parse_date and parse_time to do the job, so it allows more combinations of date and time representations, than the actual ISO 8601:2004 standard allows. ''' try: datestring, timestring = datetimestring.split('T') except ValueError: raise ISO8601Error("ISO 8601 time designator 'T' missing. Unable to" " parse datetime string %r" % datetimestring) tmpdate = parse_date(datestring) tmptime = parse_time(timestring) return datetime.combine(tmpdate, tmptime) def datetime_isoformat(tdt, format=DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + TZ_EXT): ''' Format datetime strings. This method is just a wrapper around isodate.isostrf.strftime and uses Extended-Complete as default format. ''' return strftime(tdt, format) isodate-0.6.0/src/isodate/isoduration.py0000644€YMd0€T]®j0000001506113127143454020144 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' This module provides an ISO 8601:2004 duration parser. It also provides a wrapper to strftime. This wrapper makes it easier to format timedelta or Duration instances as ISO conforming strings. ''' from datetime import timedelta from decimal import Decimal import re from six import string_types from isodate.duration import Duration from isodate.isoerror import ISO8601Error from isodate.isodatetime import parse_datetime from isodate.isostrf import strftime, D_DEFAULT ISO8601_PERIOD_REGEX = re.compile( r"^(?P[+-])?" r"P(?!\b)" r"(?P[0-9]+([,.][0-9]+)?Y)?" r"(?P[0-9]+([,.][0-9]+)?M)?" r"(?P[0-9]+([,.][0-9]+)?W)?" r"(?P[0-9]+([,.][0-9]+)?D)?" r"((?PT)(?P[0-9]+([,.][0-9]+)?H)?" r"(?P[0-9]+([,.][0-9]+)?M)?" r"(?P[0-9]+([,.][0-9]+)?S)?)?$") # regular expression to parse ISO duartion strings. def parse_duration(datestring): """ Parses an ISO 8601 durations into datetime.timedelta or Duration objects. If the ISO date string does not contain years or months, a timedelta instance is returned, else a Duration instance is returned. The following duration formats are supported: -PnnW duration in weeks -PnnYnnMnnDTnnHnnMnnS complete duration specification -PYYYYMMDDThhmmss basic alternative complete date format -PYYYY-MM-DDThh:mm:ss extended alternative complete date format -PYYYYDDDThhmmss basic alternative ordinal date format -PYYYY-DDDThh:mm:ss extended alternative ordinal date format The '-' is optional. Limitations: ISO standard defines some restrictions about where to use fractional numbers and which component and format combinations are allowed. This parser implementation ignores all those restrictions and returns something when it is able to find all necessary components. In detail: it does not check, whether only the last component has fractions. it allows weeks specified with all other combinations The alternative format does not support durations with years, months or days set to 0. """ if not isinstance(datestring, string_types): raise TypeError("Expecting a string %r" % datestring) match = ISO8601_PERIOD_REGEX.match(datestring) if not match: # try alternative format: if datestring.startswith("P"): durdt = parse_datetime(datestring[1:]) if durdt.year != 0 or durdt.month != 0: # create Duration ret = Duration(days=durdt.day, seconds=durdt.second, microseconds=durdt.microsecond, minutes=durdt.minute, hours=durdt.hour, months=durdt.month, years=durdt.year) else: # FIXME: currently not possible in alternative format # create timedelta ret = timedelta(days=durdt.day, seconds=durdt.second, microseconds=durdt.microsecond, minutes=durdt.minute, hours=durdt.hour) return ret raise ISO8601Error("Unable to parse duration string %r" % datestring) groups = match.groupdict() for key, val in groups.items(): if key not in ('separator', 'sign'): if val is None: groups[key] = "0n" # print groups[key] if key in ('years', 'months'): groups[key] = Decimal(groups[key][:-1].replace(',', '.')) else: # these values are passed into a timedelta object, # which works with floats. groups[key] = float(groups[key][:-1].replace(',', '.')) if groups["years"] == 0 and groups["months"] == 0: ret = timedelta(days=groups["days"], hours=groups["hours"], minutes=groups["minutes"], seconds=groups["seconds"], weeks=groups["weeks"]) if groups["sign"] == '-': ret = timedelta(0) - ret else: ret = Duration(years=groups["years"], months=groups["months"], days=groups["days"], hours=groups["hours"], minutes=groups["minutes"], seconds=groups["seconds"], weeks=groups["weeks"]) if groups["sign"] == '-': ret = Duration(0) - ret return ret def duration_isoformat(tduration, format=D_DEFAULT): ''' Format duration strings. This method is just a wrapper around isodate.isostrf.strftime and uses P%P (D_DEFAULT) as default format. ''' # TODO: implement better decision for negative Durations. # should be done in Duration class in consistent way with timedelta. if (((isinstance(tduration, Duration) and (tduration.years < 0 or tduration.months < 0 or tduration.tdelta < timedelta(0))) or (isinstance(tduration, timedelta) and (tduration < timedelta(0))))): ret = '-' else: ret = '' ret += strftime(tduration, format) return ret isodate-0.6.0/src/isodate/isoerror.py0000644€YMd0€T]®j0000000326212303502354017441 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' This module defines all exception classes in the whole package. ''' class ISO8601Error(ValueError): '''Raised when the given ISO string can not be parsed.''' isodate-0.6.0/src/isodate/isostrf.py0000644€YMd0€T]®j0000002204413127314601017266 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## """ This module provides an alternative strftime method. The strftime method in this module allows only a subset of Python's strftime format codes, plus a few additional. It supports the full range of date values possible with standard Python date/time objects. Furthermore there are several pr-defined format strings in this module to make ease producing of ISO 8601 conforming strings. """ import re from datetime import date, timedelta from isodate.duration import Duration from isodate.isotzinfo import tz_isoformat # Date specific format strings DATE_BAS_COMPLETE = '%Y%m%d' DATE_EXT_COMPLETE = '%Y-%m-%d' DATE_BAS_WEEK_COMPLETE = '%YW%W%w' DATE_EXT_WEEK_COMPLETE = '%Y-W%W-%w' DATE_BAS_ORD_COMPLETE = '%Y%j' DATE_EXT_ORD_COMPLETE = '%Y-%j' DATE_BAS_WEEK = '%YW%W' DATE_EXT_WEEK = '%Y-W%W' DATE_BAS_MONTH = '%Y%m' DATE_EXT_MONTH = '%Y-%m' DATE_YEAR = '%Y' DATE_CENTURY = '%C' # Time specific format strings TIME_BAS_COMPLETE = '%H%M%S' TIME_EXT_COMPLETE = '%H:%M:%S' TIME_BAS_MINUTE = '%H%M' TIME_EXT_MINUTE = '%H:%M' TIME_HOUR = '%H' # Time zone formats TZ_BAS = '%z' TZ_EXT = '%Z' TZ_HOUR = '%h' # DateTime formats DT_EXT_COMPLETE = DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + TZ_EXT DT_BAS_COMPLETE = DATE_BAS_COMPLETE + 'T' + TIME_BAS_COMPLETE + TZ_BAS DT_EXT_ORD_COMPLETE = DATE_EXT_ORD_COMPLETE + 'T' + TIME_EXT_COMPLETE + TZ_EXT DT_BAS_ORD_COMPLETE = DATE_BAS_ORD_COMPLETE + 'T' + TIME_BAS_COMPLETE + TZ_BAS DT_EXT_WEEK_COMPLETE = (DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_COMPLETE + TZ_EXT) DT_BAS_WEEK_COMPLETE = (DATE_BAS_WEEK_COMPLETE + 'T' + TIME_BAS_COMPLETE + TZ_BAS) # Duration formts D_DEFAULT = 'P%P' D_WEEK = 'P%p' D_ALT_EXT = 'P' + DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE D_ALT_BAS = 'P' + DATE_BAS_COMPLETE + 'T' + TIME_BAS_COMPLETE D_ALT_EXT_ORD = 'P' + DATE_EXT_ORD_COMPLETE + 'T' + TIME_EXT_COMPLETE D_ALT_BAS_ORD = 'P' + DATE_BAS_ORD_COMPLETE + 'T' + TIME_BAS_COMPLETE STRF_DT_MAP = {'%d': lambda tdt, yds: '%02d' % tdt.day, '%f': lambda tdt, yds: '%06d' % tdt.microsecond, '%H': lambda tdt, yds: '%02d' % tdt.hour, '%j': lambda tdt, yds: '%03d' % (tdt.toordinal() - date(tdt.year, 1, 1).toordinal() + 1), '%m': lambda tdt, yds: '%02d' % tdt.month, '%M': lambda tdt, yds: '%02d' % tdt.minute, '%S': lambda tdt, yds: '%02d' % tdt.second, '%w': lambda tdt, yds: '%1d' % tdt.isoweekday(), '%W': lambda tdt, yds: '%02d' % tdt.isocalendar()[1], '%Y': lambda tdt, yds: (((yds != 4) and '+') or '') + (('%%0%dd' % yds) % tdt.year), '%C': lambda tdt, yds: (((yds != 4) and '+') or '') + (('%%0%dd' % (yds - 2)) % (tdt.year / 100)), '%h': lambda tdt, yds: tz_isoformat(tdt, '%h'), '%Z': lambda tdt, yds: tz_isoformat(tdt, '%Z'), '%z': lambda tdt, yds: tz_isoformat(tdt, '%z'), '%%': lambda tdt, yds: '%'} STRF_D_MAP = {'%d': lambda tdt, yds: '%02d' % tdt.days, '%f': lambda tdt, yds: '%06d' % tdt.microseconds, '%H': lambda tdt, yds: '%02d' % (tdt.seconds / 60 / 60), '%m': lambda tdt, yds: '%02d' % tdt.months, '%M': lambda tdt, yds: '%02d' % ((tdt.seconds / 60) % 60), '%S': lambda tdt, yds: '%02d' % (tdt.seconds % 60), '%W': lambda tdt, yds: '%02d' % (abs(tdt.days / 7)), '%Y': lambda tdt, yds: (((yds != 4) and '+') or '') + (('%%0%dd' % yds) % tdt.years), '%C': lambda tdt, yds: (((yds != 4) and '+') or '') + (('%%0%dd' % (yds - 2)) % (tdt.years / 100)), '%%': lambda tdt, yds: '%'} def _strfduration(tdt, format, yeardigits=4): ''' this is the work method for timedelta and Duration instances. see strftime for more details. ''' def repl(match): ''' lookup format command and return corresponding replacement. ''' if match.group(0) in STRF_D_MAP: return STRF_D_MAP[match.group(0)](tdt, yeardigits) elif match.group(0) == '%P': ret = [] if isinstance(tdt, Duration): if tdt.years: ret.append('%sY' % abs(tdt.years)) if tdt.months: ret.append('%sM' % abs(tdt.months)) usecs = abs((tdt.days * 24 * 60 * 60 + tdt.seconds) * 1000000 + tdt.microseconds) seconds, usecs = divmod(usecs, 1000000) minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) days, hours = divmod(hours, 24) if days: ret.append('%sD' % days) if hours or minutes or seconds or usecs: ret.append('T') if hours: ret.append('%sH' % hours) if minutes: ret.append('%sM' % minutes) if seconds or usecs: if usecs: ret.append(("%d.%06d" % (seconds, usecs)).rstrip('0')) else: ret.append("%d" % seconds) ret.append('S') # at least one component has to be there. return ret and ''.join(ret) or '0D' elif match.group(0) == '%p': return str(abs(tdt.days // 7)) + 'W' return match.group(0) return re.sub('%d|%f|%H|%m|%M|%S|%W|%Y|%C|%%|%P|%p', repl, format) def _strfdt(tdt, format, yeardigits=4): ''' this is the work method for time and date instances. see strftime for more details. ''' def repl(match): ''' lookup format command and return corresponding replacement. ''' if match.group(0) in STRF_DT_MAP: return STRF_DT_MAP[match.group(0)](tdt, yeardigits) return match.group(0) return re.sub('%d|%f|%H|%j|%m|%M|%S|%w|%W|%Y|%C|%z|%Z|%h|%%', repl, format) def strftime(tdt, format, yeardigits=4): '''Directive Meaning Notes %d Day of the month as a decimal number [01,31]. %f Microsecond as a decimal number [0,999999], zero-padded on the left (1) %H Hour (24-hour clock) as a decimal number [00,23]. %j Day of the year as a decimal number [001,366]. %m Month as a decimal number [01,12]. %M Minute as a decimal number [00,59]. %S Second as a decimal number [00,61]. (3) %w Weekday as a decimal number [0(Monday),6]. %W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (4) %Y Year with century as a decimal number. [0000,9999] %C Century as a decimal number. [00,99] %z UTC offset in the form +HHMM or -HHMM (empty string if the object is naive). (5) %Z Time zone name (empty string if the object is naive). %P ISO8601 duration format. %p ISO8601 duration format in weeks. %% A literal '%' character. ''' if isinstance(tdt, (timedelta, Duration)): return _strfduration(tdt, format, yeardigits) return _strfdt(tdt, format, yeardigits) isodate-0.6.0/src/isodate/isotime.py0000644€YMd0€T]®j0000001603613127145235017257 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' This modules provides a method to parse an ISO 8601:2004 time string to a Python datetime.time instance. It supports all basic and extended formats including time zone specifications as described in the ISO standard. ''' import re from decimal import Decimal from datetime import time from isodate.isostrf import strftime, TIME_EXT_COMPLETE, TZ_EXT from isodate.isoerror import ISO8601Error from isodate.isotzinfo import TZ_REGEX, build_tzinfo TIME_REGEX_CACHE = [] # used to cache regular expressions to parse ISO time strings. def build_time_regexps(): ''' Build regular expressions to parse ISO time string. The regular expressions are compiled and stored in TIME_REGEX_CACHE for later reuse. ''' if not TIME_REGEX_CACHE: # ISO 8601 time representations allow decimal fractions on least # significant time component. Command and Full Stop are both valid # fraction separators. # The letter 'T' is allowed as time designator in front of a time # expression. # Immediately after a time expression, a time zone definition is # allowed. # a TZ may be missing (local time), be a 'Z' for UTC or a string of # +-hh:mm where the ':mm' part can be skipped. # TZ information patterns: # '' # Z # +-hh:mm # +-hhmm # +-hh => # isotzinfo.TZ_REGEX # 1. complete time: # hh:mm:ss.ss ... extended format TIME_REGEX_CACHE.append(re.compile(r"T?(?P[0-9]{2}):" r"(?P[0-9]{2}):" r"(?P[0-9]{2}" r"([,.][0-9]+)?)" + TZ_REGEX)) # hhmmss.ss ... basic format TIME_REGEX_CACHE.append(re.compile(r"T?(?P[0-9]{2})" r"(?P[0-9]{2})" r"(?P[0-9]{2}" r"([,.][0-9]+)?)" + TZ_REGEX)) # 2. reduced accuracy: # hh:mm.mm ... extended format TIME_REGEX_CACHE.append(re.compile(r"T?(?P[0-9]{2}):" r"(?P[0-9]{2}" r"([,.][0-9]+)?)" + TZ_REGEX)) # hhmm.mm ... basic format TIME_REGEX_CACHE.append(re.compile(r"T?(?P[0-9]{2})" r"(?P[0-9]{2}" r"([,.][0-9]+)?)" + TZ_REGEX)) # hh.hh ... basic format TIME_REGEX_CACHE.append(re.compile(r"T?(?P[0-9]{2}" r"([,.][0-9]+)?)" + TZ_REGEX)) return TIME_REGEX_CACHE def parse_time(timestring): ''' Parses ISO 8601 times into datetime.time objects. Following ISO 8601 formats are supported: (as decimal separator a ',' or a '.' is allowed) hhmmss.ssTZD basic complete time hh:mm:ss.ssTZD extended compelte time hhmm.mmTZD basic reduced accuracy time hh:mm.mmTZD extended reduced accuracy time hh.hhTZD basic reduced accuracy time TZD is the time zone designator which can be in the following format: no designator indicates local time zone Z UTC +-hhmm basic hours and minutes +-hh:mm extended hours and minutes +-hh hours ''' isotimes = build_time_regexps() for pattern in isotimes: match = pattern.match(timestring) if match: groups = match.groupdict() for key, value in groups.items(): if value is not None: groups[key] = value.replace(',', '.') tzinfo = build_tzinfo(groups['tzname'], groups['tzsign'], int(groups['tzhour'] or 0), int(groups['tzmin'] or 0)) if 'second' in groups: # round to microseconds if fractional seconds are more precise second = Decimal(groups['second']).quantize(Decimal('.000001')) microsecond = (second - int(second)) * int(1e6) # int(...) ... no rounding # to_integral() ... rounding return time(int(groups['hour']), int(groups['minute']), int(second), int(microsecond.to_integral()), tzinfo) if 'minute' in groups: minute = Decimal(groups['minute']) second = (minute - int(minute)) * 60 microsecond = (second - int(second)) * int(1e6) return time(int(groups['hour']), int(minute), int(second), int(microsecond.to_integral()), tzinfo) else: microsecond, second, minute = 0, 0, 0 hour = Decimal(groups['hour']) minute = (hour - int(hour)) * 60 second = (minute - int(minute)) * 60 microsecond = (second - int(second)) * int(1e6) return time(int(hour), int(minute), int(second), int(microsecond.to_integral()), tzinfo) raise ISO8601Error('Unrecognised ISO 8601 time format: %r' % timestring) def time_isoformat(ttime, format=TIME_EXT_COMPLETE + TZ_EXT): ''' Format time strings. This method is just a wrapper around isodate.isostrf.strftime and uses Time-Extended-Complete with extended time zone as default format. ''' return strftime(ttime, format) isodate-0.6.0/src/isodate/isotzinfo.py0000644€YMd0€T]®j0000001017113170040020017604 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' This module provides an ISO 8601:2004 time zone info parser. It offers a function to parse the time zone offset as specified by ISO 8601. ''' import re from isodate.isoerror import ISO8601Error from isodate.tzinfo import UTC, FixedOffset, ZERO TZ_REGEX = r"(?P(Z|(?P[+-])"\ r"(?P[0-9]{2})(:?(?P[0-9]{2}))?)?)" TZ_RE = re.compile(TZ_REGEX) def build_tzinfo(tzname, tzsign='+', tzhour=0, tzmin=0): ''' create a tzinfo instance according to given parameters. tzname: 'Z' ... return UTC '' | None ... return None other ... return FixedOffset ''' if tzname is None or tzname == '': return None if tzname == 'Z': return UTC tzsign = ((tzsign == '-') and -1) or 1 return FixedOffset(tzsign * tzhour, tzsign * tzmin, tzname) def parse_tzinfo(tzstring): ''' Parses ISO 8601 time zone designators to tzinfo objecs. A time zone designator can be in the following format: no designator indicates local time zone Z UTC +-hhmm basic hours and minutes +-hh:mm extended hours and minutes +-hh hours ''' match = TZ_RE.match(tzstring) if match: groups = match.groupdict() return build_tzinfo(groups['tzname'], groups['tzsign'], int(groups['tzhour'] or 0), int(groups['tzmin'] or 0)) raise ISO8601Error('%s not a valid time zone info' % tzstring) def tz_isoformat(dt, format='%Z'): ''' return time zone offset ISO 8601 formatted. The various ISO formats can be chosen with the format parameter. if tzinfo is None returns '' if tzinfo is UTC returns 'Z' else the offset is rendered to the given format. format: %h ... +-HH %z ... +-HHMM %Z ... +-HH:MM ''' tzinfo = dt.tzinfo if (tzinfo is None) or (tzinfo.utcoffset(dt) is None): return '' if tzinfo.utcoffset(dt) == ZERO and tzinfo.dst(dt) == ZERO: return 'Z' tdelta = tzinfo.utcoffset(dt) seconds = tdelta.days * 24 * 60 * 60 + tdelta.seconds sign = ((seconds < 0) and '-') or '+' seconds = abs(seconds) minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) if hours > 99: raise OverflowError('can not handle differences > 99 hours') if format == '%Z': return '%s%02d:%02d' % (sign, hours, minutes) elif format == '%z': return '%s%02d%02d' % (sign, hours, minutes) elif format == '%h': return '%s%02d' % (sign, hours) raise ValueError('unknown format string "%s"' % format) isodate-0.6.0/src/isodate/tests/0000755€YMd0€T]®j0000000000013170103163016360 5ustar s261679400000000000000isodate-0.6.0/src/isodate/tests/__init__.py0000644€YMd0€T]®j0000000421013127140536020475 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' Collect all test suites into one TestSuite instance. ''' import unittest from isodate.tests import (test_date, test_time, test_datetime, test_duration, test_strf, test_pickle) def test_suite(): ''' Return a new TestSuite instance consisting of all available TestSuites. ''' return unittest.TestSuite([ test_date.test_suite(), test_time.test_suite(), test_datetime.test_suite(), test_duration.test_suite(), test_strf.test_suite(), test_pickle.test_suite(), ]) if __name__ == '__main__': unittest.main(defaultTest='test_suite') isodate-0.6.0/src/isodate/tests/test_date.py0000644€YMd0€T]®j0000001371213127314601020715 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' Test cases for the isodate module. ''' import unittest from datetime import date from isodate import parse_date, ISO8601Error, date_isoformat from isodate import DATE_CENTURY, DATE_YEAR from isodate import DATE_BAS_MONTH, DATE_EXT_MONTH from isodate import DATE_EXT_COMPLETE, DATE_BAS_COMPLETE from isodate import DATE_BAS_ORD_COMPLETE, DATE_EXT_ORD_COMPLETE from isodate import DATE_BAS_WEEK, DATE_BAS_WEEK_COMPLETE from isodate import DATE_EXT_WEEK, DATE_EXT_WEEK_COMPLETE # the following list contains tuples of ISO date strings and the expected # result from the parse_date method. A result of None means an ISO8601Error # is expected. The test cases are grouped into dates with 4 digit years # and 6 digit years. TEST_CASES = {4: [('19', date(1901, 1, 1), DATE_CENTURY), ('1985', date(1985, 1, 1), DATE_YEAR), ('1985-04', date(1985, 4, 1), DATE_EXT_MONTH), ('198504', date(1985, 4, 1), DATE_BAS_MONTH), ('1985-04-12', date(1985, 4, 12), DATE_EXT_COMPLETE), ('19850412', date(1985, 4, 12), DATE_BAS_COMPLETE), ('1985102', date(1985, 4, 12), DATE_BAS_ORD_COMPLETE), ('1985-102', date(1985, 4, 12), DATE_EXT_ORD_COMPLETE), ('1985W155', date(1985, 4, 12), DATE_BAS_WEEK_COMPLETE), ('1985-W15-5', date(1985, 4, 12), DATE_EXT_WEEK_COMPLETE), ('1985W15', date(1985, 4, 8), DATE_BAS_WEEK), ('1985-W15', date(1985, 4, 8), DATE_EXT_WEEK), ('1989-W15', date(1989, 4, 10), DATE_EXT_WEEK), ('1989-W15-5', date(1989, 4, 14), DATE_EXT_WEEK_COMPLETE), ('1-W1-1', None, DATE_BAS_WEEK_COMPLETE)], 6: [('+0019', date(1901, 1, 1), DATE_CENTURY), ('+001985', date(1985, 1, 1), DATE_YEAR), ('+001985-04', date(1985, 4, 1), DATE_EXT_MONTH), ('+001985-04-12', date(1985, 4, 12), DATE_EXT_COMPLETE), ('+0019850412', date(1985, 4, 12), DATE_BAS_COMPLETE), ('+001985102', date(1985, 4, 12), DATE_BAS_ORD_COMPLETE), ('+001985-102', date(1985, 4, 12), DATE_EXT_ORD_COMPLETE), ('+001985W155', date(1985, 4, 12), DATE_BAS_WEEK_COMPLETE), ('+001985-W15-5', date(1985, 4, 12), DATE_EXT_WEEK_COMPLETE), ('+001985W15', date(1985, 4, 8), DATE_BAS_WEEK), ('+001985-W15', date(1985, 4, 8), DATE_EXT_WEEK)]} def create_testcase(yeardigits, datestring, expectation, format): ''' Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the TEST_CASES list, so that a failed test won't stop other tests. ''' class TestDate(unittest.TestCase): ''' A test case template to parse an ISO date string into a date object. ''' def test_parse(self): ''' Parse an ISO date string and compare it to the expected value. ''' if expectation is None: self.assertRaises(ISO8601Error, parse_date, datestring, yeardigits) else: result = parse_date(datestring, yeardigits) self.assertEqual(result, expectation) def test_format(self): ''' Take date object and create ISO string from it. This is the reverse test to test_parse. ''' if expectation is None: self.assertRaises(AttributeError, date_isoformat, expectation, format, yeardigits) else: self.assertEqual(date_isoformat(expectation, format, yeardigits), datestring) return unittest.TestLoader().loadTestsFromTestCase(TestDate) def test_suite(): ''' Construct a TestSuite instance for all test cases. ''' suite = unittest.TestSuite() for yeardigits, tests in TEST_CASES.items(): for datestring, expectation, format in tests: suite.addTest(create_testcase(yeardigits, datestring, expectation, format)) return suite # load_tests Protocol def load_tests(loader, tests, pattern): return test_suite() if __name__ == '__main__': unittest.main(defaultTest='test_suite') isodate-0.6.0/src/isodate/tests/test_datetime.py0000644€YMd0€T]®j0000001633313170040020021563 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' Test cases for the isodatetime module. ''' import unittest from datetime import datetime from isodate import parse_datetime, UTC, FixedOffset, datetime_isoformat from isodate import ISO8601Error from isodate import DATE_BAS_COMPLETE, TIME_BAS_MINUTE, TIME_BAS_COMPLETE from isodate import DATE_EXT_COMPLETE, TIME_EXT_MINUTE, TIME_EXT_COMPLETE from isodate import TZ_BAS, TZ_EXT, TZ_HOUR from isodate import DATE_BAS_ORD_COMPLETE, DATE_EXT_ORD_COMPLETE from isodate import DATE_BAS_WEEK_COMPLETE, DATE_EXT_WEEK_COMPLETE # the following list contains tuples of ISO datetime strings and the expected # result from the parse_datetime method. A result of None means an ISO8601Error # is expected. TEST_CASES = [('19850412T1015', datetime(1985, 4, 12, 10, 15), DATE_BAS_COMPLETE + 'T' + TIME_BAS_MINUTE, '19850412T1015'), ('1985-04-12T10:15', datetime(1985, 4, 12, 10, 15), DATE_EXT_COMPLETE + 'T' + TIME_EXT_MINUTE, '1985-04-12T10:15'), ('1985102T1015Z', datetime(1985, 4, 12, 10, 15, tzinfo=UTC), DATE_BAS_ORD_COMPLETE + 'T' + TIME_BAS_MINUTE + TZ_BAS, '1985102T1015Z'), ('1985-102T10:15Z', datetime(1985, 4, 12, 10, 15, tzinfo=UTC), DATE_EXT_ORD_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_EXT, '1985-102T10:15Z'), ('1985W155T1015+0400', datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(4, 0, '+0400')), DATE_BAS_WEEK_COMPLETE + 'T' + TIME_BAS_MINUTE + TZ_BAS, '1985W155T1015+0400'), ('1985-W15-5T10:15+04', datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(4, 0, '+0400'),), DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_HOUR, '1985-W15-5T10:15+04'), ('1985-W15-5T10:15-0430', datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(-4, -30, '-0430'),), DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_BAS, '1985-W15-5T10:15-0430'), ('1985-W15-5T10:15+04:45', datetime(1985, 4, 12, 10, 15, tzinfo=FixedOffset(4, 45, '+04:45'),), DATE_EXT_WEEK_COMPLETE + 'T' + TIME_EXT_MINUTE + TZ_EXT, '1985-W15-5T10:15+04:45'), ('20110410T101225.123000Z', datetime(2011, 4, 10, 10, 12, 25, 123000, tzinfo=UTC), DATE_BAS_COMPLETE + 'T' + TIME_BAS_COMPLETE + ".%f" + TZ_BAS, '20110410T101225.123000Z'), ('2012-10-12T08:29:46.069178Z', datetime(2012, 10, 12, 8, 29, 46, 69178, tzinfo=UTC), DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS, '2012-10-12T08:29:46.069178Z'), ('2012-10-12T08:29:46.691780Z', datetime(2012, 10, 12, 8, 29, 46, 691780, tzinfo=UTC), DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS, '2012-10-12T08:29:46.691780Z'), ('2012-10-30T08:55:22.1234567Z', datetime(2012, 10, 30, 8, 55, 22, 123457, tzinfo=UTC), DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS, '2012-10-30T08:55:22.123457Z'), ('2012-10-30T08:55:22.1234561Z', datetime(2012, 10, 30, 8, 55, 22, 123456, tzinfo=UTC), DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS, '2012-10-30T08:55:22.123456Z'), ('2014-08-18 14:55:22.123456Z', None, DATE_EXT_COMPLETE + 'T' + TIME_EXT_COMPLETE + '.%f' + TZ_BAS, '2014-08-18T14:55:22.123456Z'), ] def create_testcase(datetimestring, expectation, format, output): """ Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the TEST_CASES list, so that a failed test won't stop other tests. """ class TestDateTime(unittest.TestCase): ''' A test case template to parse an ISO datetime string into a datetime object. ''' def test_parse(self): ''' Parse an ISO datetime string and compare it to the expected value. ''' if expectation is None: self.assertRaises(ISO8601Error, parse_datetime, datetimestring) else: self.assertEqual(parse_datetime(datetimestring), expectation) def test_format(self): ''' Take datetime object and create ISO string from it. This is the reverse test to test_parse. ''' if expectation is None: self.assertRaises(AttributeError, datetime_isoformat, expectation, format) else: self.assertEqual(datetime_isoformat(expectation, format), output) return unittest.TestLoader().loadTestsFromTestCase(TestDateTime) def test_suite(): ''' Construct a TestSuite instance for all test cases. ''' suite = unittest.TestSuite() for datetimestring, expectation, format, output in TEST_CASES: suite.addTest(create_testcase(datetimestring, expectation, format, output)) return suite # load_tests Protocol def load_tests(loader, tests, pattern): return test_suite() if __name__ == '__main__': unittest.main(defaultTest='test_suite') isodate-0.6.0/src/isodate/tests/test_duration.py0000644€YMd0€T]®j0000005704013127263175021640 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' Test cases for the isoduration module. ''' import unittest import operator from datetime import timedelta, date, datetime from isodate import Duration, parse_duration, ISO8601Error from isodate import D_DEFAULT, D_WEEK, D_ALT_EXT, duration_isoformat # the following list contains tuples of ISO duration strings and the expected # result from the parse_duration method. A result of None means an ISO8601Error # is expected. PARSE_TEST_CASES = {'P18Y9M4DT11H9M8S': (Duration(4, 8, 0, 0, 9, 11, 0, 9, 18), D_DEFAULT, None), 'P2W': (timedelta(weeks=2), D_WEEK, None), 'P3Y6M4DT12H30M5S': (Duration(4, 5, 0, 0, 30, 12, 0, 6, 3), D_DEFAULT, None), 'P23DT23H': (timedelta(hours=23, days=23), D_DEFAULT, None), 'P4Y': (Duration(years=4), D_DEFAULT, None), 'P1M': (Duration(months=1), D_DEFAULT, None), 'PT1M': (timedelta(minutes=1), D_DEFAULT, None), 'P0.5Y': (Duration(years=0.5), D_DEFAULT, None), 'PT36H': (timedelta(hours=36), D_DEFAULT, 'P1DT12H'), 'P1DT12H': (timedelta(days=1, hours=12), D_DEFAULT, None), '+P11D': (timedelta(days=11), D_DEFAULT, 'P11D'), '-P2W': (timedelta(weeks=-2), D_WEEK, None), '-P2.2W': (timedelta(weeks=-2.2), D_DEFAULT, '-P15DT9H36M'), 'P1DT2H3M4S': (timedelta(days=1, hours=2, minutes=3, seconds=4), D_DEFAULT, None), 'P1DT2H3M': (timedelta(days=1, hours=2, minutes=3), D_DEFAULT, None), 'P1DT2H': (timedelta(days=1, hours=2), D_DEFAULT, None), 'PT2H': (timedelta(hours=2), D_DEFAULT, None), 'PT2.3H': (timedelta(hours=2.3), D_DEFAULT, 'PT2H18M'), 'PT2H3M4S': (timedelta(hours=2, minutes=3, seconds=4), D_DEFAULT, None), 'PT3M4S': (timedelta(minutes=3, seconds=4), D_DEFAULT, None), 'PT22S': (timedelta(seconds=22), D_DEFAULT, None), 'PT22.22S': (timedelta(seconds=22.22), 'PT%S.%fS', 'PT22.220000S'), '-P2Y': (Duration(years=-2), D_DEFAULT, None), '-P3Y6M4DT12H30M5S': (Duration(-4, -5, 0, 0, -30, -12, 0, -6, -3), D_DEFAULT, None), '-P1DT2H3M4S': (timedelta(days=-1, hours=-2, minutes=-3, seconds=-4), D_DEFAULT, None), # alternative format 'P0018-09-04T11:09:08': (Duration(4, 8, 0, 0, 9, 11, 0, 9, 18), D_ALT_EXT, None), # 'PT000022.22': timedelta(seconds=22.22), } # d1 d2 '+', '-', '>' # A list of test cases to test addition and subtraction between datetime and # Duration objects. # each tuple contains 2 duration strings, and a result string for addition and # one for subtraction. The last value says, if the first duration is greater # than the second. MATH_TEST_CASES = (('P5Y7M1DT9H45M16.72S', 'PT27M24.68S', 'P5Y7M1DT10H12M41.4S', 'P5Y7M1DT9H17M52.04S', None), ('PT28M12.73S', 'PT56M29.92S', 'PT1H24M42.65S', '-PT28M17.19S', False), ('P3Y7M23DT5H25M0.33S', 'PT1H1.95S', 'P3Y7M23DT6H25M2.28S', 'P3Y7M23DT4H24M58.38S', None), ('PT1H1.95S', 'P3Y7M23DT5H25M0.33S', 'P3Y7M23DT6H25M2.28S', '-P3Y7M23DT4H24M58.38S', None), ('P1332DT55M0.33S', 'PT1H1.95S', 'P1332DT1H55M2.28S', 'P1331DT23H54M58.38S', True), ('PT1H1.95S', 'P1332DT55M0.33S', 'P1332DT1H55M2.28S', '-P1331DT23H54M58.38S', False)) # A list of test cases to test addition and subtraction of date/datetime # and Duration objects. They are tested against the results of an # equal long timedelta duration. DATE_TEST_CASES = ((date(2008, 2, 29), timedelta(days=10, hours=12, minutes=20), Duration(days=10, hours=12, minutes=20)), (date(2008, 1, 31), timedelta(days=10, hours=12, minutes=20), Duration(days=10, hours=12, minutes=20)), (datetime(2008, 2, 29), timedelta(days=10, hours=12, minutes=20), Duration(days=10, hours=12, minutes=20)), (datetime(2008, 1, 31), timedelta(days=10, hours=12, minutes=20), Duration(days=10, hours=12, minutes=20)), (datetime(2008, 4, 21), timedelta(days=10, hours=12, minutes=20), Duration(days=10, hours=12, minutes=20)), (datetime(2008, 5, 5), timedelta(days=10, hours=12, minutes=20), Duration(days=10, hours=12, minutes=20)), (datetime(2000, 1, 1), timedelta(hours=-33), Duration(hours=-33)), (datetime(2008, 5, 5), Duration(years=1, months=1, days=10, hours=12, minutes=20), Duration(months=13, days=10, hours=12, minutes=20)), (datetime(2000, 3, 30), Duration(years=1, months=1, days=10, hours=12, minutes=20), Duration(months=13, days=10, hours=12, minutes=20)), ) # A list of test cases of additon of date/datetime and Duration. The results # are compared against a given expected result. DATE_CALC_TEST_CASES = ( (date(2000, 2, 1), Duration(years=1, months=1), date(2001, 3, 1)), (date(2000, 2, 29), Duration(years=1, months=1), date(2001, 3, 29)), (date(2000, 2, 29), Duration(years=1), date(2001, 2, 28)), (date(1996, 2, 29), Duration(years=4), date(2000, 2, 29)), (date(2096, 2, 29), Duration(years=4), date(2100, 2, 28)), (date(2000, 2, 1), Duration(years=-1, months=-1), date(1999, 1, 1)), (date(2000, 2, 29), Duration(years=-1, months=-1), date(1999, 1, 29)), (date(2000, 2, 1), Duration(years=1, months=1, days=1), date(2001, 3, 2)), (date(2000, 2, 29), Duration(years=1, months=1, days=1), date(2001, 3, 30)), (date(2000, 2, 29), Duration(years=1, days=1), date(2001, 3, 1)), (date(1996, 2, 29), Duration(years=4, days=1), date(2000, 3, 1)), (date(2096, 2, 29), Duration(years=4, days=1), date(2100, 3, 1)), (date(2000, 2, 1), Duration(years=-1, months=-1, days=-1), date(1998, 12, 31)), (date(2000, 2, 29), Duration(years=-1, months=-1, days=-1), date(1999, 1, 28)), (date(2001, 4, 1), Duration(years=-1, months=-1, days=-1), date(2000, 2, 29)), (date(2000, 4, 1), Duration(years=-1, months=-1, days=-1), date(1999, 2, 28)), (Duration(years=1, months=2), Duration(years=0, months=0, days=1), Duration(years=1, months=2, days=1)), (Duration(years=-1, months=-1, days=-1), date(2000, 4, 1), date(1999, 2, 28)), (Duration(years=1, months=1, weeks=5), date(2000, 1, 30), date(2001, 4, 4)), (parse_duration("P1Y1M5W"), date(2000, 1, 30), date(2001, 4, 4)), (parse_duration("P0.5Y"), date(2000, 1, 30), None), (Duration(years=1, months=1, hours=3), datetime(2000, 1, 30, 12, 15, 00), datetime(2001, 2, 28, 15, 15, 00)), (parse_duration("P1Y1MT3H"), datetime(2000, 1, 30, 12, 15, 00), datetime(2001, 2, 28, 15, 15, 00)), (Duration(years=1, months=2), timedelta(days=1), Duration(years=1, months=2, days=1)), (timedelta(days=1), Duration(years=1, months=2), Duration(years=1, months=2, days=1)), (datetime(2008, 1, 1, 0, 2), Duration(months=1), datetime(2008, 2, 1, 0, 2)), (datetime.strptime("200802", "%Y%M"), parse_duration("P1M"), datetime(2008, 2, 1, 0, 2)), (datetime(2008, 2, 1), Duration(months=1), datetime(2008, 3, 1)), (datetime.strptime("200802", "%Y%m"), parse_duration("P1M"), datetime(2008, 3, 1)), # (date(2000, 1, 1), # Duration(years=1.5), # date(2001, 6, 1)), # (date(2000, 1, 1), # Duration(years=1, months=1.5), # date(2001, 2, 14)), ) # A list of test cases of multiplications of durations # are compared against a given expected result. DATE_MUL_TEST_CASES = ( (Duration(years=1, months=1), 3, Duration(years=3, months=3)), (Duration(years=1, months=1), -3, Duration(years=-3, months=-3)), (3, Duration(years=1, months=1), Duration(years=3, months=3)), (-3, Duration(years=1, months=1), Duration(years=-3, months=-3)), (5, Duration(years=2, minutes=40), Duration(years=10, hours=3, minutes=20)), (-5, Duration(years=2, minutes=40), Duration(years=-10, hours=-3, minutes=-20)), (7, Duration(years=1, months=2, weeks=40), Duration(years=8, months=2, weeks=280))) class DurationTest(unittest.TestCase): ''' This class tests various other aspects of the isoduration module, which are not covered with the test cases listed above. ''' def test_associative(self): ''' Adding 2 durations to a date is not associative. ''' days1 = Duration(days=1) months1 = Duration(months=1) start = date(2000, 3, 30) res1 = start + days1 + months1 res2 = start + months1 + days1 self.assertNotEqual(res1, res2) def test_typeerror(self): ''' Test if TypError is raised with certain parameters. ''' self.assertRaises(TypeError, parse_duration, date(2000, 1, 1)) self.assertRaises(TypeError, operator.sub, Duration(years=1), date(2000, 1, 1)) self.assertRaises(TypeError, operator.sub, 'raise exc', Duration(years=1)) self.assertRaises(TypeError, operator.add, Duration(years=1, months=1, weeks=5), 'raise exception') self.assertRaises(TypeError, operator.add, 'raise exception', Duration(years=1, months=1, weeks=5)) self.assertRaises(TypeError, operator.mul, Duration(years=1, months=1, weeks=5), 'raise exception') self.assertRaises(TypeError, operator.mul, 'raise exception', Duration(years=1, months=1, weeks=5)) self.assertRaises(TypeError, operator.mul, Duration(years=1, months=1, weeks=5), 3.14) self.assertRaises(TypeError, operator.mul, 3.14, Duration(years=1, months=1, weeks=5)) def test_parseerror(self): ''' Test for unparseable duration string. ''' self.assertRaises(ISO8601Error, parse_duration, 'T10:10:10') def test_repr(self): ''' Test __repr__ and __str__ for Duration objects. ''' dur = Duration(10, 10, years=10, months=10) self.assertEqual('10 years, 10 months, 10 days, 0:00:10', str(dur)) self.assertEqual('isodate.duration.Duration(10, 10, 0,' ' years=10, months=10)', repr(dur)) dur = Duration(months=0) self.assertEqual('0:00:00', str(dur)) dur = Duration(months=1) self.assertEqual('1 month, 0:00:00', str(dur)) def test_hash(self): ''' Test __hash__ for Duration objects. ''' dur1 = Duration(10, 10, years=10, months=10) dur2 = Duration(9, 9, years=9, months=9) dur3 = Duration(10, 10, years=10, months=10) self.assertNotEqual(hash(dur1), hash(dur2)) self.assertNotEqual(id(dur1), id(dur2)) self.assertEqual(hash(dur1), hash(dur3)) self.assertNotEqual(id(dur1), id(dur3)) durSet = set() durSet.add(dur1) durSet.add(dur2) durSet.add(dur3) self.assertEqual(len(durSet), 2) def test_neg(self): ''' Test __neg__ for Duration objects. ''' self.assertEqual(-Duration(0), Duration(0)) self.assertEqual(-Duration(years=1, months=1), Duration(years=-1, months=-1)) self.assertEqual(-Duration(years=1, months=1), Duration(months=-13)) self.assertNotEqual(-Duration(years=1), timedelta(days=-365)) self.assertNotEqual(-timedelta(days=365), Duration(years=-1)) # FIXME: this test fails in python 3... it seems like python3 # treats a == b the same b == a # self.assertNotEqual(-timedelta(days=10), -Duration(days=10)) def test_format(self): ''' Test various other strftime combinations. ''' self.assertEqual(duration_isoformat(Duration(0)), 'P0D') self.assertEqual(duration_isoformat(-Duration(0)), 'P0D') self.assertEqual(duration_isoformat(Duration(seconds=10)), 'PT10S') self.assertEqual(duration_isoformat(Duration(years=-1, months=-1)), '-P1Y1M') self.assertEqual(duration_isoformat(-Duration(years=1, months=1)), '-P1Y1M') self.assertEqual(duration_isoformat(-Duration(years=-1, months=-1)), 'P1Y1M') self.assertEqual(duration_isoformat(-Duration(years=-1, months=-1)), 'P1Y1M') dur = Duration(years=3, months=7, days=23, hours=5, minutes=25, milliseconds=330) self.assertEqual(duration_isoformat(dur), 'P3Y7M23DT5H25M0.33S') self.assertEqual(duration_isoformat(-dur), '-P3Y7M23DT5H25M0.33S') def test_equal(self): ''' Test __eq__ and __ne__ methods. ''' self.assertEqual(Duration(years=1, months=1), Duration(years=1, months=1)) self.assertEqual(Duration(years=1, months=1), Duration(months=13)) self.assertNotEqual(Duration(years=1, months=2), Duration(years=1, months=1)) self.assertNotEqual(Duration(years=1, months=1), Duration(months=14)) self.assertNotEqual(Duration(years=1), timedelta(days=365)) self.assertFalse(Duration(years=1, months=1) != Duration(years=1, months=1)) self.assertFalse(Duration(years=1, months=1) != Duration(months=13)) self.assertTrue(Duration(years=1, months=2) != Duration(years=1, months=1)) self.assertTrue(Duration(years=1, months=1) != Duration(months=14)) self.assertTrue(Duration(years=1) != timedelta(days=365)) self.assertEqual(Duration(days=1), timedelta(days=1)) # FIXME: this test fails in python 3... it seems like python3 # treats a != b the same b != a # self.assertNotEqual(timedelta(days=1), Duration(days=1)) def test_totimedelta(self): ''' Test conversion form Duration to timedelta. ''' dur = Duration(years=1, months=2, days=10) self.assertEqual(dur.totimedelta(datetime(1998, 2, 25)), timedelta(434)) # leap year has one day more in february self.assertEqual(dur.totimedelta(datetime(2000, 2, 25)), timedelta(435)) dur = Duration(months=2) # march is longer than february, but april is shorter than # march (cause only one day difference compared to 2) self.assertEqual(dur.totimedelta(datetime(2000, 2, 25)), timedelta(60)) self.assertEqual(dur.totimedelta(datetime(2001, 2, 25)), timedelta(59)) self.assertEqual(dur.totimedelta(datetime(2001, 3, 25)), timedelta(61)) def create_parsetestcase(durationstring, expectation, format, altstr): """ Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the PARSE_TEST_CASES list, so that a failed test won't stop other tests. """ class TestParseDuration(unittest.TestCase): ''' A test case template to parse an ISO duration string into a timedelta or Duration object. ''' def test_parse(self): ''' Parse an ISO duration string and compare it to the expected value. ''' result = parse_duration(durationstring) self.assertEqual(result, expectation) def test_format(self): ''' Take duration/timedelta object and create ISO string from it. This is the reverse test to test_parse. ''' if altstr: self.assertEqual(duration_isoformat(expectation, format), altstr) else: # if durationstring == '-P2W': # import pdb; pdb.set_trace() self.assertEqual(duration_isoformat(expectation, format), durationstring) return unittest.TestLoader().loadTestsFromTestCase(TestParseDuration) def create_mathtestcase(dur1, dur2, resadd, ressub, resge): """ Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the MATH_TEST_CASES list, so that a failed test won't stop other tests. """ dur1 = parse_duration(dur1) dur2 = parse_duration(dur2) resadd = parse_duration(resadd) ressub = parse_duration(ressub) class TestMathDuration(unittest.TestCase): ''' A test case template test addition, subtraction and > operators for Duration objects. ''' def test_add(self): ''' Test operator + (__add__, __radd__) ''' self.assertEqual(dur1 + dur2, resadd) def test_sub(self): ''' Test operator - (__sub__, __rsub__) ''' self.assertEqual(dur1 - dur2, ressub) def test_ge(self): ''' Test operator > and < ''' def dogetest(): ''' Test greater than.''' return dur1 > dur2 def doletest(): ''' Test less than.''' return dur1 < dur2 if resge is None: self.assertRaises(TypeError, dogetest) self.assertRaises(TypeError, doletest) else: self.assertEqual(dogetest(), resge) self.assertEqual(doletest(), not resge) return unittest.TestLoader().loadTestsFromTestCase(TestMathDuration) def create_datetestcase(start, tdelta, duration): """ Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the DATE_TEST_CASES list, so that a failed test won't stop other tests. """ class TestDateCalc(unittest.TestCase): ''' A test case template test addition, subtraction operators for Duration objects. ''' def test_add(self): ''' Test operator +. ''' self.assertEqual(start + tdelta, start + duration) def test_sub(self): ''' Test operator -. ''' self.assertEqual(start - tdelta, start - duration) return unittest.TestLoader().loadTestsFromTestCase(TestDateCalc) def create_datecalctestcase(start, duration, expectation): """ Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the DATE_CALC_TEST_CASES list, so that a failed test won't stop other tests. """ class TestDateCalc(unittest.TestCase): ''' A test case template test addition operators for Duration objects. ''' def test_calc(self): ''' Test operator +. ''' if expectation is None: self.assertRaises(ValueError, operator.add, start, duration) else: self.assertEqual(start + duration, expectation) return unittest.TestLoader().loadTestsFromTestCase(TestDateCalc) def create_datemultestcase(operand1, operand2, expectation): """ Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the DATE_CALC_TEST_CASES list, so that a failed test won't stop other tests. """ class TestDateMul(unittest.TestCase): ''' A test case template test addition operators for Duration objects. ''' def test_mul(self): ''' Test operator *. ''' self.assertEqual(operand1 * operand2, expectation) return unittest.TestLoader().loadTestsFromTestCase(TestDateMul) def test_suite(): ''' Return a test suite containing all test defined above. ''' suite = unittest.TestSuite() for durationstring, (expectation, format, altstr) in PARSE_TEST_CASES.items(): suite.addTest(create_parsetestcase(durationstring, expectation, format, altstr)) for testdata in MATH_TEST_CASES: suite.addTest(create_mathtestcase(*testdata)) for testdata in DATE_TEST_CASES: suite.addTest(create_datetestcase(*testdata)) for testdata in DATE_CALC_TEST_CASES: suite.addTest(create_datecalctestcase(*testdata)) for testdata in DATE_MUL_TEST_CASES: suite.addTest(create_datemultestcase(*testdata)) suite.addTest(unittest.TestLoader().loadTestsFromTestCase(DurationTest)) return suite # load_tests Protocol def load_tests(loader, tests, pattern): return test_suite() if __name__ == '__main__': unittest.main(defaultTest='test_suite') isodate-0.6.0/src/isodate/tests/test_pickle.py0000644€YMd0€T]®j0000000346513127144114021253 0ustar s261679400000000000000import unittest from six.moves import cPickle as pickle import isodate class TestPickle(unittest.TestCase): ''' A test case template to parse an ISO datetime string into a datetime object. ''' def test_pickle_datetime(self): ''' Parse an ISO datetime string and compare it to the expected value. ''' dti = isodate.parse_datetime('2012-10-26T09:33+00:00') for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): pikl = pickle.dumps(dti, proto) self.assertEqual(dti, pickle.loads(pikl), "pickle proto %d failed" % proto) def test_pickle_duration(self): ''' Pickle / unpickle duration objects. ''' from isodate.duration import Duration dur = Duration() failed = [] for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): try: pikl = pickle.dumps(dur, proto) if dur != pickle.loads(pikl): raise Exception("not equal") except Exception as e: failed.append("pickle proto %d failed (%s)" % (proto, repr(e))) self.assertEqual(len(failed), 0, "pickle protos failed: %s" % str(failed)) def test_pickle_utc(self): ''' isodate.UTC objects remain the same after pickling. ''' self.assertTrue(isodate.UTC is pickle.loads(pickle.dumps(isodate.UTC))) def test_suite(): ''' Construct a TestSuite instance for all test cases. ''' suite = unittest.TestSuite() suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestPickle)) return suite # load_tests Protocol def load_tests(loader, tests, pattern): return test_suite() if __name__ == '__main__': unittest.main(defaultTest='test_suite') isodate-0.6.0/src/isodate/tests/test_strf.py0000644€YMd0€T]®j0000001217113127140635020760 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' Test cases for the isodate module. ''' import unittest import time from datetime import datetime, timedelta from isodate import strftime from isodate import LOCAL from isodate import DT_EXT_COMPLETE from isodate import tzinfo TEST_CASES = ((datetime(2012, 12, 25, 13, 30, 0, 0, LOCAL), DT_EXT_COMPLETE, "2012-12-25T13:30:00+10:00"), # DST ON (datetime(1999, 12, 25, 13, 30, 0, 0, LOCAL), DT_EXT_COMPLETE, "1999-12-25T13:30:00+11:00"), # microseconds (datetime(2012, 10, 12, 8, 29, 46, 69178), "%Y-%m-%dT%H:%M:%S.%f", "2012-10-12T08:29:46.069178"), (datetime(2012, 10, 12, 8, 29, 46, 691780), "%Y-%m-%dT%H:%M:%S.%f", "2012-10-12T08:29:46.691780"), ) def create_testcase(dt, format, expectation): """ Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the TEST_CASES list, so that a failed test won't stop other tests. """ class TestDate(unittest.TestCase): ''' A test case template to test ISO date formatting. ''' # local time zone mock function def localtime_mock(self, secs): """ mock time.localtime so that it always returns a time_struct with tm_idst=1 """ tt = self.ORIG['localtime'](secs) # befor 2000 everything is dst, after 2000 no dst. if tt.tm_year < 2000: dst = 1 else: dst = 0 tt = (tt.tm_year, tt.tm_mon, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec, tt.tm_wday, tt.tm_yday, dst) return time.struct_time(tt) def setUp(self): self.ORIG = {} self.ORIG['STDOFFSET'] = tzinfo.STDOFFSET self.ORIG['DSTOFFSET'] = tzinfo.DSTOFFSET self.ORIG['DSTDIFF'] = tzinfo.DSTDIFF self.ORIG['localtime'] = time.localtime # ovveride all saved values with fixtures. # calculate LOCAL TZ offset, so that this test runs in # every time zone tzinfo.STDOFFSET = timedelta(seconds=36000) # assume LOC = +10:00 tzinfo.DSTOFFSET = timedelta(seconds=39600) # assume DST = +11:00 tzinfo.DSTDIFF = tzinfo.DSTOFFSET - tzinfo.STDOFFSET time.localtime = self.localtime_mock def tearDown(self): # restore test fixtures tzinfo.STDOFFSET = self.ORIG['STDOFFSET'] tzinfo.DSTOFFSET = self.ORIG['DSTOFFSET'] tzinfo.DSTDIFF = self.ORIG['DSTDIFF'] time.localtime = self.ORIG['localtime'] def test_format(self): ''' Take date object and create ISO string from it. This is the reverse test to test_parse. ''' if expectation is None: self.assertRaises(AttributeError, strftime(dt, format)) else: self.assertEqual(strftime(dt, format), expectation) return unittest.TestLoader().loadTestsFromTestCase(TestDate) def test_suite(): ''' Construct a TestSuite instance for all test cases. ''' suite = unittest.TestSuite() for dt, format, expectation in TEST_CASES: suite.addTest(create_testcase(dt, format, expectation)) return suite # load_tests Protocol def load_tests(loader, tests, pattern): return test_suite() if __name__ == '__main__': unittest.main(defaultTest='test_suite') isodate-0.6.0/src/isodate/tests/test_time.py0000644€YMd0€T]®j0000001507313170040020020725 0ustar s261679400000000000000############################################################################## # Copyright 2009, Gerhard Weis # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the authors nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT ############################################################################## ''' Test cases for the isotime module. ''' import unittest from datetime import time from isodate import parse_time, UTC, FixedOffset, ISO8601Error, time_isoformat from isodate import TIME_BAS_COMPLETE, TIME_BAS_MINUTE from isodate import TIME_EXT_COMPLETE, TIME_EXT_MINUTE from isodate import TIME_HOUR from isodate import TZ_BAS, TZ_EXT, TZ_HOUR # the following list contains tuples of ISO time strings and the expected # result from the parse_time method. A result of None means an ISO8601Error # is expected. TEST_CASES = [('232050', time(23, 20, 50), TIME_BAS_COMPLETE + TZ_BAS), ('23:20:50', time(23, 20, 50), TIME_EXT_COMPLETE + TZ_EXT), ('2320', time(23, 20), TIME_BAS_MINUTE), ('23:20', time(23, 20), TIME_EXT_MINUTE), ('23', time(23), TIME_HOUR), ('232050,5', time(23, 20, 50, 500000), None), ('23:20:50.5', time(23, 20, 50, 500000), None), # test precision ('15:33:42.123456', time(15, 33, 42, 123456), None), ('15:33:42.1234564', time(15, 33, 42, 123456), None), ('15:33:42.1234557', time(15, 33, 42, 123456), None), ('2320,8', time(23, 20, 48), None), ('23:20,8', time(23, 20, 48), None), ('23,3', time(23, 18), None), ('232030Z', time(23, 20, 30, tzinfo=UTC), TIME_BAS_COMPLETE + TZ_BAS), ('2320Z', time(23, 20, tzinfo=UTC), TIME_BAS_MINUTE + TZ_BAS), ('23Z', time(23, tzinfo=UTC), TIME_HOUR + TZ_BAS), ('23:20:30Z', time(23, 20, 30, tzinfo=UTC), TIME_EXT_COMPLETE + TZ_EXT), ('23:20Z', time(23, 20, tzinfo=UTC), TIME_EXT_MINUTE + TZ_EXT), ('152746+0100', time(15, 27, 46, tzinfo=FixedOffset(1, 0, '+0100')), TIME_BAS_COMPLETE + TZ_BAS), ('152746-0500', time(15, 27, 46, tzinfo=FixedOffset(-5, 0, '-0500')), TIME_BAS_COMPLETE + TZ_BAS), ('152746+01', time(15, 27, 46, tzinfo=FixedOffset(1, 0, '+01:00')), TIME_BAS_COMPLETE + TZ_HOUR), ('152746-05', time(15, 27, 46, tzinfo=FixedOffset(-5, -0, '-05:00')), TIME_BAS_COMPLETE + TZ_HOUR), ('15:27:46+01:00', time(15, 27, 46, tzinfo=FixedOffset(1, 0, '+01:00')), TIME_EXT_COMPLETE + TZ_EXT), ('15:27:46-05:00', time(15, 27, 46, tzinfo=FixedOffset(-5, -0, '-05:00')), TIME_EXT_COMPLETE + TZ_EXT), ('15:27:46+01', time(15, 27, 46, tzinfo=FixedOffset(1, 0, '+01:00')), TIME_EXT_COMPLETE + TZ_HOUR), ('15:27:46-05', time(15, 27, 46, tzinfo=FixedOffset(-5, -0, '-05:00')), TIME_EXT_COMPLETE + TZ_HOUR), ('15:27:46-05:30', time(15, 27, 46, tzinfo=FixedOffset(-5, -30, '-05:30')), TIME_EXT_COMPLETE + TZ_EXT), ('15:27:46-0545', time(15, 27, 46, tzinfo=FixedOffset(-5, -45, '-0545')), TIME_EXT_COMPLETE + TZ_BAS), ('1:17:30', None, TIME_EXT_COMPLETE)] def create_testcase(timestring, expectation, format): """ Create a TestCase class for a specific test. This allows having a separate TestCase for each test tuple from the TEST_CASES list, so that a failed test won't stop other tests. """ class TestTime(unittest.TestCase): ''' A test case template to parse an ISO time string into a time object. ''' def test_parse(self): ''' Parse an ISO time string and compare it to the expected value. ''' if expectation is None: self.assertRaises(ISO8601Error, parse_time, timestring) else: result = parse_time(timestring) self.assertEqual(result, expectation) def test_format(self): ''' Take time object and create ISO string from it. This is the reverse test to test_parse. ''' if expectation is None: self.assertRaises(AttributeError, time_isoformat, expectation, format) elif format is not None: self.assertEqual(time_isoformat(expectation, format), timestring) return unittest.TestLoader().loadTestsFromTestCase(TestTime) def test_suite(): ''' Construct a TestSuite instance for all test cases. ''' suite = unittest.TestSuite() for timestring, expectation, format in TEST_CASES: suite.addTest(create_testcase(timestring, expectation, format)) return suite # load_tests Protocol def load_tests(loader, tests, pattern): return test_suite() if __name__ == '__main__': unittest.main(defaultTest='test_suite') isodate-0.6.0/src/isodate/tzinfo.py0000644€YMd0€T]®j0000000715413127230436017116 0ustar s261679400000000000000''' This module provides some datetime.tzinfo implementations. All those classes are taken from the Python documentation. ''' from datetime import timedelta, tzinfo import time ZERO = timedelta(0) # constant for zero time offset. class Utc(tzinfo): '''UTC Universal time coordinated time zone. ''' def utcoffset(self, dt): ''' Return offset from UTC in minutes east of UTC, which is ZERO for UTC. ''' return ZERO def tzname(self, dt): ''' Return the time zone name corresponding to the datetime object dt, as a string. ''' return "UTC" def dst(self, dt): ''' Return the daylight saving time (DST) adjustment, in minutes east of UTC. ''' return ZERO def __reduce__(self): ''' When unpickling a Utc object, return the default instance below, UTC. ''' return _Utc, () UTC = Utc() # the default instance for UTC. def _Utc(): ''' Helper function for unpickling a Utc object. ''' return UTC class FixedOffset(tzinfo): ''' A class building tzinfo objects for fixed-offset time zones. Note that FixedOffset(0, 0, "UTC") or FixedOffset() is a different way to build a UTC tzinfo object. ''' def __init__(self, offset_hours=0, offset_minutes=0, name="UTC"): ''' Initialise an instance with time offset and name. The time offset should be positive for time zones east of UTC and negate for time zones west of UTC. ''' self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes) self.__name = name def utcoffset(self, dt): ''' Return offset from UTC in minutes of UTC. ''' return self.__offset def tzname(self, dt): ''' Return the time zone name corresponding to the datetime object dt, as a string. ''' return self.__name def dst(self, dt): ''' Return the daylight saving time (DST) adjustment, in minutes east of UTC. ''' return ZERO def __repr__(self): ''' Return nicely formatted repr string. ''' return "" % self.__name STDOFFSET = timedelta(seconds=-time.timezone) # locale time zone offset # calculate local daylight saving offset if any. if time.daylight: DSTOFFSET = timedelta(seconds=-time.altzone) else: DSTOFFSET = STDOFFSET DSTDIFF = DSTOFFSET - STDOFFSET # difference between local time zone and local DST time zone class LocalTimezone(tzinfo): """ A class capturing the platform's idea of local time. """ def utcoffset(self, dt): ''' Return offset from UTC in minutes of UTC. ''' if self._isdst(dt): return DSTOFFSET else: return STDOFFSET def dst(self, dt): ''' Return daylight saving offset. ''' if self._isdst(dt): return DSTDIFF else: return ZERO def tzname(self, dt): ''' Return the time zone name corresponding to the datetime object dt, as a string. ''' return time.tzname[self._isdst(dt)] def _isdst(self, dt): ''' Returns true if DST is active for given datetime object dt. ''' tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1) stamp = time.mktime(tt) tt = time.localtime(stamp) return tt.tm_isdst > 0 # the default instance for local time zone. LOCAL = LocalTimezone() isodate-0.6.0/src/isodate.egg-info/0000755€YMd0€T]®j0000000000013170103163016710 5ustar s261679400000000000000isodate-0.6.0/src/isodate.egg-info/dependency_links.txt0000644€YMd0€T]®j0000000000113170103163022756 0ustar s261679400000000000000 isodate-0.6.0/src/isodate.egg-info/PKG-INFO0000644€YMd0€T]®j0000002625713170103163020021 0ustar s261679400000000000000Metadata-Version: 1.1 Name: isodate Version: 0.6.0 Summary: An ISO 8601 date/time/duration parser and formatter Home-page: https://github.com/gweis/isodate/ Author: Gerhard Weis Author-email: gerhard.weis@proclos.com License: BSD Description: ISO 8601 date/time parser ========================= .. image:: https://travis-ci.org/gweis/isodate.svg?branch=master :target: https://travis-ci.org/gweis/isodate :alt: Travis-CI .. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master :target: https://coveralls.io/r/gweis/isodate?branch=master :alt: Coveralls .. image:: https://img.shields.io/pypi/v/isodate.svg :target: https://pypi.python.org/pypi/isodate/ :alt: Latest Version .. image:: https://img.shields.io/pypi/l/isodate.svg :target: https://pypi.python.org/pypi/isodate/ :alt: License This module implements ISO 8601 date, time and duration parsing. The implementation follows ISO8601:2004 standard, and implements only date/time representations mentioned in the standard. If something is not mentioned there, then it is treated as non existent, and not as an allowed option. For instance, ISO8601:2004 never mentions 2 digit years. So, it is not intended by this module to support 2 digit years. (while it may still be valid as ISO date, because it is not explicitly forbidden.) Another example is, when no time zone information is given for a time, then it should be interpreted as local time, and not UTC. As this module maps ISO 8601 dates/times to standard Python data types, like *date*, *time*, *datetime* and *timedelta*, it is not possible to convert all possible ISO 8601 dates/times. For instance, dates before 0001-01-01 are not allowed by the Python *date* and *datetime* classes. Additionally fractional seconds are limited to microseconds. That means if the parser finds for instance nanoseconds it will round it to microseconds. Documentation ------------- Currently there are four parsing methods available. * parse_time: parses an ISO 8601 time string into a *time* object * parse_date: parses an ISO 8601 date string into a *date* object * parse_datetime: parses an ISO 8601 date-time string into a *datetime* object * parse_duration: parses an ISO 8601 duration string into a *timedelta* or *Duration* object. * parse_tzinfo: parses the time zone info part of an ISO 8601 string into a *tzinfo* object. As ISO 8601 allows to define durations in years and months, and *timedelta* does not handle years and months, this module provides a *Duration* class, which can be used almost like a *timedelta* object (with some limitations). However, a *Duration* object can be converted into a *timedelta* object. There are also ISO formatting methods for all supported data types. Each *xxx_isoformat* method accepts a format parameter. The default format is always the ISO 8601 expanded format. This is the same format used by *datetime.isoformat*: * time_isoformat: Intended to create ISO time strings with default format *hh:mm:ssZ*. * date_isoformat: Intended to create ISO date strings with default format *yyyy-mm-dd*. * datetime_isoformat: Intended to create ISO date-time strings with default format *yyyy-mm-ddThh:mm:ssZ*. * duration_isoformat: Intended to create ISO duration strings with default format *PnnYnnMnnDTnnHnnMnnS*. * tz_isoformat: Intended to create ISO time zone strings with default format *hh:mm*. * strftime: A re-implementation mostly compatible with Python's *strftime*, but supports only those format strings, which can also be used for dates prior 1900. This method also understands how to format *datetime* and *Duration* instances. Installation: ------------- This module can easily be installed with Python standard installation methods. Either use *python setup.py install* or in case you have *setuptools* or *distribute* available, you can also use *easy_install*. Limitations: ------------ * The parser accepts several date/time representation which should be invalid according to ISO 8601 standard. 1. for date and time together, this parser accepts a mixture of basic and extended format. e.g. the date could be in basic format, while the time is accepted in extended format. It also allows short dates and times in date-time strings. 2. For incomplete dates, the first day is chosen. e.g. 19th century results in a date of 1901-01-01. 3. negative *Duration* and *timedelta* value are not fully supported yet. Further information: -------------------- The doc strings and unit tests should provide rather detailed information about the methods and their limitations. The source release provides a *setup.py* script, which can be used to run the unit tests included. Source code is available at ``_. CHANGES ======= 0.6.0 (2017-10-13) ------------------ - support incomplete month date (Fabien Loffredo) - rely on duck typing when doing duration maths - support ':' as separator in fractional time zones (usrenmae) 0.5.4 (2015-08-06) ------------------ - Fix parsing of Periods (Fabien Bochu) - Make Duration objects hashable (Geoffrey Fairchild) - Add multiplication to duration (Reinoud Elhorst) 0.5.1 (2014-11-07) ------------------ - fixed pickling of Duration objects - raise ISO8601Error when there is no 'T' separator in datetime strings (Adrian Coveney) 0.5.0 (2014-02-23) ------------------ - ISO8601Error are subclasses of ValueError now (Michael Hrivnak) - improve compatibility across various python variants and versions - raise exceptions when using fractional years and months in date maths with durations - renamed method todatetime on Duraction objects to totimedelta 0.4.9 (2012-10-30) ------------------ - support pickling FixedOffset instances - make sure parsed fractional seconds are in microseconds - add leading zeros when formattig microseconds (Jarom Loveridge) 0.4.8 (2012-05-04) ------------------ - fixed incompatibility of unittests with python 2.5 and 2.6 (runs fine on 2.7 and 3.2) 0.4.7 (2012-01-26) ------------------ - fixed tzinfo formatting (never pass None into tzinfo.utcoffset()) 0.4.6 (2012-01-06) ------------------ - added Python 3 compatibility via 2to3 0.4.5 (2012-01-06) ------------------ - made setuptools dependency optional 0.4.4 (2011-04-16) ------------------ - Fixed formatting of microseconds for datetime objects 0.4.3 (2010-10-29) ------------------ - Fixed problem with %P formating and fractions (supplied by David Brooks) 0.4.2 (2010-10-28) ------------------ - Implemented unary - for Duration (supplied by David Brooks) - Output fractional seconds with '%P' format. (partly supplied by David Brooks) 0.4.1 (2010-10-13) ------------------ - fixed bug in comparison between timedelta and Duration. - fixed precision problem with microseconds (reported by Tommi Virtanen) 0.4.0 (2009-02-09) ------------------ - added method to parse ISO 8601 time zone strings - added methods to create ISO 8601 conforming strings 0.3.0 (2009-1-05) ------------------ - Initial release TODOs ===== This to do list contains some thoughts and ideas about missing features, and parts to think about, whether to implement them or not. This list is probably not complete. Missing features: ----------------- * time formating does not allow to create fractional representations. * parser for ISO intervals. * currently microseconds are always padded to a length of 6 characters. trailing 0s should be optional Documentation: -------------- * parse_datetime: - complete documentation to show what this function allows, but ISO forbids. and vice verse. - support other separators between date and time than 'T' * parse_date: - yeardigits should be always greater than 4 - dates before 0001-01-01 are not supported * parse_duration: - alternative formats are not fully supported due to parse_date restrictions - standard duration format is fully supported but not very restrictive. * Duration: - support fractional years and month in calculations - implement w3c order relation? (``_) - refactor to have duration mathematics only at one place. - localize __str__ method (does timedelta do this?) - when is a Duration negative? - normalize Durations. months [00-12] and years ]-inf,+inf[ Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Internet Classifier: Topic :: Software Development :: Libraries :: Python Modules isodate-0.6.0/src/isodate.egg-info/requires.txt0000644€YMd0€T]®j0000000000413170103163021302 0ustar s261679400000000000000six isodate-0.6.0/src/isodate.egg-info/SOURCES.txt0000644€YMd0€T]®j0000000130113170103163020567 0ustar s261679400000000000000CHANGES.txt MANIFEST.in README.rst TODO.txt setup.cfg setup.py src/isodate/__init__.py src/isodate/duration.py src/isodate/isodates.py src/isodate/isodatetime.py src/isodate/isoduration.py src/isodate/isoerror.py src/isodate/isostrf.py src/isodate/isotime.py src/isodate/isotzinfo.py src/isodate/tzinfo.py src/isodate.egg-info/PKG-INFO src/isodate.egg-info/SOURCES.txt src/isodate.egg-info/dependency_links.txt src/isodate.egg-info/requires.txt src/isodate.egg-info/top_level.txt src/isodate/tests/__init__.py src/isodate/tests/test_date.py src/isodate/tests/test_datetime.py src/isodate/tests/test_duration.py src/isodate/tests/test_pickle.py src/isodate/tests/test_strf.py src/isodate/tests/test_time.pyisodate-0.6.0/src/isodate.egg-info/top_level.txt0000644€YMd0€T]®j0000000001013170103163021431 0ustar s261679400000000000000isodate isodate-0.6.0/TODO.txt0000644€YMd0€T]®j0000000254212302221023014300 0ustar s261679400000000000000 TODOs ===== This to do list contains some thoughts and ideas about missing features, and parts to think about, whether to implement them or not. This list is probably not complete. Missing features: ----------------- * time formating does not allow to create fractional representations. * parser for ISO intervals. * currently microseconds are always padded to a length of 6 characters. trailing 0s should be optional Documentation: -------------- * parse_datetime: - complete documentation to show what this function allows, but ISO forbids. and vice verse. - support other separators between date and time than 'T' * parse_date: - yeardigits should be always greater than 4 - dates before 0001-01-01 are not supported * parse_duration: - alternative formats are not fully supported due to parse_date restrictions - standard duration format is fully supported but not very restrictive. * Duration: - support fractional years and month in calculations - implement w3c order relation? (``_) - refactor to have duration mathematics only at one place. - localize __str__ method (does timedelta do this?) - when is a Duration negative? - normalize Durations. months [00-12] and years ]-inf,+inf[