mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-04-16 07:23:46 +00:00
Add dateutil library
This commit is contained in:
parent
bf0ebbc487
commit
47f7d99954
37 changed files with 19626 additions and 0 deletions
|
@ -1 +1,2 @@
|
|||
import requests
|
||||
import dateutil
|
||||
|
|
54
resources/lib/libraries/dateutil/LICENSE
Normal file
54
resources/lib/libraries/dateutil/LICENSE
Normal file
|
@ -0,0 +1,54 @@
|
|||
Copyright 2017- Paul Ganssle <paul@ganssle.io>
|
||||
Copyright 2017- dateutil contributors (see AUTHORS file)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
The above license applies to all contributions after 2017-12-01, as well as
|
||||
all contributions that have been re-licensed (see AUTHORS file for the list of
|
||||
contributors who have re-licensed their code).
|
||||
--------------------------------------------------------------------------------
|
||||
dateutil - Extensions to the standard Python datetime module.
|
||||
|
||||
Copyright (c) 2003-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
|
||||
Copyright (c) 2012-2014 - Tomi Pieviläinen <tomi.pievilainen@iki.fi>
|
||||
Copyright (c) 2014-2016 - Yaron de Leeuw <me@jarondl.net>
|
||||
Copyright (c) 2015- - Paul Ganssle <paul@ganssle.io>
|
||||
Copyright (c) 2015- - dateutil contributors (see AUTHORS file)
|
||||
|
||||
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 copyright holder 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 (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The above BSD License Applies to all code, even that also covered by Apache 2.0.
|
701
resources/lib/libraries/dateutil/NEWS
Normal file
701
resources/lib/libraries/dateutil/NEWS
Normal file
|
@ -0,0 +1,701 @@
|
|||
Version 2.7.3 (2018-05-09)
|
||||
==========================
|
||||
|
||||
Data updates
|
||||
------------
|
||||
|
||||
- Update tzdata to 2018e. (gh pr #710)
|
||||
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fixed an issue where decimal.Decimal would cast `NaN` or infinite value in a
|
||||
parser.parse, which will raise decimal.Decimal-specific errors. Reported and
|
||||
fixed by @amureki (gh issue #662, gh pr #679).
|
||||
- Fixed a ValueError being thrown if tzinfos call explicity returns ``None``.
|
||||
Reported by @pganssle (gh issue #661) Fixed by @parsethis (gh pr #681)
|
||||
- Fixed incorrect parsing of certain dates earlier than 100 AD when repesented
|
||||
in the form "%B.%Y.%d", e.g. "December.0031.30". (gh issue #687, pr #700)
|
||||
- Fixed a bug where automatically generated DTSTART was naive even if a
|
||||
specified UNTIL had a time zone. Automatically generated DTSTART will now
|
||||
take on the timezone of an UNTIL date, if provided. Reported by @href (gh
|
||||
issue #652). Fixed by @absreim (gh pr #693).
|
||||
|
||||
|
||||
Documentation changes
|
||||
---------------------
|
||||
|
||||
- Corrected link syntax and updated URL to https for ISO year week number
|
||||
notation in relativedelta examples. (gh issue #670, pr #711)
|
||||
- Add doctest examples to tzfile documentation. Done by @weatherpattern and
|
||||
@pganssle (gh pr #671)
|
||||
- Updated the documentation for relativedelta. Removed references to tuple
|
||||
arguments for weekday, explained effect of weekday(_, 1) and better explained
|
||||
the order of operations that relativedelta applies. Fixed by @kvn219
|
||||
@huangy22 and @ElliotJH (gh pr #673)
|
||||
- Added changelog to documentation. (gh issue #692, gh pr #707)
|
||||
- Changed order of keywords in rrule docstring. Reported and fixed by
|
||||
@rmahajan14 (gh issue #686, gh pr #695).
|
||||
- Added documentation for ``dateutil.tz.gettz``. Reported by @pganssle (gh
|
||||
issue #647). Fixed by @weatherpattern (gh pr #704)
|
||||
- Cleaned up malformed RST in the ``tz`` documentation. (gh issue #702, gh pr
|
||||
#706)
|
||||
- Changed the default theme to sphinx_rtd_theme, and changed the sphinx
|
||||
configuration to go along with that. (gh pr #707)
|
||||
- Reorganized ``dateutil.tz`` documentation and fixed issue with the
|
||||
``dateutil.tz`` docstring. (gh pr #714)
|
||||
|
||||
|
||||
Misc
|
||||
----
|
||||
|
||||
- GH #674, GH #688, GH #699
|
||||
|
||||
|
||||
Version 2.7.2 (2018-03-26)
|
||||
==========================
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fixed an issue with the setup script running in non-UTF-8 environment.
|
||||
Reported and fixed by @gergondet (gh pr #651)
|
||||
|
||||
|
||||
Misc
|
||||
----
|
||||
|
||||
- GH #655
|
||||
|
||||
|
||||
Version 2.7.1 (2018-03-24)
|
||||
===========================
|
||||
|
||||
Data updates
|
||||
------------
|
||||
|
||||
- Updated tzdata version to 2018d.
|
||||
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fixed issue where parser.parse would occasionally raise
|
||||
decimal.Decimal-specific error types rather than ValueError. Reported by
|
||||
@amureki (gh issue #632). Fixed by @pganssle (gh pr #636).
|
||||
- Improve error message when rrule's dtstart and until are not both naive or
|
||||
both aware. Reported and fixed by @ryanpetrello (gh issue #633, gh pr #634)
|
||||
|
||||
|
||||
Misc
|
||||
----
|
||||
|
||||
- GH #644, GH #648
|
||||
|
||||
|
||||
Version 2.7.0
|
||||
=============
|
||||
- Dropped support for Python 2.6 (gh pr #362 by @jdufresne)
|
||||
- Dropped support for Python 3.2 (gh pr #626)
|
||||
- Updated zoneinfo file to 2018c (gh pr #616)
|
||||
- Changed licensing scheme so all new contributions are dual licensed under
|
||||
Apache 2.0 and BSD. (gh pr #542, issue #496)
|
||||
- Added __all__ variable to the root package. Reported by @tebriel
|
||||
(gh issue #406), fixed by @mariocj89 (gh pr #494)
|
||||
- Added python_requires to setup.py so that pip will distribute the right
|
||||
version of dateutil. Fixed by @jakec-github (gh issue #537, pr #552)
|
||||
- Added the utils submodule, for miscellaneous utilities.
|
||||
- Added within_delta function to utils - added by @justanr (gh issue #432,
|
||||
gh pr #437)
|
||||
- Added today function to utils (gh pr #474)
|
||||
- Added default_tzinfo function to utils (gh pr #475), solving an issue
|
||||
reported by @nealmcb (gh issue #94)
|
||||
- Added dedicated ISO 8601 parsing function isoparse (gh issue #424).
|
||||
Initial implementation by @pganssle in gh pr #489 and #622, with a
|
||||
pre-release fix by @kirit93 (gh issue #546, gh pr #573).
|
||||
- Moved parser module into parser/_parser.py and officially deprecated the use
|
||||
of several private functions and classes from that module. (gh pr #501, #515)
|
||||
- Tweaked parser error message to include rejected string format, added by
|
||||
@pbiering (gh pr #300)
|
||||
- Add support for parsing bytesarray, reported by @uckelman (gh issue #417) and
|
||||
fixed by @uckelman and @pganssle (gh pr #514)
|
||||
- Started raising a warning when the parser finds a timezone string that it
|
||||
cannot construct a tzinfo instance for (rather than succeeding with no
|
||||
indication of an error). Reported and fixed by @jbrockmendel (gh pr #540)
|
||||
- Dropped the use of assert in the parser. Fixed by @jbrockmendel (gh pr #502)
|
||||
- Fixed to assertion logic in parser to support dates like '2015-15-May',
|
||||
reported and fixed by @jbrockmendel (gh pr #409)
|
||||
- Fixed IndexError in parser on dates with trailing colons, reported and fixed
|
||||
by @jbrockmendel (gh pr #420)
|
||||
- Fixed bug where hours were not validated, leading to improper parse. Reported
|
||||
by @heappro (gh pr #353), fixed by @jbrockmendel (gh pr #482)
|
||||
- Fixed problem parsing strings in %b-%Y-%d format. Reported and fixed by
|
||||
@jbrockmendel (gh pr #481)
|
||||
- Fixed problem parsing strings in the %d%B%y format. Reported by @asishm
|
||||
(gh issue #360), fixed by @jbrockmendel (gh pr #483)
|
||||
- Fixed problem parsing certain unambiguous strings when year <99 (gh pr #510).
|
||||
Reported by @alexwlchan (gh issue #293).
|
||||
- Fixed issue with parsing an unambiguous string representation of an ambiguous
|
||||
datetime such that if possible the correct value for fold is set. Fixes
|
||||
issue reported by @JordonPhillips and @pganssle (gh issue #318, #320,
|
||||
gh pr #517)
|
||||
- Fixed issue with improper rounding of fractional components. Reported by
|
||||
@dddmello (gh issue #427), fixed by @m-dz (gh pr #570)
|
||||
- Performance improvement to parser from removing certain min() calls. Reported
|
||||
and fixed by @jbrockmendel (gh pr #589)
|
||||
- Significantly refactored parser code by @jbrockmendel (gh prs #419, #436,
|
||||
#490, #498, #539) and @pganssle (gh prs #435, #468)
|
||||
- Implementated of __hash__ for relativedelta and weekday, reported and fixed
|
||||
by @mrigor (gh pr #389)
|
||||
- Implemented __abs__ for relativedelta. Reported by @binnisb and @pferreir
|
||||
(gh issue #350, pr #472)
|
||||
- Fixed relativedelta.weeks property getter and setter to work for both
|
||||
negative and positive values. Reported and fixed by @souliane (gh issue #459,
|
||||
pr #460)
|
||||
- Fixed issue where passing whole number floats to the months or years
|
||||
arguments of the relativedelta constructor would lead to errors during
|
||||
addition. Reported by @arouanet (gh pr #411), fixed by @lkollar (gh pr #553)
|
||||
- Added a pre-built tz.UTC object representing UTC (gh pr #497)
|
||||
- Added a cache to tz.gettz so that by default it will return the same object
|
||||
for identical inputs. This will change the semantics of certain operations
|
||||
between datetimes constructed with tzinfo=tz.gettz(...). (gh pr #628)
|
||||
- Changed the behavior of tz.tzutc to return a singleton (gh pr #497, #504)
|
||||
- Changed the behavior of tz.tzoffset to return the same object when passed the
|
||||
same inputs, with a corresponding performance improvement (gh pr #504)
|
||||
- Changed the behavior of tz.tzstr to return the same object when passed the
|
||||
same inputs. (gh pr #628)
|
||||
- Added .instance alternate constructors for tz.tzoffset and tz.tzstr, to
|
||||
allow the construction of a new instance if desired. (gh pr #628)
|
||||
- Added the tz.gettz.nocache function to allow explicit retrieval of a new
|
||||
instance of the relevant tzinfo. (gh pr #628)
|
||||
- Expand definition of tz.tzlocal equality so that the local zone is allow
|
||||
equality with tzoffset and tzutc. (gh pr #598)
|
||||
- Deprecated the idiosyncratic tzstr format mentioned in several examples but
|
||||
evidently designed exclusively for dateutil, and very likely not used by
|
||||
any current users. (gh issue #595, gh pr #606)
|
||||
- Added the tz.resolve_imaginary function, which generates a real date from
|
||||
an imaginary one, if necessary. Implemented by @Cheukting (gh issue #339,
|
||||
gh pr #607)
|
||||
- Fixed issue where the tz.tzstr constructor would erroneously succeed if
|
||||
passed an invalid value for tzstr. Fixed by @pablogsal (gh issue #259,
|
||||
gh pr #581)
|
||||
- Fixed issue with tz.gettz for TZ variables that start with a colon. Reported
|
||||
and fixed by @lapointexavier (gh pr #601)
|
||||
- Added a lock to tz.tzical's cache. Reported and fixed by @Unrud (gh pr #430)
|
||||
- Fixed an issue with fold support on certain Python 3 implementations that
|
||||
used the pre-3.6 pure Python implementation of datetime.replace, most
|
||||
notably pypy3 (gh pr #446).
|
||||
- Added support for VALUE=DATE-TIME for DTSTART in rrulestr. Reported by @potuz
|
||||
(gh issue #401) and fixed by @Unrud (gh pr #429)
|
||||
- Started enforcing that within VTIMEZONE, the VALUE parameter can only be
|
||||
omitted or DATE-TIME, per RFC 5545. Reported by @Unrud (gh pr #439)
|
||||
- Added support for TZID parameter for DTSTART in rrulestr. Reported and
|
||||
fixed by @ryanpetrello (gh issue #614, gh pr #624)
|
||||
- Added 'RRULE:' prefix to rrule strings generated by rrule.__str__, in
|
||||
compliance with the RFC. Reported by @AndrewPashkin (gh issue #86), fixed by
|
||||
@jarondl and @mlorant (gh pr #450)
|
||||
- Switched to setuptools_scm for version management, automatically calculating
|
||||
a version number from the git metadata. Reported by @jreback (gh issue #511),
|
||||
implemented by @Sulley38 (gh pr #564)
|
||||
- Switched setup.py to use find_packages, and started testing against pip
|
||||
installed versions of dateutil in CI. Fixed issue with parser import
|
||||
discovered by @jreback in pandas-dev/pandas#18141. (gh issue #507, pr #509)
|
||||
- Switched test suite to using pytest (gh pr #495)
|
||||
- Switched CI over to use tox. Fixed by @gaborbernat (gh pr #549)
|
||||
- Added a test-only dependency on freezegun. (gh pr #474)
|
||||
- Reduced number of CI builds on Appveyor. Fixed by @kirit93 (gh issue #529,
|
||||
gh pr #579)
|
||||
- Made xfails strict by default, so that an xpass is a failure. (gh pr #567)
|
||||
- Added a documentation generation stage to tox and CI. (gh pr #568)
|
||||
- Added an explicit warning when running python setup.py explaining how to run
|
||||
the test suites with pytest. Fixed by @lkollar. (gh issue #544, gh pr #548)
|
||||
- Added requirements-dev.txt for test dependency management (gh pr #499, #516)
|
||||
- Fixed code coverage metrics to account for Windows builds (gh pr #526)
|
||||
- Fixed code coverage metrics to NOT count xfails. Fixed by @gaborbernat
|
||||
(gh issue #519, gh pr #563)
|
||||
- Style improvement to zoneinfo.tzfile that was confusing to static type
|
||||
checkers. Reported and fixed by @quodlibetor (gh pr #485)
|
||||
- Several unused imports were removed by @jdufresne. (gh pr #486)
|
||||
- Switched ``isinstance(*, collections.Callable)`` to callable, which is available
|
||||
on all supported Python versions. Implemented by @jdufresne (gh pr #612)
|
||||
- Added CONTRIBUTING.md (gh pr #533)
|
||||
- Added AUTHORS.md (gh pr #542)
|
||||
- Corrected setup.py metadata to reflect author vs. maintainer, (gh issue #477,
|
||||
gh pr #538)
|
||||
- Corrected README to reflect that tests are now run in pytest. Reported and
|
||||
fixed by @m-dz (gh issue #556, gh pr #557)
|
||||
- Updated all references to RFC 2445 (iCalendar) to point to RFC 5545. Fixed
|
||||
by @mariocj89 (gh issue #543, gh pr #555)
|
||||
- Corrected parse documentation to reflect proper integer offset units,
|
||||
reported and fixed by @abrugh (gh pr #458)
|
||||
- Fixed dangling parenthesis in tzoffset documentation (gh pr #461)
|
||||
- Started including the license file in wheels. Reported and fixed by
|
||||
@jdufresne (gh pr #476)
|
||||
- Indendation fixes to parser docstring by @jbrockmendel (gh pr #492)
|
||||
- Moved many examples from the "examples" documentation into their appropriate
|
||||
module documentation pages. Fixed by @Tomasz-Kluczkowski and @jakec-github
|
||||
(gh pr #558, #561)
|
||||
- Fixed documentation so that the parser.isoparse documentation displays.
|
||||
Fixed by @alexchamberlain (gh issue #545, gh pr #560)
|
||||
- Refactored build and release sections and added setup instructions to
|
||||
CONTRIBUTING. Reported and fixed by @kynan (gh pr #562)
|
||||
- Cleaned up various dead links in the documentation. (gh pr #602, #608, #618)
|
||||
|
||||
Version 2.6.1
|
||||
=============
|
||||
- Updated zoneinfo file to 2017b. (gh pr #395)
|
||||
- Added Python 3.6 to CI testing (gh pr #365)
|
||||
- Removed duplicate test name that was preventing a test from being run.
|
||||
Reported and fixed by @jdufresne (gh pr #371)
|
||||
- Fixed testing of folds and gaps, particularly on Windows (gh pr #392)
|
||||
- Fixed deprecated escape characters in regular expressions. Reported by
|
||||
@nascheme and @thierryba (gh issue #361), fixed by @thierryba (gh pr #358)
|
||||
- Many PEP8 style violations and other code smells were fixed by @jdufresne
|
||||
(gh prs #358, #363, #364, #366, #367, #368, #372, #374, #379, #380, #398)
|
||||
- Improved performance of tzutc and tzoffset objects. (gh pr #391)
|
||||
- Fixed issue with several time zone classes around DST transitions in any
|
||||
zones with +0 standard offset (e.g. Europe/London) (gh issue #321, pr #390)
|
||||
- Fixed issue with fuzzy parsing where tokens similar to AM/PM that are in the
|
||||
end skipped were dropped in the fuzzy_with_tokens list. Reported and fixed
|
||||
by @jbrockmendel (gh pr #332).
|
||||
- Fixed issue with parsing dates of the form X m YY. Reported by @jbrockmendel.
|
||||
(gh issue #333, pr #393)
|
||||
- Added support for parser weekdays with less than 3 characters. Reported by
|
||||
@arcadefoam (gh issue #343), fixed by @jonemo (gh pr #382)
|
||||
- Fixed issue with the addition and subtraction of certain relativedeltas.
|
||||
Reported and fixed by @kootenpv (gh issue #346, pr #347)
|
||||
- Fixed issue where the COUNT parameter of rrules was ignored if 0. Fixed by
|
||||
@mshenfield (gh pr #330), reported by @vaultah (gh issue #329).
|
||||
- Updated documentation to include the new tz methods. (gh pr #324)
|
||||
- Update documentation to reflect that the parser can raise TypeError, reported
|
||||
and fixed by @tomchuk (gh issue #336, pr #337)
|
||||
- Fixed an incorrect year in a parser doctest. Fixed by @xlotlu (gh pr #357)
|
||||
- Moved version information into _version.py and set up the versions more
|
||||
granularly.
|
||||
|
||||
Version 2.6.0
|
||||
=============
|
||||
- Added PEP-495-compatible methods to address ambiguous and imaginary dates in
|
||||
time zones in a backwards-compatible way. Ambiguous dates and times can now
|
||||
be safely represented by all dateutil time zones. Many thanks to Alexander
|
||||
Belopolski (@abalkin) and Tim Peters @tim-one for their inputs on how to
|
||||
address this. Original issues reported by Yupeng and @zed (lP: 1390262,
|
||||
gh issues #57, #112, #249, #284, #286, prs #127, #225, #248, #264, #302).
|
||||
- Added new methods for working with ambiguous and imaginary dates to the tz
|
||||
module. datetime_ambiguous() determines if a datetime is ambiguous for a given
|
||||
zone and datetime_exists() determines if a datetime exists in a given zone.
|
||||
This works for all fold-aware datetimes, not just those provided by dateutil.
|
||||
(gh issue #253, gh pr #302)
|
||||
- Fixed an issue where dst() in Portugal in 1996 was returning the wrong value
|
||||
in tz.tzfile objects. Reported by @abalkin (gh issue #128, pr #225)
|
||||
- Fixed an issue where zoneinfo.ZoneInfoFile errors were not being properly
|
||||
deep-copied. (gh issue #226, pr #225)
|
||||
- Refactored tzwin and tzrange as a subclass of a common class, tzrangebase, as
|
||||
there was substantial overlapping functionality. As part of this change,
|
||||
tzrange and tzstr now expose a transitions() function, which returns the
|
||||
DST on and off transitions for a given year. (gh issue #260, pr #302)
|
||||
- Deprecated zoneinfo.gettz() due to confusion with tz.gettz(), in favor of
|
||||
get() method of zoneinfo.ZoneInfoFile objects. (gh issue #11, pr #310)
|
||||
- For non-character, non-stream arguments, parser.parse now raises TypeError
|
||||
instead of AttributeError. (gh issues #171, #269, pr #247)
|
||||
- Fixed an issue where tzfile objects were not properly handling dst() and
|
||||
tzname() when attached to datetime.time objects. Reported by @ovacephaloid.
|
||||
(gh issue #292, pr #309)
|
||||
- /usr/share/lib/zoneinfo was added to TZPATHS for compatibility with Solaris
|
||||
systems. Reported by @dhduvall (gh issue #276, pr #307)
|
||||
- tzoffset and tzrange objects now accept either a number of seconds or a
|
||||
datetime.timedelta() object wherever previously only a number of seconds was
|
||||
allowed. (gh pr #264, #277)
|
||||
- datetime.timedelta objects can now be added to relativedelta objects. Reported
|
||||
and added by Alec Nikolas Reiter (@justanr) (gh issue #282, pr #283
|
||||
- Refactored relativedelta.weekday and rrule.weekday into a common base class
|
||||
to reduce code duplication. (gh issue #140, pr #311)
|
||||
- An issue where the WKST parameter was improperly rendering in str(rrule) was
|
||||
reported and fixed by Daniel LePage (@dplepage). (gh issue #262, pr #263)
|
||||
- A replace() method has been added to rrule objects by @jendas1, which creates
|
||||
new rrule with modified attributes, analogous to datetime.replace (gh pr #167)
|
||||
- Made some significant performance improvements to rrule objects in Python 2.x
|
||||
(gh pr #245)
|
||||
- All classes defining equality functions now return NotImplemented when
|
||||
compared to unsupported classes, rather than raising TypeError, to allow other
|
||||
classes to provide fallback support. (gh pr #236)
|
||||
- Several classes have been marked as explicitly unhashable to maintain
|
||||
identical behavior between Python 2 and 3. Submitted by Roy Williams
|
||||
(@rowillia) (gh pr #296)
|
||||
- Trailing whitespace in easter.py has been removed. Submitted by @OmgImAlexis
|
||||
(gh pr #299)
|
||||
- Windows-only batch files in build scripts had line endings switched to CRLF.
|
||||
(gh pr #237)
|
||||
- @adamchainz updated the documentation links to reflect that the canonical
|
||||
location for readthedocs links is now at .io, not .org. (gh pr #272)
|
||||
- Made some changes to the CI and codecov to test against newer versions of
|
||||
Python and pypy, and to adjust the code coverage requirements. For the moment,
|
||||
full pypy3 compatibility is not supported until a new release is available,
|
||||
due to upstream bugs in the old version affecting PEP-495 support.
|
||||
(gh prs #265, #266, #304, #308)
|
||||
- The full PGP signing key fingerprint was added to the README.md in favor of
|
||||
the previously used long-id. Reported by @valholl (gh issue #287, pr #304)
|
||||
- Updated zoneinfo to 2016i. (gh issue #298, gh pr #306)
|
||||
|
||||
|
||||
Version 2.5.3
|
||||
=============
|
||||
- Updated zoneinfo to 2016d
|
||||
- Fixed parser bug where unambiguous datetimes fail to parse when dayfirst is
|
||||
set to true. (gh issue #233, pr #234)
|
||||
- Bug in zoneinfo file on platforms such as Google App Engine which do not
|
||||
do not allow importing of subprocess.check_call was reported and fixed by
|
||||
@savraj (gh issue #239, gh pr #240)
|
||||
- Fixed incorrect version in documentation (gh issue #235, pr #243)
|
||||
|
||||
Version 2.5.2
|
||||
=============
|
||||
- Updated zoneinfo to 2016c
|
||||
- Fixed parser bug where yearfirst and dayfirst parameters were not being
|
||||
respected when no separator was present. (gh issue #81 and #217, pr #229)
|
||||
|
||||
Version 2.5.1
|
||||
=============
|
||||
- Updated zoneinfo to 2016b
|
||||
- Changed MANIFEST.in to explicitly include test suite in source distributions,
|
||||
with help from @koobs (gh issue #193, pr #194, #201, #221)
|
||||
- Explicitly set all line-endings to LF, except for the NEWS file, on a
|
||||
per-repository basis (gh pr #218)
|
||||
- Fixed an issue with improper caching behavior in rruleset objects (gh issue
|
||||
#104, pr #207)
|
||||
- Changed to an explicit error when rrulestr strings contain a missing BYDAY
|
||||
(gh issue #162, pr #211)
|
||||
- tzfile now correctly handles files containing leapcnt (although the leapcnt
|
||||
information is not actually used). Contributed by @hjoukl (gh issue #146, pr
|
||||
#147)
|
||||
- Fixed recursive import issue with tz module (gh pr #204)
|
||||
- Added compatibility between tzwin objects and datetime.time objects (gh issue
|
||||
#216, gh pr #219)
|
||||
- Refactored monolithic test suite by module (gh issue #61, pr #200 and #206)
|
||||
- Improved test coverage in the relativedelta module (gh pr #215)
|
||||
- Adjusted documentation to reflect possibly counter-intuitive properties of
|
||||
RFC-5545-compliant rrules, and other documentation improvements in the rrule
|
||||
module (gh issue #105, gh issue #149 - pointer to the solution by @phep,
|
||||
pr #213).
|
||||
|
||||
|
||||
Version 2.5.0
|
||||
=============
|
||||
- Updated zoneinfo to 2016a
|
||||
- zoneinfo_metadata file version increased to 2.0 - the updated updatezinfo.py
|
||||
script will work with older zoneinfo_metadata.json files, but new metadata
|
||||
files will not work with older updatezinfo.py versions. Additionally, we have
|
||||
started hosting our own mirror of the Olson databases on a github pages
|
||||
site (https://dateutil.github.io/tzdata/) (gh pr #183)
|
||||
- dateutil zoneinfo tarballs now contain the full zoneinfo_metadata file used
|
||||
to generate them. (gh issue #27, gh pr #85)
|
||||
- relativedelta can now be safely subclassed without derived objects reverting
|
||||
to base relativedelta objects as a result of arithmetic operations.
|
||||
(lp:1010199, gh issue #44, pr #49)
|
||||
- relativedelta 'weeks' parameter can now be set and retrieved as a property of
|
||||
relativedelta instances. (lp: 727525, gh issue #45, pr #49)
|
||||
- relativedelta now explicitly supports fractional relative weeks, days, hours,
|
||||
minutes and seconds. Fractional values in absolute parameters (year, day, etc)
|
||||
are now deprecated. (gh issue #40, pr #190)
|
||||
- relativedelta objects previously did not use microseconds to determine of two
|
||||
relativedelta objects were equal. This oversight has been corrected.
|
||||
Contributed by @elprans (gh pr #113)
|
||||
- rrule now has an xafter() method for retrieving multiple recurrences after a
|
||||
specified date. (gh pr #38)
|
||||
- str(rrule) now returns an RFC2445-compliant rrule string, contributed by
|
||||
@schinckel and @armicron (lp:1406305, gh issue #47, prs #50, #62 and #160)
|
||||
- rrule performance under certain conditions has been significantly improved
|
||||
thanks to a patch contributed by @dekoza, based on an article by Brian Beck
|
||||
(@exogen) (gh pr #136)
|
||||
- The use of both the 'until' and 'count' parameters is now deprecated as
|
||||
inconsistent with RFC2445 (gh pr #62, #185)
|
||||
- Parsing an empty string will now raise a ValueError, rather than returning the
|
||||
datetime passed to the 'default' parameter. (gh issue #78, pr #187)
|
||||
- tzwinlocal objects now have a meaningful repr() and str() implementation
|
||||
(gh issue #148, prs #184 and #186)
|
||||
- Added equality logic for tzwin and tzwinlocal objects. (gh issue #151,
|
||||
pr #180, #184)
|
||||
- Added some flexibility in subclassing timelex, and switched the default
|
||||
behavior over to using string methods rather than comparing against a fixed
|
||||
list. (gh pr #122, #139)
|
||||
- An issue causing tzstr() to crash on Python 2.x was fixed. (lp: 1331576,
|
||||
gh issue #51, pr #55)
|
||||
- An issue with string encoding causing exceptions under certain circumstances
|
||||
when tzname() is called was fixed. (gh issue #60, #74, pr #75)
|
||||
- Parser issue where calling parse() on dates with no day specified when the
|
||||
day of the month in the default datetime (which is "today" if unspecified) is
|
||||
greater than the number of days in the parsed month was fixed (this issue
|
||||
tended to crop up between the 29th and 31st of the month, for obvious reasons)
|
||||
(canonical gh issue #25, pr #30, #191)
|
||||
- Fixed parser issue causing fuzzy_with_tokens to raise an unexpected exception
|
||||
in certain circumstances. Contributed by @MichaelAquilina (gh pr #91)
|
||||
- Fixed parser issue where years > 100 AD were incorrectly parsed. Contributed
|
||||
by @Bachmann1234 (gh pr #130)
|
||||
- Fixed parser issue where commas were not a valid separator between seconds
|
||||
and microseconds, preventing parsing of ISO 8601 dates. Contributed by
|
||||
@ryanss (gh issue #28, pr #106)
|
||||
- Fixed issue with tzwin encoding in locales with non-Latin alphabets
|
||||
(gh issue #92, pr #98)
|
||||
- Fixed an issue where tzwin was not being properly imported on Windows.
|
||||
Contributed by @labrys. (gh pr #134)
|
||||
- Fixed a problem causing issues importing zoneinfo in certain circumstances.
|
||||
Issue and solution contributed by @alexxv (gh issue #97, pr #99)
|
||||
- Fixed an issue where dateutil timezones were not compatible with basic time
|
||||
objects. One of many, many timezone related issues contributed and tested by
|
||||
@labrys. (gh issue #132, pr #181)
|
||||
- Fixed issue where tzwinlocal had an invalid utcoffset. (gh issue #135,
|
||||
pr #141, #142)
|
||||
- Fixed issue with tzwin and tzwinlocal where DST transitions were incorrectly
|
||||
parsed from the registry. (gh issue #143, pr #178)
|
||||
- updatezinfo.py no longer suppresses certain OSErrors. Contributed by @bjamesv
|
||||
(gh pr #164)
|
||||
- An issue that arose when timezone locale changes during runtime has been
|
||||
fixed by @carlosxl and @mjschultz (gh issue #100, prs #107, #109)
|
||||
- Python 3.5 was added to the supported platforms in the metadata (@tacaswell
|
||||
gh pr #159) and the test suites (@moreati gh pr #117).
|
||||
- An issue with tox failing without unittest2 installed in Python 2.6 was fixed
|
||||
by @moreati (gh pr #115)
|
||||
- Several deprecated functions were replaced in the tests by @moreati
|
||||
(gh pr #116)
|
||||
- Improved the logic in Travis and Appveyor to alleviate issues where builds
|
||||
were failing due to connection issues when downloading the IANA timezone
|
||||
files. In addition to adding our own mirror for the files (gh pr #183), the
|
||||
download is now retried a number of times (with a delay) (gh pr #177)
|
||||
- Many failing doctests were fixed by @moreati. (gh pr #120)
|
||||
- Many fixes to the documentation (gh pr #103, gh pr #87 from @radarhere,
|
||||
gh pr #154 from @gpoesia, gh pr #156 from @awsum, gh pr #168 from @ja8zyjits)
|
||||
- Added a code coverage tool to the CI to help improve the library. (gh pr #182)
|
||||
- We now have a mailing list - dateutil@python.org, graciously hosted by
|
||||
Python.org.
|
||||
|
||||
|
||||
Version 2.4.2
|
||||
=============
|
||||
- Updated zoneinfo to 2015b.
|
||||
- Fixed issue with parsing of tzstr on Python 2.7.x; tzstr will now be decoded
|
||||
if not a unicode type. gh #51 (lp:1331576), gh pr #55.
|
||||
- Fix a parser issue where AM and PM tokens were showing up in fuzzy date
|
||||
stamps, triggering inappropriate errors. gh #56 (lp: 1428895), gh pr #63.
|
||||
- Missing function "setcachesize" removed from zoneinfo __all__ list by @ryanss,
|
||||
fixing an issue with wildcard imports of dateutil.zoneinfo. (gh pr #66).
|
||||
- (PyPI only) Fix an issue with source distributions not including the test
|
||||
suite.
|
||||
|
||||
|
||||
Version 2.4.1
|
||||
=============
|
||||
|
||||
- Added explicit check for valid hours if AM/PM is specified in parser.
|
||||
(gh pr #22, issue #21)
|
||||
- Fix bug in rrule introduced in 2.4.0 where byweekday parameter was not
|
||||
handled properly. (gh pr #35, issue #34)
|
||||
- Fix error where parser allowed some invalid dates, overwriting existing hours
|
||||
with the last 2-digit number in the string. (gh pr #32, issue #31)
|
||||
- Fix and add test for Python 2.x compatibility with boolean checking of
|
||||
relativedelta objects. Implemented by @nimasmi (gh pr #43) and Cédric Krier
|
||||
(lp: 1035038)
|
||||
- Replaced parse() calls with explicit datetime objects in unit tests unrelated
|
||||
to parser. (gh pr #36)
|
||||
- Changed private _byxxx from sets to sorted tuples and fixed one currently
|
||||
unreachable bug in _construct_byset. (gh pr #54)
|
||||
- Additional documentation for parser (gh pr #29, #33, #41) and rrule.
|
||||
- Formatting fixes to documentation of rrule and README.rst.
|
||||
- Updated zoneinfo to 2015a.
|
||||
|
||||
Version 2.4.0
|
||||
=============
|
||||
|
||||
- Fix an issue with relativedelta and freezegun (lp:1374022)
|
||||
- Fix tzinfo in windows for timezones without dst (lp:1010050, gh #2)
|
||||
- Ignore missing timezones in windows like in POSIX
|
||||
- Fix minimal version requirement for six (gh #6)
|
||||
- Many rrule changes and fixes by @pganssle (gh pull requests #13 #14 #17),
|
||||
including defusing some infinite loops (gh #4)
|
||||
|
||||
Version 2.3
|
||||
===========
|
||||
|
||||
- Cleanup directory structure, moved test.py to dateutil/tests/test.py
|
||||
|
||||
- Changed many aspects of dealing with the zone info file. Instead of a cache,
|
||||
all the zones are loaded to memory, but symbolic links are loaded only once,
|
||||
so not much memory is used.
|
||||
|
||||
- The package is now zip-safe, and universal-wheelable, thanks to changes in
|
||||
the handling of the zoneinfo file.
|
||||
|
||||
- Fixed tzwin silently not imported on windows python2
|
||||
|
||||
- New maintainer, together with new hosting: GitHub, Travis, Read-The-Docs
|
||||
|
||||
Version 2.2
|
||||
===========
|
||||
|
||||
- Updated zoneinfo to 2013h
|
||||
|
||||
- fuzzy_with_tokens parse addon from Christopher Corley
|
||||
|
||||
- Bug with LANG=C fixed by Mike Gilbert
|
||||
|
||||
Version 2.1
|
||||
===========
|
||||
|
||||
- New maintainer
|
||||
|
||||
- Dateutil now works on Python 2.6, 2.7 and 3.2 from same codebase (with six)
|
||||
|
||||
- #704047: Ismael Carnales' patch for a new time format
|
||||
|
||||
- Small bug fixes, thanks for reporters!
|
||||
|
||||
|
||||
Version 2.0
|
||||
===========
|
||||
|
||||
- Ported to Python 3, by Brian Jones. If you need dateutil for Python 2.X,
|
||||
please continue using the 1.X series.
|
||||
|
||||
- There's no such thing as a "PSF License". This source code is now
|
||||
made available under the Simplified BSD license. See LICENSE for
|
||||
details.
|
||||
|
||||
Version 1.5
|
||||
===========
|
||||
|
||||
- As reported by Mathieu Bridon, rrules were matching the bysecond rules
|
||||
incorrectly against byminute in some circumstances when the SECONDLY
|
||||
frequency was in use, due to a copy & paste bug. The problem has been
|
||||
unittested and corrected.
|
||||
|
||||
- Adam Ryan reported a problem in the relativedelta implementation which
|
||||
affected the yearday parameter in the month of January specifically.
|
||||
This has been unittested and fixed.
|
||||
|
||||
- Updated timezone information.
|
||||
|
||||
|
||||
Version 1.4.1
|
||||
=============
|
||||
|
||||
- Updated timezone information.
|
||||
|
||||
|
||||
Version 1.4
|
||||
===========
|
||||
|
||||
- Fixed another parser precision problem on conversion of decimal seconds
|
||||
to microseconds, as reported by Erik Brown. Now these issues are gone
|
||||
for real since it's not using floating point arithmetic anymore.
|
||||
|
||||
- Fixed case where tzrange.utcoffset and tzrange.dst() might fail due
|
||||
to a date being used where a datetime was expected (reported and fixed
|
||||
by Lennart Regebro).
|
||||
|
||||
- Prevent tzstr from introducing daylight timings in strings that didn't
|
||||
specify them (reported by Lennart Regebro).
|
||||
|
||||
- Calls like gettz("GMT+3") and gettz("UTC-2") will now return the
|
||||
expected values, instead of the TZ variable behavior.
|
||||
|
||||
- Fixed DST signal handling in zoneinfo files. Reported by
|
||||
Nicholas F. Fabry and John-Mark Gurney.
|
||||
|
||||
|
||||
Version 1.3
|
||||
===========
|
||||
|
||||
- Fixed precision problem on conversion of decimal seconds to
|
||||
microseconds, as reported by Skip Montanaro.
|
||||
|
||||
- Fixed bug in constructor of parser, and converted parser classes to
|
||||
new-style classes. Original report and patch by Michael Elsdörfer.
|
||||
|
||||
- Initialize tzid and comps in tz.py, to prevent the code from ever
|
||||
raising a NameError (even with broken files). Johan Dahlin suggested
|
||||
the fix after a pyflakes run.
|
||||
|
||||
- Version is now published in dateutil.__version__, as requested
|
||||
by Darren Dale.
|
||||
|
||||
- All code is compatible with new-style division.
|
||||
|
||||
|
||||
Version 1.2
|
||||
===========
|
||||
|
||||
- Now tzfile will round timezones to full-minutes if necessary,
|
||||
since Python's datetime doesn't support sub-minute offsets.
|
||||
Thanks to Ilpo Nyyssönen for reporting the issue.
|
||||
|
||||
- Removed bare string exceptions, as reported and fixed by
|
||||
Wilfredo Sánchez Vega.
|
||||
|
||||
- Fix bug in leap count parsing (reported and fixed by Eugene Oden).
|
||||
|
||||
|
||||
Version 1.1
|
||||
===========
|
||||
|
||||
- Fixed rrule byyearday handling. Abramo Bagnara pointed out that
|
||||
RFC2445 allows negative numbers.
|
||||
|
||||
- Fixed --prefix handling in setup.py (by Sidnei da Silva).
|
||||
|
||||
- Now tz.gettz() returns a tzlocal instance when not given any
|
||||
arguments and no other timezone information is found.
|
||||
|
||||
- Updating timezone information to version 2005q.
|
||||
|
||||
|
||||
Version 1.0
|
||||
===========
|
||||
|
||||
- Fixed parsing of XXhXXm formatted time after day/month/year
|
||||
has been parsed.
|
||||
|
||||
- Added patch by Jeffrey Harris optimizing rrule.__contains__.
|
||||
|
||||
|
||||
Version 0.9
|
||||
===========
|
||||
|
||||
- Fixed pickling of timezone types, as reported by
|
||||
Andreas Köhler.
|
||||
|
||||
- Implemented internal timezone information with binary
|
||||
timezone files. datautil.tz.gettz() function will now
|
||||
try to use the system timezone files, and fallback to
|
||||
the internal versions. It's also possible to ask for
|
||||
the internal versions directly by using
|
||||
dateutil.zoneinfo.gettz().
|
||||
|
||||
- New tzwin timezone type, allowing access to Windows
|
||||
internal timezones (contributed by Jeffrey Harris).
|
||||
|
||||
- Fixed parsing of unicode date strings.
|
||||
|
||||
- Accept parserinfo instances as the parser constructor
|
||||
parameter, besides parserinfo (sub)classes.
|
||||
|
||||
- Changed weekday to spell the not-set n value as None
|
||||
instead of 0.
|
||||
|
||||
- Fixed other reported bugs.
|
||||
|
||||
|
||||
Version 0.5
|
||||
===========
|
||||
|
||||
- Removed ``FREQ_`` prefix from rrule frequency constants
|
||||
WARNING: this breaks compatibility with previous versions.
|
||||
|
||||
- Fixed rrule.between() for cases where "after" is achieved
|
||||
before even starting, as reported by Andreas Köhler.
|
||||
|
||||
- Fixed two digit zero-year parsing (such as 31-Dec-00), as
|
||||
reported by Jim Abramson, and included test case for this.
|
||||
|
||||
- Sort exdate and rdate before iterating over them, so that
|
||||
it's not necessary to sort them before adding to the rruleset,
|
||||
as reported by Nicholas Piper.
|
158
resources/lib/libraries/dateutil/README.rst
Normal file
158
resources/lib/libraries/dateutil/README.rst
Normal file
|
@ -0,0 +1,158 @@
|
|||
dateutil - powerful extensions to datetime
|
||||
==========================================
|
||||
|
||||
|pypi| |support| |licence|
|
||||
|
||||
|gitter| |readthedocs|
|
||||
|
||||
|travis| |appveyor| |coverage|
|
||||
|
||||
.. |pypi| image:: https://img.shields.io/pypi/v/python-dateutil.svg?style=flat-square
|
||||
:target: https://pypi.org/project/python-dateutil/
|
||||
:alt: pypi version
|
||||
|
||||
.. |support| image:: https://img.shields.io/pypi/pyversions/python-dateutil.svg?style=flat-square
|
||||
:target: https://pypi.org/project/python-dateutil/
|
||||
:alt: supported Python version
|
||||
|
||||
.. |travis| image:: https://img.shields.io/travis/dateutil/dateutil/master.svg?style=flat-square&label=Travis%20Build
|
||||
:target: https://travis-ci.org/dateutil/dateutil
|
||||
:alt: travis build status
|
||||
|
||||
.. |appveyor| image:: https://img.shields.io/appveyor/ci/dateutil/dateutil/master.svg?style=flat-square&logo=appveyor
|
||||
:target: https://ci.appveyor.com/project/dateutil/dateutil
|
||||
:alt: appveyor build status
|
||||
|
||||
.. |coverage| image:: https://codecov.io/github/dateutil/dateutil/coverage.svg?branch=master
|
||||
:target: https://codecov.io/github/dateutil/dateutil?branch=master
|
||||
:alt: Code coverage
|
||||
|
||||
.. |gitter| image:: https://badges.gitter.im/dateutil/dateutil.svg
|
||||
:alt: Join the chat at https://gitter.im/dateutil/dateutil
|
||||
:target: https://gitter.im/dateutil/dateutil
|
||||
|
||||
.. |licence| image:: https://img.shields.io/pypi/l/python-dateutil.svg?style=flat-square
|
||||
:target: https://pypi.org/project/python-dateutil/
|
||||
:alt: licence
|
||||
|
||||
.. |readthedocs| image:: https://img.shields.io/readthedocs/dateutil/latest.svg?style=flat-square&label=Read%20the%20Docs
|
||||
:alt: Read the documentation at https://dateutil.readthedocs.io/en/latest/
|
||||
:target: https://dateutil.readthedocs.io/en/latest/
|
||||
|
||||
The `dateutil` module provides powerful extensions to
|
||||
the standard `datetime` module, available in Python.
|
||||
|
||||
|
||||
Download
|
||||
========
|
||||
dateutil is available on PyPI
|
||||
https://pypi.org/project/python-dateutil/
|
||||
|
||||
The documentation is hosted at:
|
||||
https://dateutil.readthedocs.io/en/stable/
|
||||
|
||||
Code
|
||||
====
|
||||
The code and issue tracker are hosted on Github:
|
||||
https://github.com/dateutil/dateutil/
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
* Computing of relative deltas (next month, next year,
|
||||
next monday, last week of month, etc);
|
||||
* Computing of relative deltas between two given
|
||||
date and/or datetime objects;
|
||||
* Computing of dates based on very flexible recurrence rules,
|
||||
using a superset of the `iCalendar <https://www.ietf.org/rfc/rfc2445.txt>`_
|
||||
specification. Parsing of RFC strings is supported as well.
|
||||
* Generic parsing of dates in almost any string format;
|
||||
* Timezone (tzinfo) implementations for tzfile(5) format
|
||||
files (/etc/localtime, /usr/share/zoneinfo, etc), TZ
|
||||
environment string (in all known formats), iCalendar
|
||||
format files, given ranges (with help from relative deltas),
|
||||
local machine timezone, fixed offset timezone, UTC timezone,
|
||||
and Windows registry-based time zones.
|
||||
* Internal up-to-date world timezone information based on
|
||||
Olson's database.
|
||||
* Computing of Easter Sunday dates for any given year,
|
||||
using Western, Orthodox or Julian algorithms;
|
||||
* A comprehensive test suite.
|
||||
|
||||
Quick example
|
||||
=============
|
||||
Here's a snapshot, just to give an idea about the power of the
|
||||
package. For more examples, look at the documentation.
|
||||
|
||||
Suppose you want to know how much time is left, in
|
||||
years/months/days/etc, before the next easter happening on a
|
||||
year with a Friday 13th in August, and you want to get today's
|
||||
date out of the "date" unix system command. Here is the code:
|
||||
|
||||
.. doctest:: readmeexample
|
||||
|
||||
>>> from dateutil.relativedelta import *
|
||||
>>> from dateutil.easter import *
|
||||
>>> from dateutil.rrule import *
|
||||
>>> from dateutil.parser import *
|
||||
>>> from datetime import *
|
||||
>>> now = parse("Sat Oct 11 17:13:46 UTC 2003")
|
||||
>>> today = now.date()
|
||||
>>> year = rrule(YEARLY,dtstart=now,bymonth=8,bymonthday=13,byweekday=FR)[0].year
|
||||
>>> rdelta = relativedelta(easter(year), today)
|
||||
>>> print("Today is: %s" % today)
|
||||
Today is: 2003-10-11
|
||||
>>> print("Year with next Aug 13th on a Friday is: %s" % year)
|
||||
Year with next Aug 13th on a Friday is: 2004
|
||||
>>> print("How far is the Easter of that year: %s" % rdelta)
|
||||
How far is the Easter of that year: relativedelta(months=+6)
|
||||
>>> print("And the Easter of that year is: %s" % (today+rdelta))
|
||||
And the Easter of that year is: 2004-04-11
|
||||
|
||||
Being exactly 6 months ahead was **really** a coincidence :)
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
We welcome many types of contributions - bug reports, pull requests (code, infrastructure or documentation fixes). For more information about how to contribute to the project, see the ``CONTRIBUTING.md`` file in the repository.
|
||||
|
||||
|
||||
Author
|
||||
======
|
||||
The dateutil module was written by Gustavo Niemeyer <gustavo@niemeyer.net>
|
||||
in 2003.
|
||||
|
||||
It is maintained by:
|
||||
|
||||
* Gustavo Niemeyer <gustavo@niemeyer.net> 2003-2011
|
||||
* Tomi Pieviläinen <tomi.pievilainen@iki.fi> 2012-2014
|
||||
* Yaron de Leeuw <me@jarondl.net> 2014-2016
|
||||
* Paul Ganssle <paul@ganssle.io> 2015-
|
||||
|
||||
Starting with version 2.4.1, all source and binary distributions will be signed
|
||||
by a PGP key that has, at the very least, been signed by the key which made the
|
||||
previous release. A table of release signing keys can be found below:
|
||||
|
||||
=========== ============================
|
||||
Releases Signing key fingerprint
|
||||
=========== ============================
|
||||
2.4.1- `6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB`_ (|pgp_mirror|_)
|
||||
=========== ============================
|
||||
|
||||
|
||||
Contact
|
||||
=======
|
||||
Our mailing list is available at `dateutil@python.org <https://mail.python.org/mailman/listinfo/dateutil>`_. As it is hosted by the PSF, it is subject to the `PSF code of
|
||||
conduct <https://www.python.org/psf/codeofconduct/>`_.
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
All contributions after December 1, 2017 released under dual license - either `Apache 2.0 License <https://www.apache.org/licenses/LICENSE-2.0>`_ or the `BSD 3-Clause License <https://opensource.org/licenses/BSD-3-Clause>`_. Contributions before December 1, 2017 - except those those explicitly relicensed - are released only under the BSD 3-Clause License.
|
||||
|
||||
|
||||
.. _6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB:
|
||||
https://pgp.mit.edu/pks/lookup?op=vindex&search=0xCD54FCE3D964BEFB
|
||||
|
||||
.. |pgp_mirror| replace:: mirror
|
||||
.. _pgp_mirror: https://sks-keyservers.net/pks/lookup?op=vindex&search=0xCD54FCE3D964BEFB
|
8
resources/lib/libraries/dateutil/__init__.py
Normal file
8
resources/lib/libraries/dateutil/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
try:
|
||||
from ._version import version as __version__
|
||||
except ImportError:
|
||||
__version__ = 'unknown'
|
||||
|
||||
__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz',
|
||||
'utils', 'zoneinfo']
|
43
resources/lib/libraries/dateutil/_common.py
Normal file
43
resources/lib/libraries/dateutil/_common.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
"""
|
||||
Common code used in multiple modules.
|
||||
"""
|
||||
|
||||
|
||||
class weekday(object):
|
||||
__slots__ = ["weekday", "n"]
|
||||
|
||||
def __init__(self, weekday, n=None):
|
||||
self.weekday = weekday
|
||||
self.n = n
|
||||
|
||||
def __call__(self, n):
|
||||
if n == self.n:
|
||||
return self
|
||||
else:
|
||||
return self.__class__(self.weekday, n)
|
||||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
if self.weekday != other.weekday or self.n != other.n:
|
||||
return False
|
||||
except AttributeError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def __hash__(self):
|
||||
return hash((
|
||||
self.weekday,
|
||||
self.n,
|
||||
))
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def __repr__(self):
|
||||
s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday]
|
||||
if not self.n:
|
||||
return s
|
||||
else:
|
||||
return "%s(%+d)" % (s, self.n)
|
||||
|
||||
# vim:ts=4:sw=4:et
|
89
resources/lib/libraries/dateutil/easter.py
Normal file
89
resources/lib/libraries/dateutil/easter.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This module offers a generic easter computing method for any given year, using
|
||||
Western, Orthodox or Julian algorithms.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
||||
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
|
||||
|
||||
EASTER_JULIAN = 1
|
||||
EASTER_ORTHODOX = 2
|
||||
EASTER_WESTERN = 3
|
||||
|
||||
|
||||
def easter(year, method=EASTER_WESTERN):
|
||||
"""
|
||||
This method was ported from the work done by GM Arts,
|
||||
on top of the algorithm by Claus Tondering, which was
|
||||
based in part on the algorithm of Ouding (1940), as
|
||||
quoted in "Explanatory Supplement to the Astronomical
|
||||
Almanac", P. Kenneth Seidelmann, editor.
|
||||
|
||||
This algorithm implements three different easter
|
||||
calculation methods:
|
||||
|
||||
1 - Original calculation in Julian calendar, valid in
|
||||
dates after 326 AD
|
||||
2 - Original method, with date converted to Gregorian
|
||||
calendar, valid in years 1583 to 4099
|
||||
3 - Revised method, in Gregorian calendar, valid in
|
||||
years 1583 to 4099 as well
|
||||
|
||||
These methods are represented by the constants:
|
||||
|
||||
* ``EASTER_JULIAN = 1``
|
||||
* ``EASTER_ORTHODOX = 2``
|
||||
* ``EASTER_WESTERN = 3``
|
||||
|
||||
The default method is method 3.
|
||||
|
||||
More about the algorithm may be found at:
|
||||
|
||||
`GM Arts: Easter Algorithms <http://www.gmarts.org/index.php?go=415>`_
|
||||
|
||||
and
|
||||
|
||||
`The Calendar FAQ: Easter <https://www.tondering.dk/claus/cal/easter.php>`_
|
||||
|
||||
"""
|
||||
|
||||
if not (1 <= method <= 3):
|
||||
raise ValueError("invalid method")
|
||||
|
||||
# g - Golden year - 1
|
||||
# c - Century
|
||||
# h - (23 - Epact) mod 30
|
||||
# i - Number of days from March 21 to Paschal Full Moon
|
||||
# j - Weekday for PFM (0=Sunday, etc)
|
||||
# p - Number of days from March 21 to Sunday on or before PFM
|
||||
# (-6 to 28 methods 1 & 3, to 56 for method 2)
|
||||
# e - Extra days to add for method 2 (converting Julian
|
||||
# date to Gregorian date)
|
||||
|
||||
y = year
|
||||
g = y % 19
|
||||
e = 0
|
||||
if method < 3:
|
||||
# Old method
|
||||
i = (19*g + 15) % 30
|
||||
j = (y + y//4 + i) % 7
|
||||
if method == 2:
|
||||
# Extra dates to convert Julian to Gregorian date
|
||||
e = 10
|
||||
if y > 1600:
|
||||
e = e + y//100 - 16 - (y//100 - 16)//4
|
||||
else:
|
||||
# New method
|
||||
c = y//100
|
||||
h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30
|
||||
i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11))
|
||||
j = (y + y//4 + i + 2 - c + c//4) % 7
|
||||
|
||||
# p can be from -6 to 56 corresponding to dates 22 March to 23 May
|
||||
# (later dates apply to method 2, although 23 May never actually occurs)
|
||||
p = i - j + e
|
||||
d = 1 + (p + 27 + (p + 6)//40) % 31
|
||||
m = 3 + (p + 26)//30
|
||||
return datetime.date(int(y), int(m), int(d))
|
60
resources/lib/libraries/dateutil/parser/__init__.py
Normal file
60
resources/lib/libraries/dateutil/parser/__init__.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from ._parser import parse, parser, parserinfo
|
||||
from ._parser import DEFAULTPARSER, DEFAULTTZPARSER
|
||||
from ._parser import UnknownTimezoneWarning
|
||||
|
||||
from ._parser import __doc__
|
||||
|
||||
from .isoparser import isoparser, isoparse
|
||||
|
||||
__all__ = ['parse', 'parser', 'parserinfo',
|
||||
'isoparse', 'isoparser',
|
||||
'UnknownTimezoneWarning']
|
||||
|
||||
|
||||
###
|
||||
# Deprecate portions of the private interface so that downstream code that
|
||||
# is improperly relying on it is given *some* notice.
|
||||
|
||||
|
||||
def __deprecated_private_func(f):
|
||||
from functools import wraps
|
||||
import warnings
|
||||
|
||||
msg = ('{name} is a private function and may break without warning, '
|
||||
'it will be moved and or renamed in future versions.')
|
||||
msg = msg.format(name=f.__name__)
|
||||
|
||||
@wraps(f)
|
||||
def deprecated_func(*args, **kwargs):
|
||||
warnings.warn(msg, DeprecationWarning)
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return deprecated_func
|
||||
|
||||
def __deprecate_private_class(c):
|
||||
import warnings
|
||||
|
||||
msg = ('{name} is a private class and may break without warning, '
|
||||
'it will be moved and or renamed in future versions.')
|
||||
msg = msg.format(name=c.__name__)
|
||||
|
||||
class private_class(c):
|
||||
__doc__ = c.__doc__
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn(msg, DeprecationWarning)
|
||||
super(private_class, self).__init__(*args, **kwargs)
|
||||
|
||||
private_class.__name__ = c.__name__
|
||||
|
||||
return private_class
|
||||
|
||||
|
||||
from ._parser import _timelex, _resultbase
|
||||
from ._parser import _tzparser, _parsetz
|
||||
|
||||
_timelex = __deprecate_private_class(_timelex)
|
||||
_tzparser = __deprecate_private_class(_tzparser)
|
||||
_resultbase = __deprecate_private_class(_resultbase)
|
||||
_parsetz = __deprecated_private_func(_parsetz)
|
1578
resources/lib/libraries/dateutil/parser/_parser.py
Normal file
1578
resources/lib/libraries/dateutil/parser/_parser.py
Normal file
File diff suppressed because it is too large
Load diff
406
resources/lib/libraries/dateutil/parser/isoparser.py
Normal file
406
resources/lib/libraries/dateutil/parser/isoparser.py
Normal file
|
@ -0,0 +1,406 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This module offers a parser for ISO-8601 strings
|
||||
|
||||
It is intended to support all valid date, time and datetime formats per the
|
||||
ISO-8601 specification.
|
||||
|
||||
..versionadded:: 2.7.0
|
||||
"""
|
||||
from datetime import datetime, timedelta, time, date
|
||||
import calendar
|
||||
from .. import tz
|
||||
|
||||
from functools import wraps
|
||||
|
||||
import re
|
||||
from .. import six
|
||||
|
||||
__all__ = ["isoparse", "isoparser"]
|
||||
|
||||
|
||||
def _takes_ascii(f):
|
||||
@wraps(f)
|
||||
def func(self, str_in, *args, **kwargs):
|
||||
# If it's a stream, read the whole thing
|
||||
str_in = getattr(str_in, 'read', lambda: str_in)()
|
||||
|
||||
# If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII
|
||||
if isinstance(str_in, six.text_type):
|
||||
# ASCII is the same in UTF-8
|
||||
try:
|
||||
str_in = str_in.encode('ascii')
|
||||
except UnicodeEncodeError as e:
|
||||
msg = 'ISO-8601 strings should contain only ASCII characters'
|
||||
six.raise_from(ValueError(msg), e)
|
||||
|
||||
return f(self, str_in, *args, **kwargs)
|
||||
|
||||
return func
|
||||
|
||||
|
||||
class isoparser(object):
|
||||
def __init__(self, sep=None):
|
||||
"""
|
||||
:param sep:
|
||||
A single character that separates date and time portions. If
|
||||
``None``, the parser will accept any single character.
|
||||
For strict ISO-8601 adherence, pass ``'T'``.
|
||||
"""
|
||||
if sep is not None:
|
||||
if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'):
|
||||
raise ValueError('Separator must be a single, non-numeric ' +
|
||||
'ASCII character')
|
||||
|
||||
sep = sep.encode('ascii')
|
||||
|
||||
self._sep = sep
|
||||
|
||||
@_takes_ascii
|
||||
def isoparse(self, dt_str):
|
||||
"""
|
||||
Parse an ISO-8601 datetime string into a :class:`datetime.datetime`.
|
||||
|
||||
An ISO-8601 datetime string consists of a date portion, followed
|
||||
optionally by a time portion - the date and time portions are separated
|
||||
by a single character separator, which is ``T`` in the official
|
||||
standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be
|
||||
combined with a time portion.
|
||||
|
||||
Supported date formats are:
|
||||
|
||||
Common:
|
||||
|
||||
- ``YYYY``
|
||||
- ``YYYY-MM`` or ``YYYYMM``
|
||||
- ``YYYY-MM-DD`` or ``YYYYMMDD``
|
||||
|
||||
Uncommon:
|
||||
|
||||
- ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0)
|
||||
- ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day
|
||||
|
||||
The ISO week and day numbering follows the same logic as
|
||||
:func:`datetime.date.isocalendar`.
|
||||
|
||||
Supported time formats are:
|
||||
|
||||
- ``hh``
|
||||
- ``hh:mm`` or ``hhmm``
|
||||
- ``hh:mm:ss`` or ``hhmmss``
|
||||
- ``hh:mm:ss.sss`` or ``hh:mm:ss.ssssss`` (3-6 sub-second digits)
|
||||
|
||||
Midnight is a special case for `hh`, as the standard supports both
|
||||
00:00 and 24:00 as a representation.
|
||||
|
||||
.. caution::
|
||||
|
||||
Support for fractional components other than seconds is part of the
|
||||
ISO-8601 standard, but is not currently implemented in this parser.
|
||||
|
||||
Supported time zone offset formats are:
|
||||
|
||||
- `Z` (UTC)
|
||||
- `±HH:MM`
|
||||
- `±HHMM`
|
||||
- `±HH`
|
||||
|
||||
Offsets will be represented as :class:`dateutil.tz.tzoffset` objects,
|
||||
with the exception of UTC, which will be represented as
|
||||
:class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such
|
||||
as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`.
|
||||
|
||||
:param dt_str:
|
||||
A string or stream containing only an ISO-8601 datetime string
|
||||
|
||||
:return:
|
||||
Returns a :class:`datetime.datetime` representing the string.
|
||||
Unspecified components default to their lowest value.
|
||||
|
||||
.. warning::
|
||||
|
||||
As of version 2.7.0, the strictness of the parser should not be
|
||||
considered a stable part of the contract. Any valid ISO-8601 string
|
||||
that parses correctly with the default settings will continue to
|
||||
parse correctly in future versions, but invalid strings that
|
||||
currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not
|
||||
guaranteed to continue failing in future versions if they encode
|
||||
a valid date.
|
||||
|
||||
.. versionadded:: 2.7.0
|
||||
"""
|
||||
components, pos = self._parse_isodate(dt_str)
|
||||
|
||||
if len(dt_str) > pos:
|
||||
if self._sep is None or dt_str[pos:pos + 1] == self._sep:
|
||||
components += self._parse_isotime(dt_str[pos + 1:])
|
||||
else:
|
||||
raise ValueError('String contains unknown ISO components')
|
||||
|
||||
return datetime(*components)
|
||||
|
||||
@_takes_ascii
|
||||
def parse_isodate(self, datestr):
|
||||
"""
|
||||
Parse the date portion of an ISO string.
|
||||
|
||||
:param datestr:
|
||||
The string portion of an ISO string, without a separator
|
||||
|
||||
:return:
|
||||
Returns a :class:`datetime.date` object
|
||||
"""
|
||||
components, pos = self._parse_isodate(datestr)
|
||||
if pos < len(datestr):
|
||||
raise ValueError('String contains unknown ISO ' +
|
||||
'components: {}'.format(datestr))
|
||||
return date(*components)
|
||||
|
||||
@_takes_ascii
|
||||
def parse_isotime(self, timestr):
|
||||
"""
|
||||
Parse the time portion of an ISO string.
|
||||
|
||||
:param timestr:
|
||||
The time portion of an ISO string, without a separator
|
||||
|
||||
:return:
|
||||
Returns a :class:`datetime.time` object
|
||||
"""
|
||||
return time(*self._parse_isotime(timestr))
|
||||
|
||||
@_takes_ascii
|
||||
def parse_tzstr(self, tzstr, zero_as_utc=True):
|
||||
"""
|
||||
Parse a valid ISO time zone string.
|
||||
|
||||
See :func:`isoparser.isoparse` for details on supported formats.
|
||||
|
||||
:param tzstr:
|
||||
A string representing an ISO time zone offset
|
||||
|
||||
:param zero_as_utc:
|
||||
Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones
|
||||
|
||||
:return:
|
||||
Returns :class:`dateutil.tz.tzoffset` for offsets and
|
||||
:class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is
|
||||
specified) offsets equivalent to UTC.
|
||||
"""
|
||||
return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
|
||||
|
||||
# Constants
|
||||
_MICROSECOND_END_REGEX = re.compile(b'[-+Z]+')
|
||||
_DATE_SEP = b'-'
|
||||
_TIME_SEP = b':'
|
||||
_MICRO_SEP = b'.'
|
||||
|
||||
def _parse_isodate(self, dt_str):
|
||||
try:
|
||||
return self._parse_isodate_common(dt_str)
|
||||
except ValueError:
|
||||
return self._parse_isodate_uncommon(dt_str)
|
||||
|
||||
def _parse_isodate_common(self, dt_str):
|
||||
len_str = len(dt_str)
|
||||
components = [1, 1, 1]
|
||||
|
||||
if len_str < 4:
|
||||
raise ValueError('ISO string too short')
|
||||
|
||||
# Year
|
||||
components[0] = int(dt_str[0:4])
|
||||
pos = 4
|
||||
if pos >= len_str:
|
||||
return components, pos
|
||||
|
||||
has_sep = dt_str[pos:pos + 1] == self._DATE_SEP
|
||||
if has_sep:
|
||||
pos += 1
|
||||
|
||||
# Month
|
||||
if len_str - pos < 2:
|
||||
raise ValueError('Invalid common month')
|
||||
|
||||
components[1] = int(dt_str[pos:pos + 2])
|
||||
pos += 2
|
||||
|
||||
if pos >= len_str:
|
||||
if has_sep:
|
||||
return components, pos
|
||||
else:
|
||||
raise ValueError('Invalid ISO format')
|
||||
|
||||
if has_sep:
|
||||
if dt_str[pos:pos + 1] != self._DATE_SEP:
|
||||
raise ValueError('Invalid separator in ISO string')
|
||||
pos += 1
|
||||
|
||||
# Day
|
||||
if len_str - pos < 2:
|
||||
raise ValueError('Invalid common day')
|
||||
components[2] = int(dt_str[pos:pos + 2])
|
||||
return components, pos + 2
|
||||
|
||||
def _parse_isodate_uncommon(self, dt_str):
|
||||
if len(dt_str) < 4:
|
||||
raise ValueError('ISO string too short')
|
||||
|
||||
# All ISO formats start with the year
|
||||
year = int(dt_str[0:4])
|
||||
|
||||
has_sep = dt_str[4:5] == self._DATE_SEP
|
||||
|
||||
pos = 4 + has_sep # Skip '-' if it's there
|
||||
if dt_str[pos:pos + 1] == b'W':
|
||||
# YYYY-?Www-?D?
|
||||
pos += 1
|
||||
weekno = int(dt_str[pos:pos + 2])
|
||||
pos += 2
|
||||
|
||||
dayno = 1
|
||||
if len(dt_str) > pos:
|
||||
if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep:
|
||||
raise ValueError('Inconsistent use of dash separator')
|
||||
|
||||
pos += has_sep
|
||||
|
||||
dayno = int(dt_str[pos:pos + 1])
|
||||
pos += 1
|
||||
|
||||
base_date = self._calculate_weekdate(year, weekno, dayno)
|
||||
else:
|
||||
# YYYYDDD or YYYY-DDD
|
||||
if len(dt_str) - pos < 3:
|
||||
raise ValueError('Invalid ordinal day')
|
||||
|
||||
ordinal_day = int(dt_str[pos:pos + 3])
|
||||
pos += 3
|
||||
|
||||
if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)):
|
||||
raise ValueError('Invalid ordinal day' +
|
||||
' {} for year {}'.format(ordinal_day, year))
|
||||
|
||||
base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1)
|
||||
|
||||
components = [base_date.year, base_date.month, base_date.day]
|
||||
return components, pos
|
||||
|
||||
def _calculate_weekdate(self, year, week, day):
|
||||
"""
|
||||
Calculate the day of corresponding to the ISO year-week-day calendar.
|
||||
|
||||
This function is effectively the inverse of
|
||||
:func:`datetime.date.isocalendar`.
|
||||
|
||||
:param year:
|
||||
The year in the ISO calendar
|
||||
|
||||
:param week:
|
||||
The week in the ISO calendar - range is [1, 53]
|
||||
|
||||
:param day:
|
||||
The day in the ISO calendar - range is [1 (MON), 7 (SUN)]
|
||||
|
||||
:return:
|
||||
Returns a :class:`datetime.date`
|
||||
"""
|
||||
if not 0 < week < 54:
|
||||
raise ValueError('Invalid week: {}'.format(week))
|
||||
|
||||
if not 0 < day < 8: # Range is 1-7
|
||||
raise ValueError('Invalid weekday: {}'.format(day))
|
||||
|
||||
# Get week 1 for the specific year:
|
||||
jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it
|
||||
week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1)
|
||||
|
||||
# Now add the specific number of weeks and days to get what we want
|
||||
week_offset = (week - 1) * 7 + (day - 1)
|
||||
return week_1 + timedelta(days=week_offset)
|
||||
|
||||
def _parse_isotime(self, timestr):
|
||||
len_str = len(timestr)
|
||||
components = [0, 0, 0, 0, None]
|
||||
pos = 0
|
||||
comp = -1
|
||||
|
||||
if len(timestr) < 2:
|
||||
raise ValueError('ISO time too short')
|
||||
|
||||
has_sep = len_str >= 3 and timestr[2:3] == self._TIME_SEP
|
||||
|
||||
while pos < len_str and comp < 5:
|
||||
comp += 1
|
||||
|
||||
if timestr[pos:pos + 1] in b'-+Z':
|
||||
# Detect time zone boundary
|
||||
components[-1] = self._parse_tzstr(timestr[pos:])
|
||||
pos = len_str
|
||||
break
|
||||
|
||||
if comp < 3:
|
||||
# Hour, minute, second
|
||||
components[comp] = int(timestr[pos:pos + 2])
|
||||
pos += 2
|
||||
if (has_sep and pos < len_str and
|
||||
timestr[pos:pos + 1] == self._TIME_SEP):
|
||||
pos += 1
|
||||
|
||||
if comp == 3:
|
||||
# Microsecond
|
||||
if timestr[pos:pos + 1] != self._MICRO_SEP:
|
||||
continue
|
||||
|
||||
pos += 1
|
||||
us_str = self._MICROSECOND_END_REGEX.split(timestr[pos:pos + 6],
|
||||
1)[0]
|
||||
|
||||
components[comp] = int(us_str) * 10**(6 - len(us_str))
|
||||
pos += len(us_str)
|
||||
|
||||
if pos < len_str:
|
||||
raise ValueError('Unused components in ISO string')
|
||||
|
||||
if components[0] == 24:
|
||||
# Standard supports 00:00 and 24:00 as representations of midnight
|
||||
if any(component != 0 for component in components[1:4]):
|
||||
raise ValueError('Hour may only be 24 at 24:00:00.000')
|
||||
components[0] = 0
|
||||
|
||||
return components
|
||||
|
||||
def _parse_tzstr(self, tzstr, zero_as_utc=True):
|
||||
if tzstr == b'Z':
|
||||
return tz.tzutc()
|
||||
|
||||
if len(tzstr) not in {3, 5, 6}:
|
||||
raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters')
|
||||
|
||||
if tzstr[0:1] == b'-':
|
||||
mult = -1
|
||||
elif tzstr[0:1] == b'+':
|
||||
mult = 1
|
||||
else:
|
||||
raise ValueError('Time zone offset requires sign')
|
||||
|
||||
hours = int(tzstr[1:3])
|
||||
if len(tzstr) == 3:
|
||||
minutes = 0
|
||||
else:
|
||||
minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):])
|
||||
|
||||
if zero_as_utc and hours == 0 and minutes == 0:
|
||||
return tz.tzutc()
|
||||
else:
|
||||
if minutes > 59:
|
||||
raise ValueError('Invalid minutes in time zone offset')
|
||||
|
||||
if hours > 23:
|
||||
raise ValueError('Invalid hours in time zone offset')
|
||||
|
||||
return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60)
|
||||
|
||||
|
||||
DEFAULT_ISOPARSER = isoparser()
|
||||
isoparse = DEFAULT_ISOPARSER.isoparse
|
590
resources/lib/libraries/dateutil/relativedelta.py
Normal file
590
resources/lib/libraries/dateutil/relativedelta.py
Normal file
|
@ -0,0 +1,590 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import calendar
|
||||
|
||||
import operator
|
||||
from math import copysign
|
||||
|
||||
from six import integer_types
|
||||
from warnings import warn
|
||||
|
||||
from ._common import weekday
|
||||
|
||||
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7))
|
||||
|
||||
__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
|
||||
|
||||
|
||||
class relativedelta(object):
|
||||
"""
|
||||
The relativedelta type is based on the specification of the excellent
|
||||
work done by M.-A. Lemburg in his
|
||||
`mx.DateTime <https://www.egenix.com/products/python/mxBase/mxDateTime/>`_ extension.
|
||||
However, notice that this type does *NOT* implement the same algorithm as
|
||||
his work. Do *NOT* expect it to behave like mx.DateTime's counterpart.
|
||||
|
||||
There are two different ways to build a relativedelta instance. The
|
||||
first one is passing it two date/datetime classes::
|
||||
|
||||
relativedelta(datetime1, datetime2)
|
||||
|
||||
The second one is passing it any number of the following keyword arguments::
|
||||
|
||||
relativedelta(arg1=x,arg2=y,arg3=z...)
|
||||
|
||||
year, month, day, hour, minute, second, microsecond:
|
||||
Absolute information (argument is singular); adding or subtracting a
|
||||
relativedelta with absolute information does not perform an arithmetic
|
||||
operation, but rather REPLACES the corresponding value in the
|
||||
original datetime with the value(s) in relativedelta.
|
||||
|
||||
years, months, weeks, days, hours, minutes, seconds, microseconds:
|
||||
Relative information, may be negative (argument is plural); adding
|
||||
or subtracting a relativedelta with relative information performs
|
||||
the corresponding aritmetic operation on the original datetime value
|
||||
with the information in the relativedelta.
|
||||
|
||||
weekday:
|
||||
One of the weekday instances (MO, TU, etc). These
|
||||
instances may receive a parameter N, specifying the Nth
|
||||
weekday, which could be positive or negative (like MO(+1)
|
||||
or MO(-2). Not specifying it is the same as specifying
|
||||
+1. You can also use an integer, where 0=MO. Notice that
|
||||
if the calculated date is already Monday, for example,
|
||||
using MO(1) or MO(-1) won't change the day.
|
||||
|
||||
leapdays:
|
||||
Will add given days to the date found, if year is a leap
|
||||
year, and the date found is post 28 of february.
|
||||
|
||||
yearday, nlyearday:
|
||||
Set the yearday or the non-leap year day (jump leap days).
|
||||
These are converted to day/month/leapdays information.
|
||||
|
||||
There are relative and absolute forms of the keyword
|
||||
arguments. The plural is relative, and the singular is
|
||||
absolute. For each argument in the order below, the absolute form
|
||||
is applied first (by setting each attribute to that value) and
|
||||
then the relative form (by adding the value to the attribute).
|
||||
|
||||
The order of attributes considered when this relativedelta is
|
||||
added to a datetime is:
|
||||
|
||||
1. Year
|
||||
2. Month
|
||||
3. Day
|
||||
4. Hours
|
||||
5. Minutes
|
||||
6. Seconds
|
||||
7. Microseconds
|
||||
|
||||
Finally, weekday is applied, using the rule described above.
|
||||
|
||||
For example
|
||||
|
||||
>>> dt = datetime(2018, 4, 9, 13, 37, 0)
|
||||
>>> delta = relativedelta(hours=25, day=1, weekday=MO(1))
|
||||
datetime(2018, 4, 2, 14, 37, 0)
|
||||
|
||||
First, the day is set to 1 (the first of the month), then 25 hours
|
||||
are added, to get to the 2nd day and 14th hour, finally the
|
||||
weekday is applied, but since the 2nd is already a Monday there is
|
||||
no effect.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, dt1=None, dt2=None,
|
||||
years=0, months=0, days=0, leapdays=0, weeks=0,
|
||||
hours=0, minutes=0, seconds=0, microseconds=0,
|
||||
year=None, month=None, day=None, weekday=None,
|
||||
yearday=None, nlyearday=None,
|
||||
hour=None, minute=None, second=None, microsecond=None):
|
||||
|
||||
if dt1 and dt2:
|
||||
# datetime is a subclass of date. So both must be date
|
||||
if not (isinstance(dt1, datetime.date) and
|
||||
isinstance(dt2, datetime.date)):
|
||||
raise TypeError("relativedelta only diffs datetime/date")
|
||||
|
||||
# We allow two dates, or two datetimes, so we coerce them to be
|
||||
# of the same type
|
||||
if (isinstance(dt1, datetime.datetime) !=
|
||||
isinstance(dt2, datetime.datetime)):
|
||||
if not isinstance(dt1, datetime.datetime):
|
||||
dt1 = datetime.datetime.fromordinal(dt1.toordinal())
|
||||
elif not isinstance(dt2, datetime.datetime):
|
||||
dt2 = datetime.datetime.fromordinal(dt2.toordinal())
|
||||
|
||||
self.years = 0
|
||||
self.months = 0
|
||||
self.days = 0
|
||||
self.leapdays = 0
|
||||
self.hours = 0
|
||||
self.minutes = 0
|
||||
self.seconds = 0
|
||||
self.microseconds = 0
|
||||
self.year = None
|
||||
self.month = None
|
||||
self.day = None
|
||||
self.weekday = None
|
||||
self.hour = None
|
||||
self.minute = None
|
||||
self.second = None
|
||||
self.microsecond = None
|
||||
self._has_time = 0
|
||||
|
||||
# Get year / month delta between the two
|
||||
months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month)
|
||||
self._set_months(months)
|
||||
|
||||
# Remove the year/month delta so the timedelta is just well-defined
|
||||
# time units (seconds, days and microseconds)
|
||||
dtm = self.__radd__(dt2)
|
||||
|
||||
# If we've overshot our target, make an adjustment
|
||||
if dt1 < dt2:
|
||||
compare = operator.gt
|
||||
increment = 1
|
||||
else:
|
||||
compare = operator.lt
|
||||
increment = -1
|
||||
|
||||
while compare(dt1, dtm):
|
||||
months += increment
|
||||
self._set_months(months)
|
||||
dtm = self.__radd__(dt2)
|
||||
|
||||
# Get the timedelta between the "months-adjusted" date and dt1
|
||||
delta = dt1 - dtm
|
||||
self.seconds = delta.seconds + delta.days * 86400
|
||||
self.microseconds = delta.microseconds
|
||||
else:
|
||||
# Check for non-integer values in integer-only quantities
|
||||
if any(x is not None and x != int(x) for x in (years, months)):
|
||||
raise ValueError("Non-integer years and months are "
|
||||
"ambiguous and not currently supported.")
|
||||
|
||||
# Relative information
|
||||
self.years = int(years)
|
||||
self.months = int(months)
|
||||
self.days = days + weeks * 7
|
||||
self.leapdays = leapdays
|
||||
self.hours = hours
|
||||
self.minutes = minutes
|
||||
self.seconds = seconds
|
||||
self.microseconds = microseconds
|
||||
|
||||
# Absolute information
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
self.hour = hour
|
||||
self.minute = minute
|
||||
self.second = second
|
||||
self.microsecond = microsecond
|
||||
|
||||
if any(x is not None and int(x) != x
|
||||
for x in (year, month, day, hour,
|
||||
minute, second, microsecond)):
|
||||
# For now we'll deprecate floats - later it'll be an error.
|
||||
warn("Non-integer value passed as absolute information. " +
|
||||
"This is not a well-defined condition and will raise " +
|
||||
"errors in future versions.", DeprecationWarning)
|
||||
|
||||
if isinstance(weekday, integer_types):
|
||||
self.weekday = weekdays[weekday]
|
||||
else:
|
||||
self.weekday = weekday
|
||||
|
||||
yday = 0
|
||||
if nlyearday:
|
||||
yday = nlyearday
|
||||
elif yearday:
|
||||
yday = yearday
|
||||
if yearday > 59:
|
||||
self.leapdays = -1
|
||||
if yday:
|
||||
ydayidx = [31, 59, 90, 120, 151, 181, 212,
|
||||
243, 273, 304, 334, 366]
|
||||
for idx, ydays in enumerate(ydayidx):
|
||||
if yday <= ydays:
|
||||
self.month = idx+1
|
||||
if idx == 0:
|
||||
self.day = yday
|
||||
else:
|
||||
self.day = yday-ydayidx[idx-1]
|
||||
break
|
||||
else:
|
||||
raise ValueError("invalid year day (%d)" % yday)
|
||||
|
||||
self._fix()
|
||||
|
||||
def _fix(self):
|
||||
if abs(self.microseconds) > 999999:
|
||||
s = _sign(self.microseconds)
|
||||
div, mod = divmod(self.microseconds * s, 1000000)
|
||||
self.microseconds = mod * s
|
||||
self.seconds += div * s
|
||||
if abs(self.seconds) > 59:
|
||||
s = _sign(self.seconds)
|
||||
div, mod = divmod(self.seconds * s, 60)
|
||||
self.seconds = mod * s
|
||||
self.minutes += div * s
|
||||
if abs(self.minutes) > 59:
|
||||
s = _sign(self.minutes)
|
||||
div, mod = divmod(self.minutes * s, 60)
|
||||
self.minutes = mod * s
|
||||
self.hours += div * s
|
||||
if abs(self.hours) > 23:
|
||||
s = _sign(self.hours)
|
||||
div, mod = divmod(self.hours * s, 24)
|
||||
self.hours = mod * s
|
||||
self.days += div * s
|
||||
if abs(self.months) > 11:
|
||||
s = _sign(self.months)
|
||||
div, mod = divmod(self.months * s, 12)
|
||||
self.months = mod * s
|
||||
self.years += div * s
|
||||
if (self.hours or self.minutes or self.seconds or self.microseconds
|
||||
or self.hour is not None or self.minute is not None or
|
||||
self.second is not None or self.microsecond is not None):
|
||||
self._has_time = 1
|
||||
else:
|
||||
self._has_time = 0
|
||||
|
||||
@property
|
||||
def weeks(self):
|
||||
return int(self.days / 7.0)
|
||||
|
||||
@weeks.setter
|
||||
def weeks(self, value):
|
||||
self.days = self.days - (self.weeks * 7) + value * 7
|
||||
|
||||
def _set_months(self, months):
|
||||
self.months = months
|
||||
if abs(self.months) > 11:
|
||||
s = _sign(self.months)
|
||||
div, mod = divmod(self.months * s, 12)
|
||||
self.months = mod * s
|
||||
self.years = div * s
|
||||
else:
|
||||
self.years = 0
|
||||
|
||||
def normalized(self):
|
||||
"""
|
||||
Return a version of this object represented entirely using integer
|
||||
values for the relative attributes.
|
||||
|
||||
>>> relativedelta(days=1.5, hours=2).normalized()
|
||||
relativedelta(days=1, hours=14)
|
||||
|
||||
:return:
|
||||
Returns a :class:`dateutil.relativedelta.relativedelta` object.
|
||||
"""
|
||||
# Cascade remainders down (rounding each to roughly nearest microsecond)
|
||||
days = int(self.days)
|
||||
|
||||
hours_f = round(self.hours + 24 * (self.days - days), 11)
|
||||
hours = int(hours_f)
|
||||
|
||||
minutes_f = round(self.minutes + 60 * (hours_f - hours), 10)
|
||||
minutes = int(minutes_f)
|
||||
|
||||
seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8)
|
||||
seconds = int(seconds_f)
|
||||
|
||||
microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds))
|
||||
|
||||
# Constructor carries overflow back up with call to _fix()
|
||||
return self.__class__(years=self.years, months=self.months,
|
||||
days=days, hours=hours, minutes=minutes,
|
||||
seconds=seconds, microseconds=microseconds,
|
||||
leapdays=self.leapdays, year=self.year,
|
||||
month=self.month, day=self.day,
|
||||
weekday=self.weekday, hour=self.hour,
|
||||
minute=self.minute, second=self.second,
|
||||
microsecond=self.microsecond)
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, relativedelta):
|
||||
return self.__class__(years=other.years + self.years,
|
||||
months=other.months + self.months,
|
||||
days=other.days + self.days,
|
||||
hours=other.hours + self.hours,
|
||||
minutes=other.minutes + self.minutes,
|
||||
seconds=other.seconds + self.seconds,
|
||||
microseconds=(other.microseconds +
|
||||
self.microseconds),
|
||||
leapdays=other.leapdays or self.leapdays,
|
||||
year=(other.year if other.year is not None
|
||||
else self.year),
|
||||
month=(other.month if other.month is not None
|
||||
else self.month),
|
||||
day=(other.day if other.day is not None
|
||||
else self.day),
|
||||
weekday=(other.weekday if other.weekday is not None
|
||||
else self.weekday),
|
||||
hour=(other.hour if other.hour is not None
|
||||
else self.hour),
|
||||
minute=(other.minute if other.minute is not None
|
||||
else self.minute),
|
||||
second=(other.second if other.second is not None
|
||||
else self.second),
|
||||
microsecond=(other.microsecond if other.microsecond
|
||||
is not None else
|
||||
self.microsecond))
|
||||
if isinstance(other, datetime.timedelta):
|
||||
return self.__class__(years=self.years,
|
||||
months=self.months,
|
||||
days=self.days + other.days,
|
||||
hours=self.hours,
|
||||
minutes=self.minutes,
|
||||
seconds=self.seconds + other.seconds,
|
||||
microseconds=self.microseconds + other.microseconds,
|
||||
leapdays=self.leapdays,
|
||||
year=self.year,
|
||||
month=self.month,
|
||||
day=self.day,
|
||||
weekday=self.weekday,
|
||||
hour=self.hour,
|
||||
minute=self.minute,
|
||||
second=self.second,
|
||||
microsecond=self.microsecond)
|
||||
if not isinstance(other, datetime.date):
|
||||
return NotImplemented
|
||||
elif self._has_time and not isinstance(other, datetime.datetime):
|
||||
other = datetime.datetime.fromordinal(other.toordinal())
|
||||
year = (self.year or other.year)+self.years
|
||||
month = self.month or other.month
|
||||
if self.months:
|
||||
assert 1 <= abs(self.months) <= 12
|
||||
month += self.months
|
||||
if month > 12:
|
||||
year += 1
|
||||
month -= 12
|
||||
elif month < 1:
|
||||
year -= 1
|
||||
month += 12
|
||||
day = min(calendar.monthrange(year, month)[1],
|
||||
self.day or other.day)
|
||||
repl = {"year": year, "month": month, "day": day}
|
||||
for attr in ["hour", "minute", "second", "microsecond"]:
|
||||
value = getattr(self, attr)
|
||||
if value is not None:
|
||||
repl[attr] = value
|
||||
days = self.days
|
||||
if self.leapdays and month > 2 and calendar.isleap(year):
|
||||
days += self.leapdays
|
||||
ret = (other.replace(**repl)
|
||||
+ datetime.timedelta(days=days,
|
||||
hours=self.hours,
|
||||
minutes=self.minutes,
|
||||
seconds=self.seconds,
|
||||
microseconds=self.microseconds))
|
||||
if self.weekday:
|
||||
weekday, nth = self.weekday.weekday, self.weekday.n or 1
|
||||
jumpdays = (abs(nth) - 1) * 7
|
||||
if nth > 0:
|
||||
jumpdays += (7 - ret.weekday() + weekday) % 7
|
||||
else:
|
||||
jumpdays += (ret.weekday() - weekday) % 7
|
||||
jumpdays *= -1
|
||||
ret += datetime.timedelta(days=jumpdays)
|
||||
return ret
|
||||
|
||||
def __radd__(self, other):
|
||||
return self.__add__(other)
|
||||
|
||||
def __rsub__(self, other):
|
||||
return self.__neg__().__radd__(other)
|
||||
|
||||
def __sub__(self, other):
|
||||
if not isinstance(other, relativedelta):
|
||||
return NotImplemented # In case the other object defines __rsub__
|
||||
return self.__class__(years=self.years - other.years,
|
||||
months=self.months - other.months,
|
||||
days=self.days - other.days,
|
||||
hours=self.hours - other.hours,
|
||||
minutes=self.minutes - other.minutes,
|
||||
seconds=self.seconds - other.seconds,
|
||||
microseconds=self.microseconds - other.microseconds,
|
||||
leapdays=self.leapdays or other.leapdays,
|
||||
year=(self.year if self.year is not None
|
||||
else other.year),
|
||||
month=(self.month if self.month is not None else
|
||||
other.month),
|
||||
day=(self.day if self.day is not None else
|
||||
other.day),
|
||||
weekday=(self.weekday if self.weekday is not None else
|
||||
other.weekday),
|
||||
hour=(self.hour if self.hour is not None else
|
||||
other.hour),
|
||||
minute=(self.minute if self.minute is not None else
|
||||
other.minute),
|
||||
second=(self.second if self.second is not None else
|
||||
other.second),
|
||||
microsecond=(self.microsecond if self.microsecond
|
||||
is not None else
|
||||
other.microsecond))
|
||||
|
||||
def __abs__(self):
|
||||
return self.__class__(years=abs(self.years),
|
||||
months=abs(self.months),
|
||||
days=abs(self.days),
|
||||
hours=abs(self.hours),
|
||||
minutes=abs(self.minutes),
|
||||
seconds=abs(self.seconds),
|
||||
microseconds=abs(self.microseconds),
|
||||
leapdays=self.leapdays,
|
||||
year=self.year,
|
||||
month=self.month,
|
||||
day=self.day,
|
||||
weekday=self.weekday,
|
||||
hour=self.hour,
|
||||
minute=self.minute,
|
||||
second=self.second,
|
||||
microsecond=self.microsecond)
|
||||
|
||||
def __neg__(self):
|
||||
return self.__class__(years=-self.years,
|
||||
months=-self.months,
|
||||
days=-self.days,
|
||||
hours=-self.hours,
|
||||
minutes=-self.minutes,
|
||||
seconds=-self.seconds,
|
||||
microseconds=-self.microseconds,
|
||||
leapdays=self.leapdays,
|
||||
year=self.year,
|
||||
month=self.month,
|
||||
day=self.day,
|
||||
weekday=self.weekday,
|
||||
hour=self.hour,
|
||||
minute=self.minute,
|
||||
second=self.second,
|
||||
microsecond=self.microsecond)
|
||||
|
||||
def __bool__(self):
|
||||
return not (not self.years and
|
||||
not self.months and
|
||||
not self.days and
|
||||
not self.hours and
|
||||
not self.minutes and
|
||||
not self.seconds and
|
||||
not self.microseconds and
|
||||
not self.leapdays and
|
||||
self.year is None and
|
||||
self.month is None and
|
||||
self.day is None and
|
||||
self.weekday is None and
|
||||
self.hour is None and
|
||||
self.minute is None and
|
||||
self.second is None and
|
||||
self.microsecond is None)
|
||||
# Compatibility with Python 2.x
|
||||
__nonzero__ = __bool__
|
||||
|
||||
def __mul__(self, other):
|
||||
try:
|
||||
f = float(other)
|
||||
except TypeError:
|
||||
return NotImplemented
|
||||
|
||||
return self.__class__(years=int(self.years * f),
|
||||
months=int(self.months * f),
|
||||
days=int(self.days * f),
|
||||
hours=int(self.hours * f),
|
||||
minutes=int(self.minutes * f),
|
||||
seconds=int(self.seconds * f),
|
||||
microseconds=int(self.microseconds * f),
|
||||
leapdays=self.leapdays,
|
||||
year=self.year,
|
||||
month=self.month,
|
||||
day=self.day,
|
||||
weekday=self.weekday,
|
||||
hour=self.hour,
|
||||
minute=self.minute,
|
||||
second=self.second,
|
||||
microsecond=self.microsecond)
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, relativedelta):
|
||||
return NotImplemented
|
||||
if self.weekday or other.weekday:
|
||||
if not self.weekday or not other.weekday:
|
||||
return False
|
||||
if self.weekday.weekday != other.weekday.weekday:
|
||||
return False
|
||||
n1, n2 = self.weekday.n, other.weekday.n
|
||||
if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)):
|
||||
return False
|
||||
return (self.years == other.years and
|
||||
self.months == other.months and
|
||||
self.days == other.days and
|
||||
self.hours == other.hours and
|
||||
self.minutes == other.minutes and
|
||||
self.seconds == other.seconds and
|
||||
self.microseconds == other.microseconds and
|
||||
self.leapdays == other.leapdays and
|
||||
self.year == other.year and
|
||||
self.month == other.month and
|
||||
self.day == other.day and
|
||||
self.hour == other.hour and
|
||||
self.minute == other.minute and
|
||||
self.second == other.second and
|
||||
self.microsecond == other.microsecond)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((
|
||||
self.weekday,
|
||||
self.years,
|
||||
self.months,
|
||||
self.days,
|
||||
self.hours,
|
||||
self.minutes,
|
||||
self.seconds,
|
||||
self.microseconds,
|
||||
self.leapdays,
|
||||
self.year,
|
||||
self.month,
|
||||
self.day,
|
||||
self.hour,
|
||||
self.minute,
|
||||
self.second,
|
||||
self.microsecond,
|
||||
))
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __div__(self, other):
|
||||
try:
|
||||
reciprocal = 1 / float(other)
|
||||
except TypeError:
|
||||
return NotImplemented
|
||||
|
||||
return self.__mul__(reciprocal)
|
||||
|
||||
__truediv__ = __div__
|
||||
|
||||
def __repr__(self):
|
||||
l = []
|
||||
for attr in ["years", "months", "days", "leapdays",
|
||||
"hours", "minutes", "seconds", "microseconds"]:
|
||||
value = getattr(self, attr)
|
||||
if value:
|
||||
l.append("{attr}={value:+g}".format(attr=attr, value=value))
|
||||
for attr in ["year", "month", "day", "weekday",
|
||||
"hour", "minute", "second", "microsecond"]:
|
||||
value = getattr(self, attr)
|
||||
if value is not None:
|
||||
l.append("{attr}={value}".format(attr=attr, value=repr(value)))
|
||||
return "{classname}({attrs})".format(classname=self.__class__.__name__,
|
||||
attrs=", ".join(l))
|
||||
|
||||
|
||||
def _sign(x):
|
||||
return int(copysign(1, x))
|
||||
|
||||
# vim:ts=4:sw=4:et
|
1672
resources/lib/libraries/dateutil/rrule.py
Normal file
1672
resources/lib/libraries/dateutil/rrule.py
Normal file
File diff suppressed because it is too large
Load diff
891
resources/lib/libraries/dateutil/six.py
Normal file
891
resources/lib/libraries/dateutil/six.py
Normal file
|
@ -0,0 +1,891 @@
|
|||
# Copyright (c) 2010-2017 Benjamin Peterson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import operator
|
||||
import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.11.0"
|
||||
|
||||
|
||||
# Useful for very coarse version differentiation.
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
PY34 = sys.version_info[0:2] >= (3, 4)
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
integer_types = int,
|
||||
class_types = type,
|
||||
text_type = str
|
||||
binary_type = bytes
|
||||
|
||||
MAXSIZE = sys.maxsize
|
||||
else:
|
||||
string_types = basestring,
|
||||
integer_types = (int, long)
|
||||
class_types = (type, types.ClassType)
|
||||
text_type = unicode
|
||||
binary_type = str
|
||||
|
||||
if sys.platform.startswith("java"):
|
||||
# Jython always uses 32 bits.
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
||||
class X(object):
|
||||
|
||||
def __len__(self):
|
||||
return 1 << 31
|
||||
try:
|
||||
len(X())
|
||||
except OverflowError:
|
||||
# 32-bit
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# 64-bit
|
||||
MAXSIZE = int((1 << 63) - 1)
|
||||
del X
|
||||
|
||||
|
||||
def _add_doc(func, doc):
|
||||
"""Add documentation to a function."""
|
||||
func.__doc__ = doc
|
||||
|
||||
|
||||
def _import_module(name):
|
||||
"""Import module, returning the module after the last dot."""
|
||||
__import__(name)
|
||||
return sys.modules[name]
|
||||
|
||||
|
||||
class _LazyDescr(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __get__(self, obj, tp):
|
||||
result = self._resolve()
|
||||
setattr(obj, self.name, result) # Invokes __set__.
|
||||
try:
|
||||
# This is a bit ugly, but it avoids running this again by
|
||||
# removing this descriptor.
|
||||
delattr(obj.__class__, self.name)
|
||||
except AttributeError:
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
class MovedModule(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old, new=None):
|
||||
super(MovedModule, self).__init__(name)
|
||||
if PY3:
|
||||
if new is None:
|
||||
new = name
|
||||
self.mod = new
|
||||
else:
|
||||
self.mod = old
|
||||
|
||||
def _resolve(self):
|
||||
return _import_module(self.mod)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
_module = self._resolve()
|
||||
value = getattr(_module, attr)
|
||||
setattr(self, attr, value)
|
||||
return value
|
||||
|
||||
|
||||
class _LazyModule(types.ModuleType):
|
||||
|
||||
def __init__(self, name):
|
||||
super(_LazyModule, self).__init__(name)
|
||||
self.__doc__ = self.__class__.__doc__
|
||||
|
||||
def __dir__(self):
|
||||
attrs = ["__doc__", "__name__"]
|
||||
attrs += [attr.name for attr in self._moved_attributes]
|
||||
return attrs
|
||||
|
||||
# Subclasses should override this
|
||||
_moved_attributes = []
|
||||
|
||||
|
||||
class MovedAttribute(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
||||
super(MovedAttribute, self).__init__(name)
|
||||
if PY3:
|
||||
if new_mod is None:
|
||||
new_mod = name
|
||||
self.mod = new_mod
|
||||
if new_attr is None:
|
||||
if old_attr is None:
|
||||
new_attr = name
|
||||
else:
|
||||
new_attr = old_attr
|
||||
self.attr = new_attr
|
||||
else:
|
||||
self.mod = old_mod
|
||||
if old_attr is None:
|
||||
old_attr = name
|
||||
self.attr = old_attr
|
||||
|
||||
def _resolve(self):
|
||||
module = _import_module(self.mod)
|
||||
return getattr(module, self.attr)
|
||||
|
||||
|
||||
class _SixMetaPathImporter(object):
|
||||
|
||||
"""
|
||||
A meta path importer to import six.moves and its submodules.
|
||||
|
||||
This class implements a PEP302 finder and loader. It should be compatible
|
||||
with Python 2.5 and all existing versions of Python3
|
||||
"""
|
||||
|
||||
def __init__(self, six_module_name):
|
||||
self.name = six_module_name
|
||||
self.known_modules = {}
|
||||
|
||||
def _add_module(self, mod, *fullnames):
|
||||
for fullname in fullnames:
|
||||
self.known_modules[self.name + "." + fullname] = mod
|
||||
|
||||
def _get_module(self, fullname):
|
||||
return self.known_modules[self.name + "." + fullname]
|
||||
|
||||
def find_module(self, fullname, path=None):
|
||||
if fullname in self.known_modules:
|
||||
return self
|
||||
return None
|
||||
|
||||
def __get_module(self, fullname):
|
||||
try:
|
||||
return self.known_modules[fullname]
|
||||
except KeyError:
|
||||
raise ImportError("This loader does not know module " + fullname)
|
||||
|
||||
def load_module(self, fullname):
|
||||
try:
|
||||
# in case of a reload
|
||||
return sys.modules[fullname]
|
||||
except KeyError:
|
||||
pass
|
||||
mod = self.__get_module(fullname)
|
||||
if isinstance(mod, MovedModule):
|
||||
mod = mod._resolve()
|
||||
else:
|
||||
mod.__loader__ = self
|
||||
sys.modules[fullname] = mod
|
||||
return mod
|
||||
|
||||
def is_package(self, fullname):
|
||||
"""
|
||||
Return true, if the named module is a package.
|
||||
|
||||
We need this method to get correct spec objects with
|
||||
Python 3.4 (see PEP451)
|
||||
"""
|
||||
return hasattr(self.__get_module(fullname), "__path__")
|
||||
|
||||
def get_code(self, fullname):
|
||||
"""Return None
|
||||
|
||||
Required, if is_package is implemented"""
|
||||
self.__get_module(fullname) # eventually raises ImportError
|
||||
return None
|
||||
get_source = get_code # same as get_code
|
||||
|
||||
_importer = _SixMetaPathImporter(__name__)
|
||||
|
||||
|
||||
class _MovedItems(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects"""
|
||||
__path__ = [] # mark as package
|
||||
|
||||
|
||||
_moved_attributes = [
|
||||
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
||||
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
||||
MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
|
||||
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
|
||||
MovedAttribute("intern", "__builtin__", "sys"),
|
||||
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||
MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
|
||||
MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
|
||||
MovedAttribute("getoutput", "commands", "subprocess"),
|
||||
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
|
||||
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
|
||||
MovedAttribute("StringIO", "StringIO", "io"),
|
||||
MovedAttribute("UserDict", "UserDict", "collections"),
|
||||
MovedAttribute("UserList", "UserList", "collections"),
|
||||
MovedAttribute("UserString", "UserString", "collections"),
|
||||
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
||||
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
|
||||
MovedModule("builtins", "__builtin__"),
|
||||
MovedModule("configparser", "ConfigParser"),
|
||||
MovedModule("copyreg", "copy_reg"),
|
||||
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
|
||||
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
|
||||
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
||||
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
||||
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
||||
MovedModule("http_client", "httplib", "http.client"),
|
||||
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
||||
MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
|
||||
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
|
||||
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
|
||||
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
||||
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
||||
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
||||
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
||||
MovedModule("cPickle", "cPickle", "pickle"),
|
||||
MovedModule("queue", "Queue"),
|
||||
MovedModule("reprlib", "repr"),
|
||||
MovedModule("socketserver", "SocketServer"),
|
||||
MovedModule("_thread", "thread", "_thread"),
|
||||
MovedModule("tkinter", "Tkinter"),
|
||||
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
|
||||
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
|
||||
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
|
||||
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
|
||||
MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
|
||||
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
|
||||
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
|
||||
MovedModule("tkinter_colorchooser", "tkColorChooser",
|
||||
"tkinter.colorchooser"),
|
||||
MovedModule("tkinter_commondialog", "tkCommonDialog",
|
||||
"tkinter.commondialog"),
|
||||
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
|
||||
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
|
||||
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
|
||||
"tkinter.simpledialog"),
|
||||
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
|
||||
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
|
||||
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
|
||||
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
|
||||
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
|
||||
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
|
||||
]
|
||||
# Add windows specific modules.
|
||||
if sys.platform == "win32":
|
||||
_moved_attributes += [
|
||||
MovedModule("winreg", "_winreg"),
|
||||
]
|
||||
|
||||
for attr in _moved_attributes:
|
||||
setattr(_MovedItems, attr.name, attr)
|
||||
if isinstance(attr, MovedModule):
|
||||
_importer._add_module(attr, "moves." + attr.name)
|
||||
del attr
|
||||
|
||||
_MovedItems._moved_attributes = _moved_attributes
|
||||
|
||||
moves = _MovedItems(__name__ + ".moves")
|
||||
_importer._add_module(moves, "moves")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_parse(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_parse"""
|
||||
|
||||
|
||||
_urllib_parse_moved_attributes = [
|
||||
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urljoin", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlparse", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("quote", "urllib", "urllib.parse"),
|
||||
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
|
||||
MovedAttribute("urlencode", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splitquery", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splittag", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splituser", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splitvalue", "urllib", "urllib.parse"),
|
||||
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_query", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
|
||||
]
|
||||
for attr in _urllib_parse_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_parse, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
|
||||
"moves.urllib_parse", "moves.urllib.parse")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_error(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_error"""
|
||||
|
||||
|
||||
_urllib_error_moved_attributes = [
|
||||
MovedAttribute("URLError", "urllib2", "urllib.error"),
|
||||
MovedAttribute("HTTPError", "urllib2", "urllib.error"),
|
||||
MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
|
||||
]
|
||||
for attr in _urllib_error_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_error, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
|
||||
"moves.urllib_error", "moves.urllib.error")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_request(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_request"""
|
||||
|
||||
|
||||
_urllib_request_moved_attributes = [
|
||||
MovedAttribute("urlopen", "urllib2", "urllib.request"),
|
||||
MovedAttribute("install_opener", "urllib2", "urllib.request"),
|
||||
MovedAttribute("build_opener", "urllib2", "urllib.request"),
|
||||
MovedAttribute("pathname2url", "urllib", "urllib.request"),
|
||||
MovedAttribute("url2pathname", "urllib", "urllib.request"),
|
||||
MovedAttribute("getproxies", "urllib", "urllib.request"),
|
||||
MovedAttribute("Request", "urllib2", "urllib.request"),
|
||||
MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
|
||||
MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("FileHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
|
||||
MovedAttribute("urlretrieve", "urllib", "urllib.request"),
|
||||
MovedAttribute("urlcleanup", "urllib", "urllib.request"),
|
||||
MovedAttribute("URLopener", "urllib", "urllib.request"),
|
||||
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
|
||||
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
|
||||
MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
|
||||
MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
|
||||
]
|
||||
for attr in _urllib_request_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_request, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
|
||||
"moves.urllib_request", "moves.urllib.request")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_response(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_response"""
|
||||
|
||||
|
||||
_urllib_response_moved_attributes = [
|
||||
MovedAttribute("addbase", "urllib", "urllib.response"),
|
||||
MovedAttribute("addclosehook", "urllib", "urllib.response"),
|
||||
MovedAttribute("addinfo", "urllib", "urllib.response"),
|
||||
MovedAttribute("addinfourl", "urllib", "urllib.response"),
|
||||
]
|
||||
for attr in _urllib_response_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_response, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
|
||||
"moves.urllib_response", "moves.urllib.response")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_robotparser(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_robotparser"""
|
||||
|
||||
|
||||
_urllib_robotparser_moved_attributes = [
|
||||
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
|
||||
]
|
||||
for attr in _urllib_robotparser_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
|
||||
"moves.urllib_robotparser", "moves.urllib.robotparser")
|
||||
|
||||
|
||||
class Module_six_moves_urllib(types.ModuleType):
|
||||
|
||||
"""Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
|
||||
__path__ = [] # mark as package
|
||||
parse = _importer._get_module("moves.urllib_parse")
|
||||
error = _importer._get_module("moves.urllib_error")
|
||||
request = _importer._get_module("moves.urllib_request")
|
||||
response = _importer._get_module("moves.urllib_response")
|
||||
robotparser = _importer._get_module("moves.urllib_robotparser")
|
||||
|
||||
def __dir__(self):
|
||||
return ['parse', 'error', 'request', 'response', 'robotparser']
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
|
||||
"moves.urllib")
|
||||
|
||||
|
||||
def add_move(move):
|
||||
"""Add an item to six.moves."""
|
||||
setattr(_MovedItems, move.name, move)
|
||||
|
||||
|
||||
def remove_move(name):
|
||||
"""Remove item from six.moves."""
|
||||
try:
|
||||
delattr(_MovedItems, name)
|
||||
except AttributeError:
|
||||
try:
|
||||
del moves.__dict__[name]
|
||||
except KeyError:
|
||||
raise AttributeError("no such move, %r" % (name,))
|
||||
|
||||
|
||||
if PY3:
|
||||
_meth_func = "__func__"
|
||||
_meth_self = "__self__"
|
||||
|
||||
_func_closure = "__closure__"
|
||||
_func_code = "__code__"
|
||||
_func_defaults = "__defaults__"
|
||||
_func_globals = "__globals__"
|
||||
else:
|
||||
_meth_func = "im_func"
|
||||
_meth_self = "im_self"
|
||||
|
||||
_func_closure = "func_closure"
|
||||
_func_code = "func_code"
|
||||
_func_defaults = "func_defaults"
|
||||
_func_globals = "func_globals"
|
||||
|
||||
|
||||
try:
|
||||
advance_iterator = next
|
||||
except NameError:
|
||||
def advance_iterator(it):
|
||||
return it.next()
|
||||
next = advance_iterator
|
||||
|
||||
|
||||
try:
|
||||
callable = callable
|
||||
except NameError:
|
||||
def callable(obj):
|
||||
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||
|
||||
|
||||
if PY3:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound
|
||||
|
||||
create_bound_method = types.MethodType
|
||||
|
||||
def create_unbound_method(func, cls):
|
||||
return func
|
||||
|
||||
Iterator = object
|
||||
else:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound.im_func
|
||||
|
||||
def create_bound_method(func, obj):
|
||||
return types.MethodType(func, obj, obj.__class__)
|
||||
|
||||
def create_unbound_method(func, cls):
|
||||
return types.MethodType(func, None, cls)
|
||||
|
||||
class Iterator(object):
|
||||
|
||||
def next(self):
|
||||
return type(self).__next__(self)
|
||||
|
||||
callable = callable
|
||||
_add_doc(get_unbound_function,
|
||||
"""Get the function out of a possibly unbound function""")
|
||||
|
||||
|
||||
get_method_function = operator.attrgetter(_meth_func)
|
||||
get_method_self = operator.attrgetter(_meth_self)
|
||||
get_function_closure = operator.attrgetter(_func_closure)
|
||||
get_function_code = operator.attrgetter(_func_code)
|
||||
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||
get_function_globals = operator.attrgetter(_func_globals)
|
||||
|
||||
|
||||
if PY3:
|
||||
def iterkeys(d, **kw):
|
||||
return iter(d.keys(**kw))
|
||||
|
||||
def itervalues(d, **kw):
|
||||
return iter(d.values(**kw))
|
||||
|
||||
def iteritems(d, **kw):
|
||||
return iter(d.items(**kw))
|
||||
|
||||
def iterlists(d, **kw):
|
||||
return iter(d.lists(**kw))
|
||||
|
||||
viewkeys = operator.methodcaller("keys")
|
||||
|
||||
viewvalues = operator.methodcaller("values")
|
||||
|
||||
viewitems = operator.methodcaller("items")
|
||||
else:
|
||||
def iterkeys(d, **kw):
|
||||
return d.iterkeys(**kw)
|
||||
|
||||
def itervalues(d, **kw):
|
||||
return d.itervalues(**kw)
|
||||
|
||||
def iteritems(d, **kw):
|
||||
return d.iteritems(**kw)
|
||||
|
||||
def iterlists(d, **kw):
|
||||
return d.iterlists(**kw)
|
||||
|
||||
viewkeys = operator.methodcaller("viewkeys")
|
||||
|
||||
viewvalues = operator.methodcaller("viewvalues")
|
||||
|
||||
viewitems = operator.methodcaller("viewitems")
|
||||
|
||||
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
|
||||
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
|
||||
_add_doc(iteritems,
|
||||
"Return an iterator over the (key, value) pairs of a dictionary.")
|
||||
_add_doc(iterlists,
|
||||
"Return an iterator over the (key, [values]) pairs of a dictionary.")
|
||||
|
||||
|
||||
if PY3:
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
|
||||
def u(s):
|
||||
return s
|
||||
unichr = chr
|
||||
import struct
|
||||
int2byte = struct.Struct(">B").pack
|
||||
del struct
|
||||
byte2int = operator.itemgetter(0)
|
||||
indexbytes = operator.getitem
|
||||
iterbytes = iter
|
||||
import io
|
||||
StringIO = io.StringIO
|
||||
BytesIO = io.BytesIO
|
||||
_assertCountEqual = "assertCountEqual"
|
||||
if sys.version_info[1] <= 1:
|
||||
_assertRaisesRegex = "assertRaisesRegexp"
|
||||
_assertRegex = "assertRegexpMatches"
|
||||
else:
|
||||
_assertRaisesRegex = "assertRaisesRegex"
|
||||
_assertRegex = "assertRegex"
|
||||
else:
|
||||
def b(s):
|
||||
return s
|
||||
# Workaround for standalone backslash
|
||||
|
||||
def u(s):
|
||||
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
|
||||
unichr = unichr
|
||||
int2byte = chr
|
||||
|
||||
def byte2int(bs):
|
||||
return ord(bs[0])
|
||||
|
||||
def indexbytes(buf, i):
|
||||
return ord(buf[i])
|
||||
iterbytes = functools.partial(itertools.imap, ord)
|
||||
import StringIO
|
||||
StringIO = BytesIO = StringIO.StringIO
|
||||
_assertCountEqual = "assertItemsEqual"
|
||||
_assertRaisesRegex = "assertRaisesRegexp"
|
||||
_assertRegex = "assertRegexpMatches"
|
||||
_add_doc(b, """Byte literal""")
|
||||
_add_doc(u, """Text literal""")
|
||||
|
||||
|
||||
def assertCountEqual(self, *args, **kwargs):
|
||||
return getattr(self, _assertCountEqual)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertRaisesRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
if PY3:
|
||||
exec_ = getattr(moves.builtins, "exec")
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
try:
|
||||
if value is None:
|
||||
value = tp()
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
finally:
|
||||
value = None
|
||||
tb = None
|
||||
|
||||
else:
|
||||
def exec_(_code_, _globs_=None, _locs_=None):
|
||||
"""Execute code in a namespace."""
|
||||
if _globs_ is None:
|
||||
frame = sys._getframe(1)
|
||||
_globs_ = frame.f_globals
|
||||
if _locs_ is None:
|
||||
_locs_ = frame.f_locals
|
||||
del frame
|
||||
elif _locs_ is None:
|
||||
_locs_ = _globs_
|
||||
exec("""exec _code_ in _globs_, _locs_""")
|
||||
|
||||
exec_("""def reraise(tp, value, tb=None):
|
||||
try:
|
||||
raise tp, value, tb
|
||||
finally:
|
||||
tb = None
|
||||
""")
|
||||
|
||||
|
||||
if sys.version_info[:2] == (3, 2):
|
||||
exec_("""def raise_from(value, from_value):
|
||||
try:
|
||||
if from_value is None:
|
||||
raise value
|
||||
raise value from from_value
|
||||
finally:
|
||||
value = None
|
||||
""")
|
||||
elif sys.version_info[:2] > (3, 2):
|
||||
exec_("""def raise_from(value, from_value):
|
||||
try:
|
||||
raise value from from_value
|
||||
finally:
|
||||
value = None
|
||||
""")
|
||||
else:
|
||||
def raise_from(value, from_value):
|
||||
raise value
|
||||
|
||||
|
||||
print_ = getattr(moves.builtins, "print", None)
|
||||
if print_ is None:
|
||||
def print_(*args, **kwargs):
|
||||
"""The new-style print function for Python 2.4 and 2.5."""
|
||||
fp = kwargs.pop("file", sys.stdout)
|
||||
if fp is None:
|
||||
return
|
||||
|
||||
def write(data):
|
||||
if not isinstance(data, basestring):
|
||||
data = str(data)
|
||||
# If the file has an encoding, encode unicode with it.
|
||||
if (isinstance(fp, file) and
|
||||
isinstance(data, unicode) and
|
||||
fp.encoding is not None):
|
||||
errors = getattr(fp, "errors", None)
|
||||
if errors is None:
|
||||
errors = "strict"
|
||||
data = data.encode(fp.encoding, errors)
|
||||
fp.write(data)
|
||||
want_unicode = False
|
||||
sep = kwargs.pop("sep", None)
|
||||
if sep is not None:
|
||||
if isinstance(sep, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(sep, str):
|
||||
raise TypeError("sep must be None or a string")
|
||||
end = kwargs.pop("end", None)
|
||||
if end is not None:
|
||||
if isinstance(end, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(end, str):
|
||||
raise TypeError("end must be None or a string")
|
||||
if kwargs:
|
||||
raise TypeError("invalid keyword arguments to print()")
|
||||
if not want_unicode:
|
||||
for arg in args:
|
||||
if isinstance(arg, unicode):
|
||||
want_unicode = True
|
||||
break
|
||||
if want_unicode:
|
||||
newline = unicode("\n")
|
||||
space = unicode(" ")
|
||||
else:
|
||||
newline = "\n"
|
||||
space = " "
|
||||
if sep is None:
|
||||
sep = space
|
||||
if end is None:
|
||||
end = newline
|
||||
for i, arg in enumerate(args):
|
||||
if i:
|
||||
write(sep)
|
||||
write(arg)
|
||||
write(end)
|
||||
if sys.version_info[:2] < (3, 3):
|
||||
_print = print_
|
||||
|
||||
def print_(*args, **kwargs):
|
||||
fp = kwargs.get("file", sys.stdout)
|
||||
flush = kwargs.pop("flush", False)
|
||||
_print(*args, **kwargs)
|
||||
if flush and fp is not None:
|
||||
fp.flush()
|
||||
|
||||
_add_doc(reraise, """Reraise an exception.""")
|
||||
|
||||
if sys.version_info[0:2] < (3, 4):
|
||||
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||
updated=functools.WRAPPER_UPDATES):
|
||||
def wrapper(f):
|
||||
f = functools.wraps(wrapped, assigned, updated)(f)
|
||||
f.__wrapped__ = wrapped
|
||||
return f
|
||||
return wrapper
|
||||
else:
|
||||
wraps = functools.wraps
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass."""
|
||||
# This requires a bit of explanation: the basic idea is to make a dummy
|
||||
# metaclass for one level of class instantiation that replaces itself with
|
||||
# the actual metaclass.
|
||||
class metaclass(type):
|
||||
|
||||
def __new__(cls, name, this_bases, d):
|
||||
return meta(name, bases, d)
|
||||
|
||||
@classmethod
|
||||
def __prepare__(cls, name, this_bases):
|
||||
return meta.__prepare__(name, bases)
|
||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||
|
||||
|
||||
def add_metaclass(metaclass):
|
||||
"""Class decorator for creating a class with a metaclass."""
|
||||
def wrapper(cls):
|
||||
orig_vars = cls.__dict__.copy()
|
||||
slots = orig_vars.get('__slots__')
|
||||
if slots is not None:
|
||||
if isinstance(slots, str):
|
||||
slots = [slots]
|
||||
for slots_var in slots:
|
||||
orig_vars.pop(slots_var)
|
||||
orig_vars.pop('__dict__', None)
|
||||
orig_vars.pop('__weakref__', None)
|
||||
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
||||
return wrapper
|
||||
|
||||
|
||||
def python_2_unicode_compatible(klass):
|
||||
"""
|
||||
A decorator that defines __unicode__ and __str__ methods under Python 2.
|
||||
Under Python 3 it does nothing.
|
||||
|
||||
To support Python 2 and 3 with a single code base, define a __str__ method
|
||||
returning text and apply this decorator to the class.
|
||||
"""
|
||||
if PY2:
|
||||
if '__str__' not in klass.__dict__:
|
||||
raise ValueError("@python_2_unicode_compatible cannot be applied "
|
||||
"to %s because it doesn't define __str__()." %
|
||||
klass.__name__)
|
||||
klass.__unicode__ = klass.__str__
|
||||
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
|
||||
return klass
|
||||
|
||||
|
||||
# Complete the moves implementation.
|
||||
# This code is at the end of this module to speed up module loading.
|
||||
# Turn this module into a package.
|
||||
__path__ = [] # required for PEP 302 and PEP 451
|
||||
__package__ = __name__ # see PEP 366 @ReservedAssignment
|
||||
if globals().get("__spec__") is not None:
|
||||
__spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
|
||||
# Remove other six meta path importers, since they cause problems. This can
|
||||
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
|
||||
# this for some reason.)
|
||||
if sys.meta_path:
|
||||
for i, importer in enumerate(sys.meta_path):
|
||||
# Here's some real nastiness: Another "instance" of the six module might
|
||||
# be floating around. Therefore, we can't use isinstance() to check for
|
||||
# the six meta path importer, since the other six instance will have
|
||||
# inserted an importer with different class.
|
||||
if (type(importer).__name__ == "_SixMetaPathImporter" and
|
||||
importer.name == __name__):
|
||||
del sys.meta_path[i]
|
||||
break
|
||||
del i, importer
|
||||
# Finally, add the importer to the meta path import hook.
|
||||
sys.meta_path.append(_importer)
|
0
resources/lib/libraries/dateutil/test/__init__.py
Normal file
0
resources/lib/libraries/dateutil/test/__init__.py
Normal file
275
resources/lib/libraries/dateutil/test/_common.py
Normal file
275
resources/lib/libraries/dateutil/test/_common.py
Normal file
|
@ -0,0 +1,275 @@
|
|||
from __future__ import unicode_literals
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
import warnings
|
||||
import tempfile
|
||||
import pickle
|
||||
|
||||
|
||||
class WarningTestMixin(object):
|
||||
# Based on https://stackoverflow.com/a/12935176/467366
|
||||
class _AssertWarnsContext(warnings.catch_warnings):
|
||||
def __init__(self, expected_warnings, parent, **kwargs):
|
||||
super(WarningTestMixin._AssertWarnsContext, self).__init__(**kwargs)
|
||||
|
||||
self.parent = parent
|
||||
try:
|
||||
self.expected_warnings = list(expected_warnings)
|
||||
except TypeError:
|
||||
self.expected_warnings = [expected_warnings]
|
||||
|
||||
self._warning_log = []
|
||||
|
||||
def __enter__(self, *args, **kwargs):
|
||||
rv = super(WarningTestMixin._AssertWarnsContext, self).__enter__(*args, **kwargs)
|
||||
|
||||
if self._showwarning is not self._module.showwarning:
|
||||
super_showwarning = self._module.showwarning
|
||||
else:
|
||||
super_showwarning = None
|
||||
|
||||
def showwarning(*args, **kwargs):
|
||||
if super_showwarning is not None:
|
||||
super_showwarning(*args, **kwargs)
|
||||
|
||||
self._warning_log.append(warnings.WarningMessage(*args, **kwargs))
|
||||
|
||||
self._module.showwarning = showwarning
|
||||
return rv
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
super(WarningTestMixin._AssertWarnsContext, self).__exit__(self, *args, **kwargs)
|
||||
|
||||
self.parent.assertTrue(any(issubclass(item.category, warning)
|
||||
for warning in self.expected_warnings
|
||||
for item in self._warning_log))
|
||||
|
||||
def assertWarns(self, warning, callable=None, *args, **kwargs):
|
||||
warnings.simplefilter('always')
|
||||
context = self.__class__._AssertWarnsContext(warning, self)
|
||||
if callable is None:
|
||||
return context
|
||||
else:
|
||||
with context:
|
||||
callable(*args, **kwargs)
|
||||
|
||||
|
||||
class PicklableMixin(object):
|
||||
def _get_nobj_bytes(self, obj, dump_kwargs, load_kwargs):
|
||||
"""
|
||||
Pickle and unpickle an object using ``pickle.dumps`` / ``pickle.loads``
|
||||
"""
|
||||
pkl = pickle.dumps(obj, **dump_kwargs)
|
||||
return pickle.loads(pkl, **load_kwargs)
|
||||
|
||||
def _get_nobj_file(self, obj, dump_kwargs, load_kwargs):
|
||||
"""
|
||||
Pickle and unpickle an object using ``pickle.dump`` / ``pickle.load`` on
|
||||
a temporary file.
|
||||
"""
|
||||
with tempfile.TemporaryFile('w+b') as pkl:
|
||||
pickle.dump(obj, pkl, **dump_kwargs)
|
||||
pkl.seek(0) # Reset the file to the beginning to read it
|
||||
nobj = pickle.load(pkl, **load_kwargs)
|
||||
|
||||
return nobj
|
||||
|
||||
def assertPicklable(self, obj, singleton=False, asfile=False,
|
||||
dump_kwargs=None, load_kwargs=None):
|
||||
"""
|
||||
Assert that an object can be pickled and unpickled. This assertion
|
||||
assumes that the desired behavior is that the unpickled object compares
|
||||
equal to the original object, but is not the same object.
|
||||
"""
|
||||
get_nobj = self._get_nobj_file if asfile else self._get_nobj_bytes
|
||||
dump_kwargs = dump_kwargs or {}
|
||||
load_kwargs = load_kwargs or {}
|
||||
|
||||
nobj = get_nobj(obj, dump_kwargs, load_kwargs)
|
||||
if not singleton:
|
||||
self.assertIsNot(obj, nobj)
|
||||
self.assertEqual(obj, nobj)
|
||||
|
||||
|
||||
class TZContextBase(object):
|
||||
"""
|
||||
Base class for a context manager which allows changing of time zones.
|
||||
|
||||
Subclasses may define a guard variable to either block or or allow time
|
||||
zone changes by redefining ``_guard_var_name`` and ``_guard_allows_change``.
|
||||
The default is that the guard variable must be affirmatively set.
|
||||
|
||||
Subclasses must define ``get_current_tz`` and ``set_current_tz``.
|
||||
"""
|
||||
_guard_var_name = "DATEUTIL_MAY_CHANGE_TZ"
|
||||
_guard_allows_change = True
|
||||
|
||||
def __init__(self, tzval):
|
||||
self.tzval = tzval
|
||||
self._old_tz = None
|
||||
|
||||
@classmethod
|
||||
def tz_change_allowed(cls):
|
||||
"""
|
||||
Class method used to query whether or not this class allows time zone
|
||||
changes.
|
||||
"""
|
||||
guard = bool(os.environ.get(cls._guard_var_name, False))
|
||||
|
||||
# _guard_allows_change gives the "default" behavior - if True, the
|
||||
# guard is overcoming a block. If false, the guard is causing a block.
|
||||
# Whether tz_change is allowed is therefore the XNOR of the two.
|
||||
return guard == cls._guard_allows_change
|
||||
|
||||
@classmethod
|
||||
def tz_change_disallowed_message(cls):
|
||||
""" Generate instructions on how to allow tz changes """
|
||||
msg = ('Changing time zone not allowed. Set {envar} to {gval} '
|
||||
'if you would like to allow this behavior')
|
||||
|
||||
return msg.format(envar=cls._guard_var_name,
|
||||
gval=cls._guard_allows_change)
|
||||
|
||||
def __enter__(self):
|
||||
if not self.tz_change_allowed():
|
||||
raise ValueError(self.tz_change_disallowed_message())
|
||||
|
||||
self._old_tz = self.get_current_tz()
|
||||
self.set_current_tz(self.tzval)
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
if self._old_tz is not None:
|
||||
self.set_current_tz(self._old_tz)
|
||||
|
||||
self._old_tz = None
|
||||
|
||||
def get_current_tz(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_current_tz(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class TZEnvContext(TZContextBase):
|
||||
"""
|
||||
Context manager that temporarily sets the `TZ` variable (for use on
|
||||
*nix-like systems). Because the effect is local to the shell anyway, this
|
||||
will apply *unless* a guard is set.
|
||||
|
||||
If you do not want the TZ environment variable set, you may set the
|
||||
``DATEUTIL_MAY_NOT_CHANGE_TZ_VAR`` variable to a truthy value.
|
||||
"""
|
||||
_guard_var_name = "DATEUTIL_MAY_NOT_CHANGE_TZ_VAR"
|
||||
_guard_allows_change = False
|
||||
|
||||
def get_current_tz(self):
|
||||
return os.environ.get('TZ', UnsetTz)
|
||||
|
||||
def set_current_tz(self, tzval):
|
||||
if tzval is UnsetTz and 'TZ' in os.environ:
|
||||
del os.environ['TZ']
|
||||
else:
|
||||
os.environ['TZ'] = tzval
|
||||
|
||||
time.tzset()
|
||||
|
||||
|
||||
class TZWinContext(TZContextBase):
|
||||
"""
|
||||
Context manager for changing local time zone on Windows.
|
||||
|
||||
Because the effect of this is system-wide and global, it may have
|
||||
unintended side effect. Set the ``DATEUTIL_MAY_CHANGE_TZ`` environment
|
||||
variable to a truthy value before using this context manager.
|
||||
"""
|
||||
def get_current_tz(self):
|
||||
p = subprocess.Popen(['tzutil', '/g'], stdout=subprocess.PIPE)
|
||||
|
||||
ctzname, err = p.communicate()
|
||||
ctzname = ctzname.decode() # Popen returns
|
||||
|
||||
if p.returncode:
|
||||
raise OSError('Failed to get current time zone: ' + err)
|
||||
|
||||
return ctzname
|
||||
|
||||
def set_current_tz(self, tzname):
|
||||
p = subprocess.Popen('tzutil /s "' + tzname + '"')
|
||||
|
||||
out, err = p.communicate()
|
||||
|
||||
if p.returncode:
|
||||
raise OSError('Failed to set current time zone: ' +
|
||||
(err or 'Unknown error.'))
|
||||
|
||||
|
||||
###
|
||||
# Utility classes
|
||||
class NotAValueClass(object):
|
||||
"""
|
||||
A class analogous to NaN that has operations defined for any type.
|
||||
"""
|
||||
def _op(self, other):
|
||||
return self # Operation with NotAValue returns NotAValue
|
||||
|
||||
def _cmp(self, other):
|
||||
return False
|
||||
|
||||
__add__ = __radd__ = _op
|
||||
__sub__ = __rsub__ = _op
|
||||
__mul__ = __rmul__ = _op
|
||||
__div__ = __rdiv__ = _op
|
||||
__truediv__ = __rtruediv__ = _op
|
||||
__floordiv__ = __rfloordiv__ = _op
|
||||
|
||||
__lt__ = __rlt__ = _op
|
||||
__gt__ = __rgt__ = _op
|
||||
__eq__ = __req__ = _op
|
||||
__le__ = __rle__ = _op
|
||||
__ge__ = __rge__ = _op
|
||||
|
||||
|
||||
NotAValue = NotAValueClass()
|
||||
|
||||
|
||||
class ComparesEqualClass(object):
|
||||
"""
|
||||
A class that is always equal to whatever you compare it to.
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return False
|
||||
|
||||
def __le__(self, other):
|
||||
return True
|
||||
|
||||
def __ge__(self, other):
|
||||
return True
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
||||
def __gt__(self, other):
|
||||
return False
|
||||
|
||||
__req__ = __eq__
|
||||
__rne__ = __ne__
|
||||
__rle__ = __le__
|
||||
__rge__ = __ge__
|
||||
__rlt__ = __lt__
|
||||
__rgt__ = __gt__
|
||||
|
||||
|
||||
ComparesEqual = ComparesEqualClass()
|
||||
|
||||
|
||||
class UnsetTzClass(object):
|
||||
""" Sentinel class for unset time zone variable """
|
||||
pass
|
||||
|
||||
|
||||
UnsetTz = UnsetTzClass()
|
|
@ -0,0 +1,27 @@
|
|||
from hypothesis import given, assume
|
||||
from hypothesis import strategies as st
|
||||
|
||||
from dateutil import tz
|
||||
from dateutil.parser import isoparse
|
||||
|
||||
import pytest
|
||||
|
||||
# Strategies
|
||||
TIME_ZONE_STRATEGY = st.sampled_from([None, tz.tzutc()] +
|
||||
[tz.gettz(zname) for zname in ('US/Eastern', 'US/Pacific',
|
||||
'Australia/Sydney', 'Europe/London')])
|
||||
ASCII_STRATEGY = st.characters(max_codepoint=127)
|
||||
|
||||
|
||||
@pytest.mark.isoparser
|
||||
@given(dt=st.datetimes(timezones=TIME_ZONE_STRATEGY), sep=ASCII_STRATEGY)
|
||||
def test_timespec_auto(dt, sep):
|
||||
if dt.tzinfo is not None:
|
||||
# Assume offset has no sub-second components
|
||||
assume(dt.utcoffset().total_seconds() % 60 == 0)
|
||||
|
||||
sep = str(sep) # Python 2.7 requires bytes
|
||||
dtstr = dt.isoformat(sep=sep)
|
||||
dt_rt = isoparse(dtstr)
|
||||
|
||||
assert dt_rt == dt
|
|
@ -0,0 +1,22 @@
|
|||
from hypothesis.strategies import integers
|
||||
from hypothesis import given
|
||||
|
||||
import pytest
|
||||
|
||||
from dateutil.parser import parserinfo
|
||||
|
||||
|
||||
@pytest.mark.parserinfo
|
||||
@given(integers(min_value=100, max_value=9999))
|
||||
def test_convertyear(n):
|
||||
assert n == parserinfo().convertyear(n)
|
||||
|
||||
|
||||
@pytest.mark.parserinfo
|
||||
@given(integers(min_value=-50,
|
||||
max_value=49))
|
||||
def test_convertyear_no_specified_century(n):
|
||||
p = parserinfo()
|
||||
new_year = p._year + n
|
||||
result = p.convertyear(new_year % 100, century_specified=False)
|
||||
assert result == new_year
|
95
resources/lib/libraries/dateutil/test/test_easter.py
Normal file
95
resources/lib/libraries/dateutil/test/test_easter.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
from dateutil.easter import easter
|
||||
from dateutil.easter import EASTER_WESTERN, EASTER_ORTHODOX, EASTER_JULIAN
|
||||
|
||||
from datetime import date
|
||||
import unittest
|
||||
|
||||
# List of easters between 1990 and 2050
|
||||
western_easter_dates = [
|
||||
date(1990, 4, 15), date(1991, 3, 31), date(1992, 4, 19), date(1993, 4, 11),
|
||||
date(1994, 4, 3), date(1995, 4, 16), date(1996, 4, 7), date(1997, 3, 30),
|
||||
date(1998, 4, 12), date(1999, 4, 4),
|
||||
|
||||
date(2000, 4, 23), date(2001, 4, 15), date(2002, 3, 31), date(2003, 4, 20),
|
||||
date(2004, 4, 11), date(2005, 3, 27), date(2006, 4, 16), date(2007, 4, 8),
|
||||
date(2008, 3, 23), date(2009, 4, 12),
|
||||
|
||||
date(2010, 4, 4), date(2011, 4, 24), date(2012, 4, 8), date(2013, 3, 31),
|
||||
date(2014, 4, 20), date(2015, 4, 5), date(2016, 3, 27), date(2017, 4, 16),
|
||||
date(2018, 4, 1), date(2019, 4, 21),
|
||||
|
||||
date(2020, 4, 12), date(2021, 4, 4), date(2022, 4, 17), date(2023, 4, 9),
|
||||
date(2024, 3, 31), date(2025, 4, 20), date(2026, 4, 5), date(2027, 3, 28),
|
||||
date(2028, 4, 16), date(2029, 4, 1),
|
||||
|
||||
date(2030, 4, 21), date(2031, 4, 13), date(2032, 3, 28), date(2033, 4, 17),
|
||||
date(2034, 4, 9), date(2035, 3, 25), date(2036, 4, 13), date(2037, 4, 5),
|
||||
date(2038, 4, 25), date(2039, 4, 10),
|
||||
|
||||
date(2040, 4, 1), date(2041, 4, 21), date(2042, 4, 6), date(2043, 3, 29),
|
||||
date(2044, 4, 17), date(2045, 4, 9), date(2046, 3, 25), date(2047, 4, 14),
|
||||
date(2048, 4, 5), date(2049, 4, 18), date(2050, 4, 10)
|
||||
]
|
||||
|
||||
orthodox_easter_dates = [
|
||||
date(1990, 4, 15), date(1991, 4, 7), date(1992, 4, 26), date(1993, 4, 18),
|
||||
date(1994, 5, 1), date(1995, 4, 23), date(1996, 4, 14), date(1997, 4, 27),
|
||||
date(1998, 4, 19), date(1999, 4, 11),
|
||||
|
||||
date(2000, 4, 30), date(2001, 4, 15), date(2002, 5, 5), date(2003, 4, 27),
|
||||
date(2004, 4, 11), date(2005, 5, 1), date(2006, 4, 23), date(2007, 4, 8),
|
||||
date(2008, 4, 27), date(2009, 4, 19),
|
||||
|
||||
date(2010, 4, 4), date(2011, 4, 24), date(2012, 4, 15), date(2013, 5, 5),
|
||||
date(2014, 4, 20), date(2015, 4, 12), date(2016, 5, 1), date(2017, 4, 16),
|
||||
date(2018, 4, 8), date(2019, 4, 28),
|
||||
|
||||
date(2020, 4, 19), date(2021, 5, 2), date(2022, 4, 24), date(2023, 4, 16),
|
||||
date(2024, 5, 5), date(2025, 4, 20), date(2026, 4, 12), date(2027, 5, 2),
|
||||
date(2028, 4, 16), date(2029, 4, 8),
|
||||
|
||||
date(2030, 4, 28), date(2031, 4, 13), date(2032, 5, 2), date(2033, 4, 24),
|
||||
date(2034, 4, 9), date(2035, 4, 29), date(2036, 4, 20), date(2037, 4, 5),
|
||||
date(2038, 4, 25), date(2039, 4, 17),
|
||||
|
||||
date(2040, 5, 6), date(2041, 4, 21), date(2042, 4, 13), date(2043, 5, 3),
|
||||
date(2044, 4, 24), date(2045, 4, 9), date(2046, 4, 29), date(2047, 4, 21),
|
||||
date(2048, 4, 5), date(2049, 4, 25), date(2050, 4, 17)
|
||||
]
|
||||
|
||||
# A random smattering of Julian dates.
|
||||
# Pulled values from http://www.kevinlaughery.com/east4099.html
|
||||
julian_easter_dates = [
|
||||
date( 326, 4, 3), date( 375, 4, 5), date( 492, 4, 5), date( 552, 3, 31),
|
||||
date( 562, 4, 9), date( 569, 4, 21), date( 597, 4, 14), date( 621, 4, 19),
|
||||
date( 636, 3, 31), date( 655, 3, 29), date( 700, 4, 11), date( 725, 4, 8),
|
||||
date( 750, 3, 29), date( 782, 4, 7), date( 835, 4, 18), date( 849, 4, 14),
|
||||
date( 867, 3, 30), date( 890, 4, 12), date( 922, 4, 21), date( 934, 4, 6),
|
||||
date(1049, 3, 26), date(1058, 4, 19), date(1113, 4, 6), date(1119, 3, 30),
|
||||
date(1242, 4, 20), date(1255, 3, 28), date(1257, 4, 8), date(1258, 3, 24),
|
||||
date(1261, 4, 24), date(1278, 4, 17), date(1333, 4, 4), date(1351, 4, 17),
|
||||
date(1371, 4, 6), date(1391, 3, 26), date(1402, 3, 26), date(1412, 4, 3),
|
||||
date(1439, 4, 5), date(1445, 3, 28), date(1531, 4, 9), date(1555, 4, 14)
|
||||
]
|
||||
|
||||
|
||||
class EasterTest(unittest.TestCase):
|
||||
def testEasterWestern(self):
|
||||
for easter_date in western_easter_dates:
|
||||
self.assertEqual(easter_date,
|
||||
easter(easter_date.year, EASTER_WESTERN))
|
||||
|
||||
def testEasterOrthodox(self):
|
||||
for easter_date in orthodox_easter_dates:
|
||||
self.assertEqual(easter_date,
|
||||
easter(easter_date.year, EASTER_ORTHODOX))
|
||||
|
||||
def testEasterJulian(self):
|
||||
for easter_date in julian_easter_dates:
|
||||
self.assertEqual(easter_date,
|
||||
easter(easter_date.year, EASTER_JULIAN))
|
||||
|
||||
def testEasterBadMethod(self):
|
||||
# Invalid methods raise ValueError
|
||||
with self.assertRaises(ValueError):
|
||||
easter(1975, 4)
|
33
resources/lib/libraries/dateutil/test/test_import_star.py
Normal file
33
resources/lib/libraries/dateutil/test/test_import_star.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""Test for the "import *" functionality.
|
||||
|
||||
As imort * can be only done at module level, it has been added in a separate file
|
||||
"""
|
||||
import unittest
|
||||
|
||||
prev_locals = list(locals())
|
||||
from dateutil import *
|
||||
new_locals = {name:value for name,value in locals().items()
|
||||
if name not in prev_locals}
|
||||
new_locals.pop('prev_locals')
|
||||
|
||||
class ImportStarTest(unittest.TestCase):
|
||||
""" Test that `from dateutil import *` adds the modules in __all__ locally"""
|
||||
|
||||
def testImportedModules(self):
|
||||
import dateutil.easter
|
||||
import dateutil.parser
|
||||
import dateutil.relativedelta
|
||||
import dateutil.rrule
|
||||
import dateutil.tz
|
||||
import dateutil.utils
|
||||
import dateutil.zoneinfo
|
||||
|
||||
self.assertEquals(dateutil.easter, new_locals.pop("easter"))
|
||||
self.assertEquals(dateutil.parser, new_locals.pop("parser"))
|
||||
self.assertEquals(dateutil.relativedelta, new_locals.pop("relativedelta"))
|
||||
self.assertEquals(dateutil.rrule, new_locals.pop("rrule"))
|
||||
self.assertEquals(dateutil.tz, new_locals.pop("tz"))
|
||||
self.assertEquals(dateutil.utils, new_locals.pop("utils"))
|
||||
self.assertEquals(dateutil.zoneinfo, new_locals.pop("zoneinfo"))
|
||||
|
||||
self.assertFalse(new_locals)
|
166
resources/lib/libraries/dateutil/test/test_imports.py
Normal file
166
resources/lib/libraries/dateutil/test/test_imports.py
Normal file
|
@ -0,0 +1,166 @@
|
|||
import sys
|
||||
import unittest
|
||||
|
||||
class ImportVersionTest(unittest.TestCase):
|
||||
""" Test that dateutil.__version__ can be imported"""
|
||||
|
||||
def testImportVersionStr(self):
|
||||
from dateutil import __version__
|
||||
|
||||
def testImportRoot(self):
|
||||
import dateutil
|
||||
|
||||
self.assertTrue(hasattr(dateutil, '__version__'))
|
||||
|
||||
|
||||
class ImportEasterTest(unittest.TestCase):
|
||||
""" Test that dateutil.easter-related imports work properly """
|
||||
|
||||
def testEasterDirect(self):
|
||||
import dateutil.easter
|
||||
|
||||
def testEasterFrom(self):
|
||||
from dateutil import easter
|
||||
|
||||
def testEasterStar(self):
|
||||
from dateutil.easter import easter
|
||||
|
||||
|
||||
class ImportParserTest(unittest.TestCase):
|
||||
""" Test that dateutil.parser-related imports work properly """
|
||||
def testParserDirect(self):
|
||||
import dateutil.parser
|
||||
|
||||
def testParserFrom(self):
|
||||
from dateutil import parser
|
||||
|
||||
def testParserAll(self):
|
||||
# All interface
|
||||
from dateutil.parser import parse
|
||||
from dateutil.parser import parserinfo
|
||||
|
||||
# Other public classes
|
||||
from dateutil.parser import parser
|
||||
|
||||
for var in (parse, parserinfo, parser):
|
||||
self.assertIsNot(var, None)
|
||||
|
||||
|
||||
class ImportRelativeDeltaTest(unittest.TestCase):
|
||||
""" Test that dateutil.relativedelta-related imports work properly """
|
||||
def testRelativeDeltaDirect(self):
|
||||
import dateutil.relativedelta
|
||||
|
||||
def testRelativeDeltaFrom(self):
|
||||
from dateutil import relativedelta
|
||||
|
||||
def testRelativeDeltaAll(self):
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from dateutil.relativedelta import MO, TU, WE, TH, FR, SA, SU
|
||||
|
||||
for var in (relativedelta, MO, TU, WE, TH, FR, SA, SU):
|
||||
self.assertIsNot(var, None)
|
||||
|
||||
# In the public interface but not in all
|
||||
from dateutil.relativedelta import weekday
|
||||
self.assertIsNot(weekday, None)
|
||||
|
||||
|
||||
class ImportRRuleTest(unittest.TestCase):
|
||||
""" Test that dateutil.rrule related imports work properly """
|
||||
def testRRuleDirect(self):
|
||||
import dateutil.rrule
|
||||
|
||||
def testRRuleFrom(self):
|
||||
from dateutil import rrule
|
||||
|
||||
def testRRuleAll(self):
|
||||
from dateutil.rrule import rrule
|
||||
from dateutil.rrule import rruleset
|
||||
from dateutil.rrule import rrulestr
|
||||
from dateutil.rrule import YEARLY, MONTHLY, WEEKLY, DAILY
|
||||
from dateutil.rrule import HOURLY, MINUTELY, SECONDLY
|
||||
from dateutil.rrule import MO, TU, WE, TH, FR, SA, SU
|
||||
|
||||
rr_all = (rrule, rruleset, rrulestr,
|
||||
YEARLY, MONTHLY, WEEKLY, DAILY,
|
||||
HOURLY, MINUTELY, SECONDLY,
|
||||
MO, TU, WE, TH, FR, SA, SU)
|
||||
|
||||
for var in rr_all:
|
||||
self.assertIsNot(var, None)
|
||||
|
||||
# In the public interface but not in all
|
||||
from dateutil.rrule import weekday
|
||||
self.assertIsNot(weekday, None)
|
||||
|
||||
|
||||
class ImportTZTest(unittest.TestCase):
|
||||
""" Test that dateutil.tz related imports work properly """
|
||||
def testTzDirect(self):
|
||||
import dateutil.tz
|
||||
|
||||
def testTzFrom(self):
|
||||
from dateutil import tz
|
||||
|
||||
def testTzAll(self):
|
||||
from dateutil.tz import tzutc
|
||||
from dateutil.tz import tzoffset
|
||||
from dateutil.tz import tzlocal
|
||||
from dateutil.tz import tzfile
|
||||
from dateutil.tz import tzrange
|
||||
from dateutil.tz import tzstr
|
||||
from dateutil.tz import tzical
|
||||
from dateutil.tz import gettz
|
||||
from dateutil.tz import tzwin
|
||||
from dateutil.tz import tzwinlocal
|
||||
from dateutil.tz import UTC
|
||||
from dateutil.tz import datetime_ambiguous
|
||||
from dateutil.tz import datetime_exists
|
||||
from dateutil.tz import resolve_imaginary
|
||||
|
||||
tz_all = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
|
||||
"tzstr", "tzical", "gettz", "datetime_ambiguous",
|
||||
"datetime_exists", "resolve_imaginary", "UTC"]
|
||||
|
||||
tz_all += ["tzwin", "tzwinlocal"] if sys.platform.startswith("win") else []
|
||||
lvars = locals()
|
||||
|
||||
for var in tz_all:
|
||||
self.assertIsNot(lvars[var], None)
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith('win'), "Requires Windows")
|
||||
class ImportTZWinTest(unittest.TestCase):
|
||||
""" Test that dateutil.tzwin related imports work properly """
|
||||
def testTzwinDirect(self):
|
||||
import dateutil.tzwin
|
||||
|
||||
def testTzwinFrom(self):
|
||||
from dateutil import tzwin
|
||||
|
||||
def testTzwinStar(self):
|
||||
from dateutil.tzwin import tzwin
|
||||
from dateutil.tzwin import tzwinlocal
|
||||
|
||||
tzwin_all = [tzwin, tzwinlocal]
|
||||
|
||||
for var in tzwin_all:
|
||||
self.assertIsNot(var, None)
|
||||
|
||||
|
||||
class ImportZoneInfoTest(unittest.TestCase):
|
||||
def testZoneinfoDirect(self):
|
||||
import dateutil.zoneinfo
|
||||
|
||||
def testZoneinfoFrom(self):
|
||||
from dateutil import zoneinfo
|
||||
|
||||
def testZoneinfoStar(self):
|
||||
from dateutil.zoneinfo import gettz
|
||||
from dateutil.zoneinfo import gettz_db_metadata
|
||||
from dateutil.zoneinfo import rebuild
|
||||
|
||||
zi_all = (gettz, gettz_db_metadata, rebuild)
|
||||
|
||||
for var in zi_all:
|
||||
self.assertIsNot(var, None)
|
95
resources/lib/libraries/dateutil/test/test_internals.py
Normal file
95
resources/lib/libraries/dateutil/test/test_internals.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for implementation details, not necessarily part of the user-facing
|
||||
API.
|
||||
|
||||
The motivating case for these tests is #483, where we want to smoke-test
|
||||
code that may be difficult to reach through the standard API calls.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from dateutil.parser._parser import _ymd
|
||||
from dateutil import tz
|
||||
|
||||
IS_PY32 = sys.version_info[0:2] == (3, 2)
|
||||
|
||||
|
||||
class TestYMD(unittest.TestCase):
|
||||
|
||||
# @pytest.mark.smoke
|
||||
def test_could_be_day(self):
|
||||
ymd = _ymd('foo bar 124 baz')
|
||||
|
||||
ymd.append(2, 'M')
|
||||
assert ymd.has_month
|
||||
assert not ymd.has_year
|
||||
assert ymd.could_be_day(4)
|
||||
assert not ymd.could_be_day(-6)
|
||||
assert not ymd.could_be_day(32)
|
||||
|
||||
# Assumes leapyear
|
||||
assert ymd.could_be_day(29)
|
||||
|
||||
ymd.append(1999)
|
||||
assert ymd.has_year
|
||||
assert not ymd.could_be_day(29)
|
||||
|
||||
ymd.append(16, 'D')
|
||||
assert ymd.has_day
|
||||
assert not ymd.could_be_day(1)
|
||||
|
||||
ymd = _ymd('foo bar 124 baz')
|
||||
ymd.append(1999)
|
||||
assert ymd.could_be_day(31)
|
||||
|
||||
|
||||
###
|
||||
# Test that private interfaces in _parser are deprecated properly
|
||||
@pytest.mark.skipif(IS_PY32, reason='pytest.warns not supported on Python 3.2')
|
||||
def test_parser_private_warns():
|
||||
from dateutil.parser import _timelex, _tzparser
|
||||
from dateutil.parser import _parsetz
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
_tzparser()
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
_timelex('2014-03-03')
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
_parsetz('+05:00')
|
||||
|
||||
|
||||
@pytest.mark.skipif(IS_PY32, reason='pytest.warns not supported on Python 3.2')
|
||||
def test_parser_parser_private_not_warns():
|
||||
from dateutil.parser._parser import _timelex, _tzparser
|
||||
from dateutil.parser._parser import _parsetz
|
||||
|
||||
with pytest.warns(None) as recorder:
|
||||
_tzparser()
|
||||
assert len(recorder) == 0
|
||||
|
||||
with pytest.warns(None) as recorder:
|
||||
_timelex('2014-03-03')
|
||||
|
||||
assert len(recorder) == 0
|
||||
|
||||
with pytest.warns(None) as recorder:
|
||||
_parsetz('+05:00')
|
||||
assert len(recorder) == 0
|
||||
|
||||
|
||||
@pytest.mark.tzstr
|
||||
def test_tzstr_internal_timedeltas():
|
||||
with pytest.warns(tz.DeprecatedTzFormatWarning):
|
||||
tz1 = tz.tzstr("EST5EDT,5,4,0,7200,11,-3,0,7200")
|
||||
|
||||
with pytest.warns(tz.DeprecatedTzFormatWarning):
|
||||
tz2 = tz.tzstr("EST5EDT,4,1,0,7200,10,-1,0,7200")
|
||||
|
||||
assert tz1._start_delta != tz2._start_delta
|
||||
assert tz1._end_delta != tz2._end_delta
|
482
resources/lib/libraries/dateutil/test/test_isoparser.py
Normal file
482
resources/lib/libraries/dateutil/test/test_isoparser.py
Normal file
|
@ -0,0 +1,482 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from datetime import datetime, timedelta, date, time
|
||||
import itertools as it
|
||||
|
||||
from dateutil.tz import tz
|
||||
from dateutil.parser import isoparser, isoparse
|
||||
|
||||
import pytest
|
||||
import six
|
||||
|
||||
UTC = tz.tzutc()
|
||||
|
||||
def _generate_tzoffsets(limited):
|
||||
def _mkoffset(hmtuple, fmt):
|
||||
h, m = hmtuple
|
||||
m_td = (-1 if h < 0 else 1) * m
|
||||
|
||||
tzo = tz.tzoffset(None, timedelta(hours=h, minutes=m_td))
|
||||
return tzo, fmt.format(h, m)
|
||||
|
||||
out = []
|
||||
if not limited:
|
||||
# The subset that's just hours
|
||||
hm_out_h = [(h, 0) for h in (-23, -5, 0, 5, 23)]
|
||||
out.extend([_mkoffset(hm, '{:+03d}') for hm in hm_out_h])
|
||||
|
||||
# Ones that have hours and minutes
|
||||
hm_out = [] + hm_out_h
|
||||
hm_out += [(-12, 15), (11, 30), (10, 2), (5, 15), (-5, 30)]
|
||||
else:
|
||||
hm_out = [(-5, -0)]
|
||||
|
||||
fmts = ['{:+03d}:{:02d}', '{:+03d}{:02d}']
|
||||
out += [_mkoffset(hm, fmt) for hm in hm_out for fmt in fmts]
|
||||
|
||||
# Also add in UTC and naive
|
||||
out.append((tz.tzutc(), 'Z'))
|
||||
out.append((None, ''))
|
||||
|
||||
return out
|
||||
|
||||
FULL_TZOFFSETS = _generate_tzoffsets(False)
|
||||
FULL_TZOFFSETS_AWARE = [x for x in FULL_TZOFFSETS if x[1]]
|
||||
TZOFFSETS = _generate_tzoffsets(True)
|
||||
|
||||
DATES = [datetime(1996, 1, 1), datetime(2017, 1, 1)]
|
||||
@pytest.mark.parametrize('dt', tuple(DATES))
|
||||
def test_year_only(dt):
|
||||
dtstr = dt.strftime('%Y')
|
||||
|
||||
assert isoparse(dtstr) == dt
|
||||
|
||||
DATES += [datetime(2000, 2, 1), datetime(2017, 4, 1)]
|
||||
@pytest.mark.parametrize('dt', tuple(DATES))
|
||||
def test_year_month(dt):
|
||||
fmt = '%Y-%m'
|
||||
dtstr = dt.strftime(fmt)
|
||||
|
||||
assert isoparse(dtstr) == dt
|
||||
|
||||
DATES += [datetime(2016, 2, 29), datetime(2018, 3, 15)]
|
||||
YMD_FMTS = ('%Y%m%d', '%Y-%m-%d')
|
||||
@pytest.mark.parametrize('dt', tuple(DATES))
|
||||
@pytest.mark.parametrize('fmt', YMD_FMTS)
|
||||
def test_year_month_day(dt, fmt):
|
||||
dtstr = dt.strftime(fmt)
|
||||
|
||||
assert isoparse(dtstr) == dt
|
||||
|
||||
def _isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset,
|
||||
microsecond_precision=None):
|
||||
tzi, offset_str = tzoffset
|
||||
fmt = date_fmt + 'T' + time_fmt
|
||||
dt = dt.replace(tzinfo=tzi)
|
||||
dtstr = dt.strftime(fmt)
|
||||
|
||||
if microsecond_precision is not None:
|
||||
if not fmt.endswith('%f'):
|
||||
raise ValueError('Time format has no microseconds!')
|
||||
|
||||
if microsecond_precision != 6:
|
||||
dtstr = dtstr[:-(6 - microsecond_precision)]
|
||||
elif microsecond_precision > 6:
|
||||
raise ValueError('Precision must be 1-6')
|
||||
|
||||
dtstr += offset_str
|
||||
|
||||
assert isoparse(dtstr) == dt
|
||||
|
||||
DATETIMES = [datetime(1998, 4, 16, 12),
|
||||
datetime(2019, 11, 18, 23),
|
||||
datetime(2014, 12, 16, 4)]
|
||||
@pytest.mark.parametrize('dt', tuple(DATETIMES))
|
||||
@pytest.mark.parametrize('date_fmt', YMD_FMTS)
|
||||
@pytest.mark.parametrize('tzoffset', TZOFFSETS)
|
||||
def test_ymd_h(dt, date_fmt, tzoffset):
|
||||
_isoparse_date_and_time(dt, date_fmt, '%H', tzoffset)
|
||||
|
||||
DATETIMES = [datetime(2012, 1, 6, 9, 37)]
|
||||
@pytest.mark.parametrize('dt', tuple(DATETIMES))
|
||||
@pytest.mark.parametrize('date_fmt', YMD_FMTS)
|
||||
@pytest.mark.parametrize('time_fmt', ('%H%M', '%H:%M'))
|
||||
@pytest.mark.parametrize('tzoffset', TZOFFSETS)
|
||||
def test_ymd_hm(dt, date_fmt, time_fmt, tzoffset):
|
||||
_isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset)
|
||||
|
||||
DATETIMES = [datetime(2003, 9, 2, 22, 14, 2),
|
||||
datetime(2003, 8, 8, 14, 9, 14),
|
||||
datetime(2003, 4, 7, 6, 14, 59)]
|
||||
HMS_FMTS = ('%H%M%S', '%H:%M:%S')
|
||||
@pytest.mark.parametrize('dt', tuple(DATETIMES))
|
||||
@pytest.mark.parametrize('date_fmt', YMD_FMTS)
|
||||
@pytest.mark.parametrize('time_fmt', HMS_FMTS)
|
||||
@pytest.mark.parametrize('tzoffset', TZOFFSETS)
|
||||
def test_ymd_hms(dt, date_fmt, time_fmt, tzoffset):
|
||||
_isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset)
|
||||
|
||||
DATETIMES = [datetime(2017, 11, 27, 6, 14, 30, 123456)]
|
||||
@pytest.mark.parametrize('dt', tuple(DATETIMES))
|
||||
@pytest.mark.parametrize('date_fmt', YMD_FMTS)
|
||||
@pytest.mark.parametrize('time_fmt', (x + '.%f' for x in HMS_FMTS))
|
||||
@pytest.mark.parametrize('tzoffset', TZOFFSETS)
|
||||
@pytest.mark.parametrize('precision', list(range(3, 7)))
|
||||
def test_ymd_hms_micro(dt, date_fmt, time_fmt, tzoffset, precision):
|
||||
# Truncate the microseconds to the desired precision for the representation
|
||||
dt = dt.replace(microsecond=int(round(dt.microsecond, precision-6)))
|
||||
|
||||
_isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset, precision)
|
||||
|
||||
@pytest.mark.parametrize('tzoffset', FULL_TZOFFSETS)
|
||||
def test_full_tzoffsets(tzoffset):
|
||||
dt = datetime(2017, 11, 27, 6, 14, 30, 123456)
|
||||
date_fmt = '%Y-%m-%d'
|
||||
time_fmt = '%H:%M:%S.%f'
|
||||
|
||||
_isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset)
|
||||
|
||||
@pytest.mark.parametrize('dt_str', [
|
||||
'2014-04-11T00',
|
||||
'2014-04-11T24',
|
||||
'2014-04-11T00:00',
|
||||
'2014-04-11T24:00',
|
||||
'2014-04-11T00:00:00',
|
||||
'2014-04-11T24:00:00',
|
||||
'2014-04-11T00:00:00.000',
|
||||
'2014-04-11T24:00:00.000',
|
||||
'2014-04-11T00:00:00.000000',
|
||||
'2014-04-11T24:00:00.000000']
|
||||
)
|
||||
def test_datetime_midnight(dt_str):
|
||||
assert isoparse(dt_str) == datetime(2014, 4, 11, 0, 0, 0, 0)
|
||||
|
||||
@pytest.mark.parametrize('datestr', [
|
||||
'2014-01-01',
|
||||
'20140101',
|
||||
])
|
||||
@pytest.mark.parametrize('sep', [' ', 'a', 'T', '_', '-'])
|
||||
def test_isoparse_sep_none(datestr, sep):
|
||||
isostr = datestr + sep + '14:33:09'
|
||||
assert isoparse(isostr) == datetime(2014, 1, 1, 14, 33, 9)
|
||||
|
||||
##
|
||||
# Uncommon date formats
|
||||
TIME_ARGS = ('time_args',
|
||||
((None, time(0), None), ) + tuple(('%H:%M:%S.%f', _t, _tz)
|
||||
for _t, _tz in it.product([time(0), time(9, 30), time(14, 47)],
|
||||
TZOFFSETS)))
|
||||
|
||||
@pytest.mark.parametrize('isocal,dt_expected',[
|
||||
((2017, 10), datetime(2017, 3, 6)),
|
||||
((2020, 1), datetime(2019, 12, 30)), # ISO year != Cal year
|
||||
((2004, 53), datetime(2004, 12, 27)), # Only half the week is in 2014
|
||||
])
|
||||
def test_isoweek(isocal, dt_expected):
|
||||
# TODO: Figure out how to parametrize this on formats, too
|
||||
for fmt in ('{:04d}-W{:02d}', '{:04d}W{:02d}'):
|
||||
dtstr = fmt.format(*isocal)
|
||||
assert isoparse(dtstr) == dt_expected
|
||||
|
||||
@pytest.mark.parametrize('isocal,dt_expected',[
|
||||
((2016, 13, 7), datetime(2016, 4, 3)),
|
||||
((2004, 53, 7), datetime(2005, 1, 2)), # ISO year != Cal year
|
||||
((2009, 1, 2), datetime(2008, 12, 30)), # ISO year < Cal year
|
||||
((2009, 53, 6), datetime(2010, 1, 2)) # ISO year > Cal year
|
||||
])
|
||||
def test_isoweek_day(isocal, dt_expected):
|
||||
# TODO: Figure out how to parametrize this on formats, too
|
||||
for fmt in ('{:04d}-W{:02d}-{:d}', '{:04d}W{:02d}{:d}'):
|
||||
dtstr = fmt.format(*isocal)
|
||||
assert isoparse(dtstr) == dt_expected
|
||||
|
||||
@pytest.mark.parametrize('isoord,dt_expected', [
|
||||
((2004, 1), datetime(2004, 1, 1)),
|
||||
((2016, 60), datetime(2016, 2, 29)),
|
||||
((2017, 60), datetime(2017, 3, 1)),
|
||||
((2016, 366), datetime(2016, 12, 31)),
|
||||
((2017, 365), datetime(2017, 12, 31))
|
||||
])
|
||||
def test_iso_ordinal(isoord, dt_expected):
|
||||
for fmt in ('{:04d}-{:03d}', '{:04d}{:03d}'):
|
||||
dtstr = fmt.format(*isoord)
|
||||
|
||||
assert isoparse(dtstr) == dt_expected
|
||||
|
||||
|
||||
###
|
||||
# Acceptance of bytes
|
||||
@pytest.mark.parametrize('isostr,dt', [
|
||||
(b'2014', datetime(2014, 1, 1)),
|
||||
(b'20140204', datetime(2014, 2, 4)),
|
||||
(b'2014-02-04', datetime(2014, 2, 4)),
|
||||
(b'2014-02-04T12', datetime(2014, 2, 4, 12)),
|
||||
(b'2014-02-04T12:30', datetime(2014, 2, 4, 12, 30)),
|
||||
(b'2014-02-04T12:30:15', datetime(2014, 2, 4, 12, 30, 15)),
|
||||
(b'2014-02-04T12:30:15.224', datetime(2014, 2, 4, 12, 30, 15, 224000)),
|
||||
(b'20140204T123015.224', datetime(2014, 2, 4, 12, 30, 15, 224000)),
|
||||
(b'2014-02-04T12:30:15.224Z', datetime(2014, 2, 4, 12, 30, 15, 224000,
|
||||
tz.tzutc())),
|
||||
(b'2014-02-04T12:30:15.224+05:00',
|
||||
datetime(2014, 2, 4, 12, 30, 15, 224000,
|
||||
tzinfo=tz.tzoffset(None, timedelta(hours=5))))])
|
||||
def test_bytes(isostr, dt):
|
||||
assert isoparse(isostr) == dt
|
||||
|
||||
|
||||
###
|
||||
# Invalid ISO strings
|
||||
@pytest.mark.parametrize('isostr,exception', [
|
||||
('201', ValueError), # ISO string too short
|
||||
('2012-0425', ValueError), # Inconsistent date separators
|
||||
('201204-25', ValueError), # Inconsistent date separators
|
||||
('20120425T0120:00', ValueError), # Inconsistent time separators
|
||||
('20120425T012500-334', ValueError), # Wrong microsecond separator
|
||||
('2001-1', ValueError), # YYYY-M not valid
|
||||
('2012-04-9', ValueError), # YYYY-MM-D not valid
|
||||
('201204', ValueError), # YYYYMM not valid
|
||||
('20120411T03:30+', ValueError), # Time zone too short
|
||||
('20120411T03:30+1234567', ValueError), # Time zone too long
|
||||
('20120411T03:30-25:40', ValueError), # Time zone invalid
|
||||
('2012-1a', ValueError), # Invalid month
|
||||
('20120411T03:30+00:60', ValueError), # Time zone invalid minutes
|
||||
('20120411T03:30+00:61', ValueError), # Time zone invalid minutes
|
||||
('20120411T033030.123456012:00', # No sign in time zone
|
||||
ValueError),
|
||||
('2012-W00', ValueError), # Invalid ISO week
|
||||
('2012-W55', ValueError), # Invalid ISO week
|
||||
('2012-W01-0', ValueError), # Invalid ISO week day
|
||||
('2012-W01-8', ValueError), # Invalid ISO week day
|
||||
('2013-000', ValueError), # Invalid ordinal day
|
||||
('2013-366', ValueError), # Invalid ordinal day
|
||||
('2013366', ValueError), # Invalid ordinal day
|
||||
('2014-03-12Т12:30:14', ValueError), # Cyrillic T
|
||||
('2014-04-21T24:00:01', ValueError), # Invalid use of 24 for midnight
|
||||
('2014_W01-1', ValueError), # Invalid separator
|
||||
('2014W01-1', ValueError), # Inconsistent use of dashes
|
||||
('2014-W011', ValueError), # Inconsistent use of dashes
|
||||
|
||||
])
|
||||
def test_iso_raises(isostr, exception):
|
||||
with pytest.raises(exception):
|
||||
isoparse(isostr)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('sep_act,valid_sep', [
|
||||
('C', 'T'),
|
||||
('T', 'C')
|
||||
])
|
||||
def test_iso_raises_sep(sep_act, valid_sep):
|
||||
isostr = '2012-04-25' + sep_act + '01:25:00'
|
||||
|
||||
|
||||
@pytest.mark.xfail()
|
||||
@pytest.mark.parametrize('isostr,exception', [
|
||||
('20120425T01:2000', ValueError), # Inconsistent time separators
|
||||
])
|
||||
def test_iso_raises_failing(isostr, exception):
|
||||
# These are test cases where the current implementation is too lenient
|
||||
# and need to be fixed
|
||||
with pytest.raises(exception):
|
||||
isoparse(isostr)
|
||||
|
||||
|
||||
###
|
||||
# Test ISOParser constructor
|
||||
@pytest.mark.parametrize('sep', [' ', '9', '🍛'])
|
||||
def test_isoparser_invalid_sep(sep):
|
||||
with pytest.raises(ValueError):
|
||||
isoparser(sep=sep)
|
||||
|
||||
|
||||
# This only fails on Python 3
|
||||
@pytest.mark.xfail(six.PY3, reason="Fails on Python 3 only")
|
||||
def test_isoparser_byte_sep():
|
||||
dt = datetime(2017, 12, 6, 12, 30, 45)
|
||||
dt_str = dt.isoformat(sep=str('T'))
|
||||
|
||||
dt_rt = isoparser(sep=b'T').isoparse(dt_str)
|
||||
|
||||
assert dt == dt_rt
|
||||
|
||||
|
||||
###
|
||||
# Test parse_tzstr
|
||||
@pytest.mark.parametrize('tzoffset', FULL_TZOFFSETS)
|
||||
def test_parse_tzstr(tzoffset):
|
||||
dt = datetime(2017, 11, 27, 6, 14, 30, 123456)
|
||||
date_fmt = '%Y-%m-%d'
|
||||
time_fmt = '%H:%M:%S.%f'
|
||||
|
||||
_isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('tzstr', [
|
||||
'-00:00', '+00:00', '+00', '-00', '+0000', '-0000'
|
||||
])
|
||||
@pytest.mark.parametrize('zero_as_utc', [True, False])
|
||||
def test_parse_tzstr_zero_as_utc(tzstr, zero_as_utc):
|
||||
tzi = isoparser().parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
|
||||
assert tzi == tz.tzutc()
|
||||
assert (type(tzi) == tz.tzutc) == zero_as_utc
|
||||
|
||||
|
||||
@pytest.mark.parametrize('tzstr,exception', [
|
||||
('00:00', ValueError), # No sign
|
||||
('05:00', ValueError), # No sign
|
||||
('_00:00', ValueError), # Invalid sign
|
||||
('+25:00', ValueError), # Offset too large
|
||||
('00:0000', ValueError), # String too long
|
||||
])
|
||||
def test_parse_tzstr_fails(tzstr, exception):
|
||||
with pytest.raises(exception):
|
||||
isoparser().parse_tzstr(tzstr)
|
||||
|
||||
###
|
||||
# Test parse_isodate
|
||||
def __make_date_examples():
|
||||
dates_no_day = [
|
||||
date(1999, 12, 1),
|
||||
date(2016, 2, 1)
|
||||
]
|
||||
|
||||
if six.PY3:
|
||||
# strftime does not support dates before 1900 in Python 2
|
||||
dates_no_day.append(date(1000, 11, 1))
|
||||
|
||||
# Only one supported format for dates with no day
|
||||
o = zip(dates_no_day, it.repeat('%Y-%m'))
|
||||
|
||||
dates_w_day = [
|
||||
date(1969, 12, 31),
|
||||
date(1900, 1, 1),
|
||||
date(2016, 2, 29),
|
||||
date(2017, 11, 14)
|
||||
]
|
||||
|
||||
dates_w_day_fmts = ('%Y%m%d', '%Y-%m-%d')
|
||||
o = it.chain(o, it.product(dates_w_day, dates_w_day_fmts))
|
||||
|
||||
return list(o)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('d,dt_fmt', __make_date_examples())
|
||||
@pytest.mark.parametrize('as_bytes', [True, False])
|
||||
def test_parse_isodate(d, dt_fmt, as_bytes):
|
||||
d_str = d.strftime(dt_fmt)
|
||||
if isinstance(d_str, six.text_type) and as_bytes:
|
||||
d_str = d_str.encode('ascii')
|
||||
elif isinstance(d_str, six.binary_type) and not as_bytes:
|
||||
d_str = d_str.decode('ascii')
|
||||
|
||||
iparser = isoparser()
|
||||
assert iparser.parse_isodate(d_str) == d
|
||||
|
||||
|
||||
@pytest.mark.parametrize('isostr,exception', [
|
||||
('243', ValueError), # ISO string too short
|
||||
('2014-0423', ValueError), # Inconsistent date separators
|
||||
('201404-23', ValueError), # Inconsistent date separators
|
||||
('2014日03月14', ValueError), # Not ASCII
|
||||
('2013-02-29', ValueError), # Not a leap year
|
||||
('2014/12/03', ValueError), # Wrong separators
|
||||
('2014-04-19T', ValueError), # Unknown components
|
||||
])
|
||||
def test_isodate_raises(isostr, exception):
|
||||
with pytest.raises(exception):
|
||||
isoparser().parse_isodate(isostr)
|
||||
|
||||
|
||||
###
|
||||
# Test parse_isotime
|
||||
def __make_time_examples():
|
||||
outputs = []
|
||||
|
||||
# HH
|
||||
time_h = [time(0), time(8), time(22)]
|
||||
time_h_fmts = ['%H']
|
||||
|
||||
outputs.append(it.product(time_h, time_h_fmts))
|
||||
|
||||
# HHMM / HH:MM
|
||||
time_hm = [time(0, 0), time(0, 30), time(8, 47), time(16, 1)]
|
||||
time_hm_fmts = ['%H%M', '%H:%M']
|
||||
|
||||
outputs.append(it.product(time_hm, time_hm_fmts))
|
||||
|
||||
# HHMMSS / HH:MM:SS
|
||||
time_hms = [time(0, 0, 0), time(0, 15, 30),
|
||||
time(8, 2, 16), time(12, 0), time(16, 2), time(20, 45)]
|
||||
|
||||
time_hms_fmts = ['%H%M%S', '%H:%M:%S']
|
||||
|
||||
outputs.append(it.product(time_hms, time_hms_fmts))
|
||||
|
||||
# HHMMSS.ffffff / HH:MM:SS.ffffff
|
||||
time_hmsu = [time(0, 0, 0, 0), time(4, 15, 3, 247993),
|
||||
time(14, 21, 59, 948730),
|
||||
time(23, 59, 59, 999999)]
|
||||
|
||||
time_hmsu_fmts = ['%H%M%S.%f', '%H:%M:%S.%f']
|
||||
|
||||
outputs.append(it.product(time_hmsu, time_hmsu_fmts))
|
||||
|
||||
outputs = list(map(list, outputs))
|
||||
|
||||
# Time zones
|
||||
ex_naive = list(it.chain.from_iterable(x[0:2] for x in outputs))
|
||||
o = it.product(ex_naive, TZOFFSETS) # ((time, fmt), (tzinfo, offsetstr))
|
||||
o = ((t.replace(tzinfo=tzi), fmt + off_str)
|
||||
for (t, fmt), (tzi, off_str) in o)
|
||||
|
||||
outputs.append(o)
|
||||
|
||||
return list(it.chain.from_iterable(outputs))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('time_val,time_fmt', __make_time_examples())
|
||||
@pytest.mark.parametrize('as_bytes', [True, False])
|
||||
def test_isotime(time_val, time_fmt, as_bytes):
|
||||
tstr = time_val.strftime(time_fmt)
|
||||
if isinstance(time_val, six.text_type) and as_bytes:
|
||||
tstr = tstr.encode('ascii')
|
||||
elif isinstance(time_val, six.binary_type) and not as_bytes:
|
||||
tstr = tstr.decode('ascii')
|
||||
|
||||
iparser = isoparser()
|
||||
|
||||
assert iparser.parse_isotime(tstr) == time_val
|
||||
|
||||
@pytest.mark.parametrize('isostr,exception', [
|
||||
('3', ValueError), # ISO string too short
|
||||
('14時30分15秒', ValueError), # Not ASCII
|
||||
('14_30_15', ValueError), # Invalid separators
|
||||
('1430:15', ValueError), # Inconsistent separator use
|
||||
('14:30:15.3684000309', ValueError), # Too much us precision
|
||||
('25', ValueError), # Invalid hours
|
||||
('25:15', ValueError), # Invalid hours
|
||||
('14:60', ValueError), # Invalid minutes
|
||||
('14:59:61', ValueError), # Invalid seconds
|
||||
('14:30:15.3446830500', ValueError), # No sign in time zone
|
||||
('14:30:15+', ValueError), # Time zone too short
|
||||
('14:30:15+1234567', ValueError), # Time zone invalid
|
||||
('14:59:59+25:00', ValueError), # Invalid tz hours
|
||||
('14:59:59+12:62', ValueError), # Invalid tz minutes
|
||||
('14:59:30_344583', ValueError), # Invalid microsecond separator
|
||||
])
|
||||
def test_isotime_raises(isostr, exception):
|
||||
iparser = isoparser()
|
||||
with pytest.raises(exception):
|
||||
iparser.parse_isotime(isostr)
|
||||
|
||||
|
||||
@pytest.mark.xfail()
|
||||
@pytest.mark.parametrize('isostr,exception', [
|
||||
('14:3015', ValueError), # Inconsistent separator use
|
||||
('201202', ValueError) # Invalid ISO format
|
||||
])
|
||||
def test_isotime_raises_xfail(isostr, exception):
|
||||
iparser = isoparser()
|
||||
with pytest.raises(exception):
|
||||
iparser.parse_isotime(isostr)
|
1114
resources/lib/libraries/dateutil/test/test_parser.py
Normal file
1114
resources/lib/libraries/dateutil/test/test_parser.py
Normal file
File diff suppressed because it is too large
Load diff
678
resources/lib/libraries/dateutil/test/test_relativedelta.py
Normal file
678
resources/lib/libraries/dateutil/test/test_relativedelta.py
Normal file
|
@ -0,0 +1,678 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from ._common import WarningTestMixin, NotAValue
|
||||
|
||||
import calendar
|
||||
from datetime import datetime, date, timedelta
|
||||
import unittest
|
||||
|
||||
from dateutil.relativedelta import relativedelta, MO, TU, WE, FR, SU
|
||||
|
||||
|
||||
class RelativeDeltaTest(WarningTestMixin, unittest.TestCase):
|
||||
now = datetime(2003, 9, 17, 20, 54, 47, 282310)
|
||||
today = date(2003, 9, 17)
|
||||
|
||||
def testInheritance(self):
|
||||
# Ensure that relativedelta is inheritance-friendly.
|
||||
class rdChildClass(relativedelta):
|
||||
pass
|
||||
|
||||
ccRD = rdChildClass(years=1, months=1, days=1, leapdays=1, weeks=1,
|
||||
hours=1, minutes=1, seconds=1, microseconds=1)
|
||||
|
||||
rd = relativedelta(years=1, months=1, days=1, leapdays=1, weeks=1,
|
||||
hours=1, minutes=1, seconds=1, microseconds=1)
|
||||
|
||||
self.assertEqual(type(ccRD + rd), type(ccRD),
|
||||
msg='Addition does not inherit type.')
|
||||
|
||||
self.assertEqual(type(ccRD - rd), type(ccRD),
|
||||
msg='Subtraction does not inherit type.')
|
||||
|
||||
self.assertEqual(type(-ccRD), type(ccRD),
|
||||
msg='Negation does not inherit type.')
|
||||
|
||||
self.assertEqual(type(ccRD * 5.0), type(ccRD),
|
||||
msg='Multiplication does not inherit type.')
|
||||
|
||||
self.assertEqual(type(ccRD / 5.0), type(ccRD),
|
||||
msg='Division does not inherit type.')
|
||||
|
||||
def testMonthEndMonthBeginning(self):
|
||||
self.assertEqual(relativedelta(datetime(2003, 1, 31, 23, 59, 59),
|
||||
datetime(2003, 3, 1, 0, 0, 0)),
|
||||
relativedelta(months=-1, seconds=-1))
|
||||
|
||||
self.assertEqual(relativedelta(datetime(2003, 3, 1, 0, 0, 0),
|
||||
datetime(2003, 1, 31, 23, 59, 59)),
|
||||
relativedelta(months=1, seconds=1))
|
||||
|
||||
def testMonthEndMonthBeginningLeapYear(self):
|
||||
self.assertEqual(relativedelta(datetime(2012, 1, 31, 23, 59, 59),
|
||||
datetime(2012, 3, 1, 0, 0, 0)),
|
||||
relativedelta(months=-1, seconds=-1))
|
||||
|
||||
self.assertEqual(relativedelta(datetime(2003, 3, 1, 0, 0, 0),
|
||||
datetime(2003, 1, 31, 23, 59, 59)),
|
||||
relativedelta(months=1, seconds=1))
|
||||
|
||||
def testNextMonth(self):
|
||||
self.assertEqual(self.now+relativedelta(months=+1),
|
||||
datetime(2003, 10, 17, 20, 54, 47, 282310))
|
||||
|
||||
def testNextMonthPlusOneWeek(self):
|
||||
self.assertEqual(self.now+relativedelta(months=+1, weeks=+1),
|
||||
datetime(2003, 10, 24, 20, 54, 47, 282310))
|
||||
|
||||
def testNextMonthPlusOneWeek10am(self):
|
||||
self.assertEqual(self.today +
|
||||
relativedelta(months=+1, weeks=+1, hour=10),
|
||||
datetime(2003, 10, 24, 10, 0))
|
||||
|
||||
def testNextMonthPlusOneWeek10amDiff(self):
|
||||
self.assertEqual(relativedelta(datetime(2003, 10, 24, 10, 0),
|
||||
self.today),
|
||||
relativedelta(months=+1, days=+7, hours=+10))
|
||||
|
||||
def testOneMonthBeforeOneYear(self):
|
||||
self.assertEqual(self.now+relativedelta(years=+1, months=-1),
|
||||
datetime(2004, 8, 17, 20, 54, 47, 282310))
|
||||
|
||||
def testMonthsOfDiffNumOfDays(self):
|
||||
self.assertEqual(date(2003, 1, 27)+relativedelta(months=+1),
|
||||
date(2003, 2, 27))
|
||||
self.assertEqual(date(2003, 1, 31)+relativedelta(months=+1),
|
||||
date(2003, 2, 28))
|
||||
self.assertEqual(date(2003, 1, 31)+relativedelta(months=+2),
|
||||
date(2003, 3, 31))
|
||||
|
||||
def testMonthsOfDiffNumOfDaysWithYears(self):
|
||||
self.assertEqual(date(2000, 2, 28)+relativedelta(years=+1),
|
||||
date(2001, 2, 28))
|
||||
self.assertEqual(date(2000, 2, 29)+relativedelta(years=+1),
|
||||
date(2001, 2, 28))
|
||||
|
||||
self.assertEqual(date(1999, 2, 28)+relativedelta(years=+1),
|
||||
date(2000, 2, 28))
|
||||
self.assertEqual(date(1999, 3, 1)+relativedelta(years=+1),
|
||||
date(2000, 3, 1))
|
||||
self.assertEqual(date(1999, 3, 1)+relativedelta(years=+1),
|
||||
date(2000, 3, 1))
|
||||
|
||||
self.assertEqual(date(2001, 2, 28)+relativedelta(years=-1),
|
||||
date(2000, 2, 28))
|
||||
self.assertEqual(date(2001, 3, 1)+relativedelta(years=-1),
|
||||
date(2000, 3, 1))
|
||||
|
||||
def testNextFriday(self):
|
||||
self.assertEqual(self.today+relativedelta(weekday=FR),
|
||||
date(2003, 9, 19))
|
||||
|
||||
def testNextFridayInt(self):
|
||||
self.assertEqual(self.today+relativedelta(weekday=calendar.FRIDAY),
|
||||
date(2003, 9, 19))
|
||||
|
||||
def testLastFridayInThisMonth(self):
|
||||
self.assertEqual(self.today+relativedelta(day=31, weekday=FR(-1)),
|
||||
date(2003, 9, 26))
|
||||
|
||||
def testNextWednesdayIsToday(self):
|
||||
self.assertEqual(self.today+relativedelta(weekday=WE),
|
||||
date(2003, 9, 17))
|
||||
|
||||
def testNextWenesdayNotToday(self):
|
||||
self.assertEqual(self.today+relativedelta(days=+1, weekday=WE),
|
||||
date(2003, 9, 24))
|
||||
|
||||
def test15thISOYearWeek(self):
|
||||
self.assertEqual(date(2003, 1, 1) +
|
||||
relativedelta(day=4, weeks=+14, weekday=MO(-1)),
|
||||
date(2003, 4, 7))
|
||||
|
||||
def testMillenniumAge(self):
|
||||
self.assertEqual(relativedelta(self.now, date(2001, 1, 1)),
|
||||
relativedelta(years=+2, months=+8, days=+16,
|
||||
hours=+20, minutes=+54, seconds=+47,
|
||||
microseconds=+282310))
|
||||
|
||||
def testJohnAge(self):
|
||||
self.assertEqual(relativedelta(self.now,
|
||||
datetime(1978, 4, 5, 12, 0)),
|
||||
relativedelta(years=+25, months=+5, days=+12,
|
||||
hours=+8, minutes=+54, seconds=+47,
|
||||
microseconds=+282310))
|
||||
|
||||
def testJohnAgeWithDate(self):
|
||||
self.assertEqual(relativedelta(self.today,
|
||||
datetime(1978, 4, 5, 12, 0)),
|
||||
relativedelta(years=+25, months=+5, days=+11,
|
||||
hours=+12))
|
||||
|
||||
def testYearDay(self):
|
||||
self.assertEqual(date(2003, 1, 1)+relativedelta(yearday=260),
|
||||
date(2003, 9, 17))
|
||||
self.assertEqual(date(2002, 1, 1)+relativedelta(yearday=260),
|
||||
date(2002, 9, 17))
|
||||
self.assertEqual(date(2000, 1, 1)+relativedelta(yearday=260),
|
||||
date(2000, 9, 16))
|
||||
self.assertEqual(self.today+relativedelta(yearday=261),
|
||||
date(2003, 9, 18))
|
||||
|
||||
def testYearDayBug(self):
|
||||
# Tests a problem reported by Adam Ryan.
|
||||
self.assertEqual(date(2010, 1, 1)+relativedelta(yearday=15),
|
||||
date(2010, 1, 15))
|
||||
|
||||
def testNonLeapYearDay(self):
|
||||
self.assertEqual(date(2003, 1, 1)+relativedelta(nlyearday=260),
|
||||
date(2003, 9, 17))
|
||||
self.assertEqual(date(2002, 1, 1)+relativedelta(nlyearday=260),
|
||||
date(2002, 9, 17))
|
||||
self.assertEqual(date(2000, 1, 1)+relativedelta(nlyearday=260),
|
||||
date(2000, 9, 17))
|
||||
self.assertEqual(self.today+relativedelta(yearday=261),
|
||||
date(2003, 9, 18))
|
||||
|
||||
def testAddition(self):
|
||||
self.assertEqual(relativedelta(days=10) +
|
||||
relativedelta(years=1, months=2, days=3, hours=4,
|
||||
minutes=5, microseconds=6),
|
||||
relativedelta(years=1, months=2, days=13, hours=4,
|
||||
minutes=5, microseconds=6))
|
||||
|
||||
def testAbsoluteAddition(self):
|
||||
self.assertEqual(relativedelta() + relativedelta(day=0, hour=0),
|
||||
relativedelta(day=0, hour=0))
|
||||
self.assertEqual(relativedelta(day=0, hour=0) + relativedelta(),
|
||||
relativedelta(day=0, hour=0))
|
||||
|
||||
def testAdditionToDatetime(self):
|
||||
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1),
|
||||
datetime(2000, 1, 2))
|
||||
|
||||
def testRightAdditionToDatetime(self):
|
||||
self.assertEqual(relativedelta(days=1) + datetime(2000, 1, 1),
|
||||
datetime(2000, 1, 2))
|
||||
|
||||
def testAdditionInvalidType(self):
|
||||
with self.assertRaises(TypeError):
|
||||
relativedelta(days=3) + 9
|
||||
|
||||
def testAdditionUnsupportedType(self):
|
||||
# For unsupported types that define their own comparators, etc.
|
||||
self.assertIs(relativedelta(days=1) + NotAValue, NotAValue)
|
||||
|
||||
def testAdditionFloatValue(self):
|
||||
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=float(1)),
|
||||
datetime(2000, 1, 2))
|
||||
self.assertEqual(datetime(2000, 1, 1) + relativedelta(months=float(1)),
|
||||
datetime(2000, 2, 1))
|
||||
self.assertEqual(datetime(2000, 1, 1) + relativedelta(years=float(1)),
|
||||
datetime(2001, 1, 1))
|
||||
|
||||
def testAdditionFloatFractionals(self):
|
||||
self.assertEqual(datetime(2000, 1, 1, 0) +
|
||||
relativedelta(days=float(0.5)),
|
||||
datetime(2000, 1, 1, 12))
|
||||
self.assertEqual(datetime(2000, 1, 1, 0, 0) +
|
||||
relativedelta(hours=float(0.5)),
|
||||
datetime(2000, 1, 1, 0, 30))
|
||||
self.assertEqual(datetime(2000, 1, 1, 0, 0, 0) +
|
||||
relativedelta(minutes=float(0.5)),
|
||||
datetime(2000, 1, 1, 0, 0, 30))
|
||||
self.assertEqual(datetime(2000, 1, 1, 0, 0, 0, 0) +
|
||||
relativedelta(seconds=float(0.5)),
|
||||
datetime(2000, 1, 1, 0, 0, 0, 500000))
|
||||
self.assertEqual(datetime(2000, 1, 1, 0, 0, 0, 0) +
|
||||
relativedelta(microseconds=float(500000.25)),
|
||||
datetime(2000, 1, 1, 0, 0, 0, 500000))
|
||||
|
||||
def testSubtraction(self):
|
||||
self.assertEqual(relativedelta(days=10) -
|
||||
relativedelta(years=1, months=2, days=3, hours=4,
|
||||
minutes=5, microseconds=6),
|
||||
relativedelta(years=-1, months=-2, days=7, hours=-4,
|
||||
minutes=-5, microseconds=-6))
|
||||
|
||||
def testRightSubtractionFromDatetime(self):
|
||||
self.assertEqual(datetime(2000, 1, 2) - relativedelta(days=1),
|
||||
datetime(2000, 1, 1))
|
||||
|
||||
def testSubractionWithDatetime(self):
|
||||
self.assertRaises(TypeError, lambda x, y: x - y,
|
||||
(relativedelta(days=1), datetime(2000, 1, 1)))
|
||||
|
||||
def testSubtractionInvalidType(self):
|
||||
with self.assertRaises(TypeError):
|
||||
relativedelta(hours=12) - 14
|
||||
|
||||
def testSubtractionUnsupportedType(self):
|
||||
self.assertIs(relativedelta(days=1) + NotAValue, NotAValue)
|
||||
|
||||
def testMultiplication(self):
|
||||
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1) * 28,
|
||||
datetime(2000, 1, 29))
|
||||
self.assertEqual(datetime(2000, 1, 1) + 28 * relativedelta(days=1),
|
||||
datetime(2000, 1, 29))
|
||||
|
||||
def testMultiplicationUnsupportedType(self):
|
||||
self.assertIs(relativedelta(days=1) * NotAValue, NotAValue)
|
||||
|
||||
def testDivision(self):
|
||||
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=28) / 28,
|
||||
datetime(2000, 1, 2))
|
||||
|
||||
def testDivisionUnsupportedType(self):
|
||||
self.assertIs(relativedelta(days=1) / NotAValue, NotAValue)
|
||||
|
||||
def testBoolean(self):
|
||||
self.assertFalse(relativedelta(days=0))
|
||||
self.assertTrue(relativedelta(days=1))
|
||||
|
||||
def testAbsoluteValueNegative(self):
|
||||
rd_base = relativedelta(years=-1, months=-5, days=-2, hours=-3,
|
||||
minutes=-5, seconds=-2, microseconds=-12)
|
||||
rd_expected = relativedelta(years=1, months=5, days=2, hours=3,
|
||||
minutes=5, seconds=2, microseconds=12)
|
||||
self.assertEqual(abs(rd_base), rd_expected)
|
||||
|
||||
def testAbsoluteValuePositive(self):
|
||||
rd_base = relativedelta(years=1, months=5, days=2, hours=3,
|
||||
minutes=5, seconds=2, microseconds=12)
|
||||
rd_expected = rd_base
|
||||
|
||||
self.assertEqual(abs(rd_base), rd_expected)
|
||||
|
||||
def testComparison(self):
|
||||
d1 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
|
||||
minutes=1, seconds=1, microseconds=1)
|
||||
d2 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
|
||||
minutes=1, seconds=1, microseconds=1)
|
||||
d3 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
|
||||
minutes=1, seconds=1, microseconds=2)
|
||||
|
||||
self.assertEqual(d1, d2)
|
||||
self.assertNotEqual(d1, d3)
|
||||
|
||||
def testInequalityTypeMismatch(self):
|
||||
# Different type
|
||||
self.assertFalse(relativedelta(year=1) == 19)
|
||||
|
||||
def testInequalityUnsupportedType(self):
|
||||
self.assertIs(relativedelta(hours=3) == NotAValue, NotAValue)
|
||||
|
||||
def testInequalityWeekdays(self):
|
||||
# Different weekdays
|
||||
no_wday = relativedelta(year=1997, month=4)
|
||||
wday_mo_1 = relativedelta(year=1997, month=4, weekday=MO(+1))
|
||||
wday_mo_2 = relativedelta(year=1997, month=4, weekday=MO(+2))
|
||||
wday_tu = relativedelta(year=1997, month=4, weekday=TU)
|
||||
|
||||
self.assertTrue(wday_mo_1 == wday_mo_1)
|
||||
|
||||
self.assertFalse(no_wday == wday_mo_1)
|
||||
self.assertFalse(wday_mo_1 == no_wday)
|
||||
|
||||
self.assertFalse(wday_mo_1 == wday_mo_2)
|
||||
self.assertFalse(wday_mo_2 == wday_mo_1)
|
||||
|
||||
self.assertFalse(wday_mo_1 == wday_tu)
|
||||
self.assertFalse(wday_tu == wday_mo_1)
|
||||
|
||||
def testMonthOverflow(self):
|
||||
self.assertEqual(relativedelta(months=273),
|
||||
relativedelta(years=22, months=9))
|
||||
|
||||
def testWeeks(self):
|
||||
# Test that the weeks property is working properly.
|
||||
rd = relativedelta(years=4, months=2, weeks=8, days=6)
|
||||
self.assertEqual((rd.weeks, rd.days), (8, 8 * 7 + 6))
|
||||
|
||||
rd.weeks = 3
|
||||
self.assertEqual((rd.weeks, rd.days), (3, 3 * 7 + 6))
|
||||
|
||||
def testRelativeDeltaRepr(self):
|
||||
self.assertEqual(repr(relativedelta(years=1, months=-1, days=15)),
|
||||
'relativedelta(years=+1, months=-1, days=+15)')
|
||||
|
||||
self.assertEqual(repr(relativedelta(months=14, seconds=-25)),
|
||||
'relativedelta(years=+1, months=+2, seconds=-25)')
|
||||
|
||||
self.assertEqual(repr(relativedelta(month=3, hour=3, weekday=SU(3))),
|
||||
'relativedelta(month=3, weekday=SU(+3), hour=3)')
|
||||
|
||||
def testRelativeDeltaFractionalYear(self):
|
||||
with self.assertRaises(ValueError):
|
||||
relativedelta(years=1.5)
|
||||
|
||||
def testRelativeDeltaFractionalMonth(self):
|
||||
with self.assertRaises(ValueError):
|
||||
relativedelta(months=1.5)
|
||||
|
||||
def testRelativeDeltaFractionalAbsolutes(self):
|
||||
# Fractional absolute values will soon be unsupported,
|
||||
# check for the deprecation warning.
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
relativedelta(year=2.86)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
relativedelta(month=1.29)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
relativedelta(day=0.44)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
relativedelta(hour=23.98)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
relativedelta(minute=45.21)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
relativedelta(second=13.2)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
relativedelta(microsecond=157221.93)
|
||||
|
||||
def testRelativeDeltaFractionalRepr(self):
|
||||
rd = relativedelta(years=3, months=-2, days=1.25)
|
||||
|
||||
self.assertEqual(repr(rd),
|
||||
'relativedelta(years=+3, months=-2, days=+1.25)')
|
||||
|
||||
rd = relativedelta(hours=0.5, seconds=9.22)
|
||||
self.assertEqual(repr(rd),
|
||||
'relativedelta(hours=+0.5, seconds=+9.22)')
|
||||
|
||||
def testRelativeDeltaFractionalWeeks(self):
|
||||
# Equivalent to days=8, hours=18
|
||||
rd = relativedelta(weeks=1.25)
|
||||
d1 = datetime(2009, 9, 3, 0, 0)
|
||||
self.assertEqual(d1 + rd,
|
||||
datetime(2009, 9, 11, 18))
|
||||
|
||||
def testRelativeDeltaFractionalDays(self):
|
||||
rd1 = relativedelta(days=1.48)
|
||||
|
||||
d1 = datetime(2009, 9, 3, 0, 0)
|
||||
self.assertEqual(d1 + rd1,
|
||||
datetime(2009, 9, 4, 11, 31, 12))
|
||||
|
||||
rd2 = relativedelta(days=1.5)
|
||||
self.assertEqual(d1 + rd2,
|
||||
datetime(2009, 9, 4, 12, 0, 0))
|
||||
|
||||
def testRelativeDeltaFractionalHours(self):
|
||||
rd = relativedelta(days=1, hours=12.5)
|
||||
d1 = datetime(2009, 9, 3, 0, 0)
|
||||
self.assertEqual(d1 + rd,
|
||||
datetime(2009, 9, 4, 12, 30, 0))
|
||||
|
||||
def testRelativeDeltaFractionalMinutes(self):
|
||||
rd = relativedelta(hours=1, minutes=30.5)
|
||||
d1 = datetime(2009, 9, 3, 0, 0)
|
||||
self.assertEqual(d1 + rd,
|
||||
datetime(2009, 9, 3, 1, 30, 30))
|
||||
|
||||
def testRelativeDeltaFractionalSeconds(self):
|
||||
rd = relativedelta(hours=5, minutes=30, seconds=30.5)
|
||||
d1 = datetime(2009, 9, 3, 0, 0)
|
||||
self.assertEqual(d1 + rd,
|
||||
datetime(2009, 9, 3, 5, 30, 30, 500000))
|
||||
|
||||
def testRelativeDeltaFractionalPositiveOverflow(self):
|
||||
# Equivalent to (days=1, hours=14)
|
||||
rd1 = relativedelta(days=1.5, hours=2)
|
||||
d1 = datetime(2009, 9, 3, 0, 0)
|
||||
self.assertEqual(d1 + rd1,
|
||||
datetime(2009, 9, 4, 14, 0, 0))
|
||||
|
||||
# Equivalent to (days=1, hours=14, minutes=45)
|
||||
rd2 = relativedelta(days=1.5, hours=2.5, minutes=15)
|
||||
d1 = datetime(2009, 9, 3, 0, 0)
|
||||
self.assertEqual(d1 + rd2,
|
||||
datetime(2009, 9, 4, 14, 45))
|
||||
|
||||
# Carry back up - equivalent to (days=2, hours=2, minutes=0, seconds=1)
|
||||
rd3 = relativedelta(days=1.5, hours=13, minutes=59.5, seconds=31)
|
||||
self.assertEqual(d1 + rd3,
|
||||
datetime(2009, 9, 5, 2, 0, 1))
|
||||
|
||||
def testRelativeDeltaFractionalNegativeDays(self):
|
||||
# Equivalent to (days=-1, hours=-1)
|
||||
rd1 = relativedelta(days=-1.5, hours=11)
|
||||
d1 = datetime(2009, 9, 3, 12, 0)
|
||||
self.assertEqual(d1 + rd1,
|
||||
datetime(2009, 9, 2, 11, 0, 0))
|
||||
|
||||
# Equivalent to (days=-1, hours=-9)
|
||||
rd2 = relativedelta(days=-1.25, hours=-3)
|
||||
self.assertEqual(d1 + rd2,
|
||||
datetime(2009, 9, 2, 3))
|
||||
|
||||
def testRelativeDeltaNormalizeFractionalDays(self):
|
||||
# Equivalent to (days=2, hours=18)
|
||||
rd1 = relativedelta(days=2.75)
|
||||
|
||||
self.assertEqual(rd1.normalized(), relativedelta(days=2, hours=18))
|
||||
|
||||
# Equvalent to (days=1, hours=11, minutes=31, seconds=12)
|
||||
rd2 = relativedelta(days=1.48)
|
||||
|
||||
self.assertEqual(rd2.normalized(),
|
||||
relativedelta(days=1, hours=11, minutes=31, seconds=12))
|
||||
|
||||
def testRelativeDeltaNormalizeFractionalDays2(self):
|
||||
# Equivalent to (hours=1, minutes=30)
|
||||
rd1 = relativedelta(hours=1.5)
|
||||
|
||||
self.assertEqual(rd1.normalized(), relativedelta(hours=1, minutes=30))
|
||||
|
||||
# Equivalent to (hours=3, minutes=17, seconds=5, microseconds=100)
|
||||
rd2 = relativedelta(hours=3.28472225)
|
||||
|
||||
self.assertEqual(rd2.normalized(),
|
||||
relativedelta(hours=3, minutes=17, seconds=5, microseconds=100))
|
||||
|
||||
def testRelativeDeltaNormalizeFractionalMinutes(self):
|
||||
# Equivalent to (minutes=15, seconds=36)
|
||||
rd1 = relativedelta(minutes=15.6)
|
||||
|
||||
self.assertEqual(rd1.normalized(),
|
||||
relativedelta(minutes=15, seconds=36))
|
||||
|
||||
# Equivalent to (minutes=25, seconds=20, microseconds=25000)
|
||||
rd2 = relativedelta(minutes=25.33375)
|
||||
|
||||
self.assertEqual(rd2.normalized(),
|
||||
relativedelta(minutes=25, seconds=20, microseconds=25000))
|
||||
|
||||
def testRelativeDeltaNormalizeFractionalSeconds(self):
|
||||
# Equivalent to (seconds=45, microseconds=25000)
|
||||
rd1 = relativedelta(seconds=45.025)
|
||||
self.assertEqual(rd1.normalized(),
|
||||
relativedelta(seconds=45, microseconds=25000))
|
||||
|
||||
def testRelativeDeltaFractionalPositiveOverflow2(self):
|
||||
# Equivalent to (days=1, hours=14)
|
||||
rd1 = relativedelta(days=1.5, hours=2)
|
||||
self.assertEqual(rd1.normalized(),
|
||||
relativedelta(days=1, hours=14))
|
||||
|
||||
# Equivalent to (days=1, hours=14, minutes=45)
|
||||
rd2 = relativedelta(days=1.5, hours=2.5, minutes=15)
|
||||
self.assertEqual(rd2.normalized(),
|
||||
relativedelta(days=1, hours=14, minutes=45))
|
||||
|
||||
# Carry back up - equivalent to:
|
||||
# (days=2, hours=2, minutes=0, seconds=2, microseconds=3)
|
||||
rd3 = relativedelta(days=1.5, hours=13, minutes=59.50045,
|
||||
seconds=31.473, microseconds=500003)
|
||||
self.assertEqual(rd3.normalized(),
|
||||
relativedelta(days=2, hours=2, minutes=0,
|
||||
seconds=2, microseconds=3))
|
||||
|
||||
def testRelativeDeltaFractionalNegativeOverflow(self):
|
||||
# Equivalent to (days=-1)
|
||||
rd1 = relativedelta(days=-0.5, hours=-12)
|
||||
self.assertEqual(rd1.normalized(),
|
||||
relativedelta(days=-1))
|
||||
|
||||
# Equivalent to (days=-1)
|
||||
rd2 = relativedelta(days=-1.5, hours=12)
|
||||
self.assertEqual(rd2.normalized(),
|
||||
relativedelta(days=-1))
|
||||
|
||||
# Equivalent to (days=-1, hours=-14, minutes=-45)
|
||||
rd3 = relativedelta(days=-1.5, hours=-2.5, minutes=-15)
|
||||
self.assertEqual(rd3.normalized(),
|
||||
relativedelta(days=-1, hours=-14, minutes=-45))
|
||||
|
||||
# Equivalent to (days=-1, hours=-14, minutes=+15)
|
||||
rd4 = relativedelta(days=-1.5, hours=-2.5, minutes=45)
|
||||
self.assertEqual(rd4.normalized(),
|
||||
relativedelta(days=-1, hours=-14, minutes=+15))
|
||||
|
||||
# Carry back up - equivalent to:
|
||||
# (days=-2, hours=-2, minutes=0, seconds=-2, microseconds=-3)
|
||||
rd3 = relativedelta(days=-1.5, hours=-13, minutes=-59.50045,
|
||||
seconds=-31.473, microseconds=-500003)
|
||||
self.assertEqual(rd3.normalized(),
|
||||
relativedelta(days=-2, hours=-2, minutes=0,
|
||||
seconds=-2, microseconds=-3))
|
||||
|
||||
def testInvalidYearDay(self):
|
||||
with self.assertRaises(ValueError):
|
||||
relativedelta(yearday=367)
|
||||
|
||||
def testAddTimedeltaToUnpopulatedRelativedelta(self):
|
||||
td = timedelta(
|
||||
days=1,
|
||||
seconds=1,
|
||||
microseconds=1,
|
||||
milliseconds=1,
|
||||
minutes=1,
|
||||
hours=1,
|
||||
weeks=1
|
||||
)
|
||||
|
||||
expected = relativedelta(
|
||||
weeks=1,
|
||||
days=1,
|
||||
hours=1,
|
||||
minutes=1,
|
||||
seconds=1,
|
||||
microseconds=1001
|
||||
)
|
||||
|
||||
self.assertEqual(expected, relativedelta() + td)
|
||||
|
||||
def testAddTimedeltaToPopulatedRelativeDelta(self):
|
||||
td = timedelta(
|
||||
days=1,
|
||||
seconds=1,
|
||||
microseconds=1,
|
||||
milliseconds=1,
|
||||
minutes=1,
|
||||
hours=1,
|
||||
weeks=1
|
||||
)
|
||||
|
||||
rd = relativedelta(
|
||||
year=1,
|
||||
month=1,
|
||||
day=1,
|
||||
hour=1,
|
||||
minute=1,
|
||||
second=1,
|
||||
microsecond=1,
|
||||
years=1,
|
||||
months=1,
|
||||
days=1,
|
||||
weeks=1,
|
||||
hours=1,
|
||||
minutes=1,
|
||||
seconds=1,
|
||||
microseconds=1
|
||||
)
|
||||
|
||||
expected = relativedelta(
|
||||
year=1,
|
||||
month=1,
|
||||
day=1,
|
||||
hour=1,
|
||||
minute=1,
|
||||
second=1,
|
||||
microsecond=1,
|
||||
years=1,
|
||||
months=1,
|
||||
weeks=2,
|
||||
days=2,
|
||||
hours=2,
|
||||
minutes=2,
|
||||
seconds=2,
|
||||
microseconds=1002,
|
||||
)
|
||||
|
||||
self.assertEqual(expected, rd + td)
|
||||
|
||||
def testHashable(self):
|
||||
try:
|
||||
{relativedelta(minute=1): 'test'}
|
||||
except:
|
||||
self.fail("relativedelta() failed to hash!")
|
||||
|
||||
|
||||
class RelativeDeltaWeeksPropertyGetterTest(unittest.TestCase):
|
||||
"""Test the weeks property getter"""
|
||||
|
||||
def test_one_day(self):
|
||||
rd = relativedelta(days=1)
|
||||
self.assertEqual(rd.days, 1)
|
||||
self.assertEqual(rd.weeks, 0)
|
||||
|
||||
def test_minus_one_day(self):
|
||||
rd = relativedelta(days=-1)
|
||||
self.assertEqual(rd.days, -1)
|
||||
self.assertEqual(rd.weeks, 0)
|
||||
|
||||
def test_height_days(self):
|
||||
rd = relativedelta(days=8)
|
||||
self.assertEqual(rd.days, 8)
|
||||
self.assertEqual(rd.weeks, 1)
|
||||
|
||||
def test_minus_height_days(self):
|
||||
rd = relativedelta(days=-8)
|
||||
self.assertEqual(rd.days, -8)
|
||||
self.assertEqual(rd.weeks, -1)
|
||||
|
||||
|
||||
class RelativeDeltaWeeksPropertySetterTest(unittest.TestCase):
|
||||
"""Test the weeks setter which makes a "smart" update of the days attribute"""
|
||||
|
||||
def test_one_day_set_one_week(self):
|
||||
rd = relativedelta(days=1)
|
||||
rd.weeks = 1 # add 7 days
|
||||
self.assertEqual(rd.days, 8)
|
||||
self.assertEqual(rd.weeks, 1)
|
||||
|
||||
def test_minus_one_day_set_one_week(self):
|
||||
rd = relativedelta(days=-1)
|
||||
rd.weeks = 1 # add 7 days
|
||||
self.assertEqual(rd.days, 6)
|
||||
self.assertEqual(rd.weeks, 0)
|
||||
|
||||
def test_height_days_set_minus_one_week(self):
|
||||
rd = relativedelta(days=8)
|
||||
rd.weeks = -1 # change from 1 week, 1 day to -1 week, 1 day
|
||||
self.assertEqual(rd.days, -6)
|
||||
self.assertEqual(rd.weeks, 0)
|
||||
|
||||
def test_minus_height_days_set_minus_one_week(self):
|
||||
rd = relativedelta(days=-8)
|
||||
rd.weeks = -1 # does not change anything
|
||||
self.assertEqual(rd.days, -8)
|
||||
self.assertEqual(rd.weeks, -1)
|
||||
|
||||
|
||||
# vim:ts=4:sw=4:et
|
4842
resources/lib/libraries/dateutil/test/test_rrule.py
Normal file
4842
resources/lib/libraries/dateutil/test/test_rrule.py
Normal file
File diff suppressed because it is too large
Load diff
2603
resources/lib/libraries/dateutil/test/test_tz.py
Normal file
2603
resources/lib/libraries/dateutil/test/test_tz.py
Normal file
File diff suppressed because it is too large
Load diff
53
resources/lib/libraries/dateutil/test/test_utils.py
Normal file
53
resources/lib/libraries/dateutil/test/test_utils.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
import unittest
|
||||
|
||||
from dateutil import tz
|
||||
from dateutil import utils
|
||||
from dateutil.utils import within_delta
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
UTC = tz.tzutc()
|
||||
NYC = tz.gettz("America/New_York")
|
||||
|
||||
|
||||
class UtilsTest(unittest.TestCase):
|
||||
@freeze_time(datetime(2014, 12, 15, 1, 21, 33, 4003))
|
||||
def testToday(self):
|
||||
self.assertEqual(utils.today(), datetime(2014, 12, 15, 0, 0, 0))
|
||||
|
||||
@freeze_time(datetime(2014, 12, 15, 12), tz_offset=5)
|
||||
def testTodayTzInfo(self):
|
||||
self.assertEqual(utils.today(NYC),
|
||||
datetime(2014, 12, 15, 0, 0, 0, tzinfo=NYC))
|
||||
|
||||
@freeze_time(datetime(2014, 12, 15, 23), tz_offset=5)
|
||||
def testTodayTzInfoDifferentDay(self):
|
||||
self.assertEqual(utils.today(UTC),
|
||||
datetime(2014, 12, 16, 0, 0, 0, tzinfo=UTC))
|
||||
|
||||
def testDefaultTZInfoNaive(self):
|
||||
dt = datetime(2014, 9, 14, 9, 30)
|
||||
self.assertIs(utils.default_tzinfo(dt, NYC).tzinfo,
|
||||
NYC)
|
||||
|
||||
def testDefaultTZInfoAware(self):
|
||||
dt = datetime(2014, 9, 14, 9, 30, tzinfo=UTC)
|
||||
self.assertIs(utils.default_tzinfo(dt, NYC).tzinfo,
|
||||
UTC)
|
||||
|
||||
def testWithinDelta(self):
|
||||
d1 = datetime(2016, 1, 1, 12, 14, 1, 9)
|
||||
d2 = d1.replace(microsecond=15)
|
||||
|
||||
self.assertTrue(within_delta(d1, d2, timedelta(seconds=1)))
|
||||
self.assertFalse(within_delta(d1, d2, timedelta(microseconds=1)))
|
||||
|
||||
def testWithinDeltaWithNegativeDelta(self):
|
||||
d1 = datetime(2016, 1, 1)
|
||||
d2 = datetime(2015, 12, 31)
|
||||
|
||||
self.assertTrue(within_delta(d2, d1, timedelta(days=-1)))
|
17
resources/lib/libraries/dateutil/tz/__init__.py
Normal file
17
resources/lib/libraries/dateutil/tz/__init__.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .tz import *
|
||||
from .tz import __doc__
|
||||
|
||||
#: Convenience constant providing a :class:`tzutc()` instance
|
||||
#:
|
||||
#: .. versionadded:: 2.7.0
|
||||
UTC = tzutc()
|
||||
|
||||
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
|
||||
"tzstr", "tzical", "tzwin", "tzwinlocal", "gettz",
|
||||
"enfold", "datetime_ambiguous", "datetime_exists",
|
||||
"resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"]
|
||||
|
||||
|
||||
class DeprecatedTzFormatWarning(Warning):
|
||||
"""Warning raised when time zones are parsed from deprecated formats."""
|
415
resources/lib/libraries/dateutil/tz/_common.py
Normal file
415
resources/lib/libraries/dateutil/tz/_common.py
Normal file
|
@ -0,0 +1,415 @@
|
|||
from six import PY3
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from datetime import datetime, timedelta, tzinfo
|
||||
|
||||
|
||||
ZERO = timedelta(0)
|
||||
|
||||
__all__ = ['tzname_in_python2', 'enfold']
|
||||
|
||||
|
||||
def tzname_in_python2(namefunc):
|
||||
"""Change unicode output into bytestrings in Python 2
|
||||
|
||||
tzname() API changed in Python 3. It used to return bytes, but was changed
|
||||
to unicode strings
|
||||
"""
|
||||
def adjust_encoding(*args, **kwargs):
|
||||
name = namefunc(*args, **kwargs)
|
||||
if name is not None and not PY3:
|
||||
name = name.encode()
|
||||
|
||||
return name
|
||||
|
||||
return adjust_encoding
|
||||
|
||||
|
||||
# The following is adapted from Alexander Belopolsky's tz library
|
||||
# https://github.com/abalkin/tz
|
||||
if hasattr(datetime, 'fold'):
|
||||
# This is the pre-python 3.6 fold situation
|
||||
def enfold(dt, fold=1):
|
||||
"""
|
||||
Provides a unified interface for assigning the ``fold`` attribute to
|
||||
datetimes both before and after the implementation of PEP-495.
|
||||
|
||||
:param fold:
|
||||
The value for the ``fold`` attribute in the returned datetime. This
|
||||
should be either 0 or 1.
|
||||
|
||||
:return:
|
||||
Returns an object for which ``getattr(dt, 'fold', 0)`` returns
|
||||
``fold`` for all versions of Python. In versions prior to
|
||||
Python 3.6, this is a ``_DatetimeWithFold`` object, which is a
|
||||
subclass of :py:class:`datetime.datetime` with the ``fold``
|
||||
attribute added, if ``fold`` is 1.
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
"""
|
||||
return dt.replace(fold=fold)
|
||||
|
||||
else:
|
||||
class _DatetimeWithFold(datetime):
|
||||
"""
|
||||
This is a class designed to provide a PEP 495-compliant interface for
|
||||
Python versions before 3.6. It is used only for dates in a fold, so
|
||||
the ``fold`` attribute is fixed at ``1``.
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def replace(self, *args, **kwargs):
|
||||
"""
|
||||
Return a datetime with the same attributes, except for those
|
||||
attributes given new values by whichever keyword arguments are
|
||||
specified. Note that tzinfo=None can be specified to create a naive
|
||||
datetime from an aware datetime with no conversion of date and time
|
||||
data.
|
||||
|
||||
This is reimplemented in ``_DatetimeWithFold`` because pypy3 will
|
||||
return a ``datetime.datetime`` even if ``fold`` is unchanged.
|
||||
"""
|
||||
argnames = (
|
||||
'year', 'month', 'day', 'hour', 'minute', 'second',
|
||||
'microsecond', 'tzinfo'
|
||||
)
|
||||
|
||||
for arg, argname in zip(args, argnames):
|
||||
if argname in kwargs:
|
||||
raise TypeError('Duplicate argument: {}'.format(argname))
|
||||
|
||||
kwargs[argname] = arg
|
||||
|
||||
for argname in argnames:
|
||||
if argname not in kwargs:
|
||||
kwargs[argname] = getattr(self, argname)
|
||||
|
||||
dt_class = self.__class__ if kwargs.get('fold', 1) else datetime
|
||||
|
||||
return dt_class(**kwargs)
|
||||
|
||||
@property
|
||||
def fold(self):
|
||||
return 1
|
||||
|
||||
def enfold(dt, fold=1):
|
||||
"""
|
||||
Provides a unified interface for assigning the ``fold`` attribute to
|
||||
datetimes both before and after the implementation of PEP-495.
|
||||
|
||||
:param fold:
|
||||
The value for the ``fold`` attribute in the returned datetime. This
|
||||
should be either 0 or 1.
|
||||
|
||||
:return:
|
||||
Returns an object for which ``getattr(dt, 'fold', 0)`` returns
|
||||
``fold`` for all versions of Python. In versions prior to
|
||||
Python 3.6, this is a ``_DatetimeWithFold`` object, which is a
|
||||
subclass of :py:class:`datetime.datetime` with the ``fold``
|
||||
attribute added, if ``fold`` is 1.
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
"""
|
||||
if getattr(dt, 'fold', 0) == fold:
|
||||
return dt
|
||||
|
||||
args = dt.timetuple()[:6]
|
||||
args += (dt.microsecond, dt.tzinfo)
|
||||
|
||||
if fold:
|
||||
return _DatetimeWithFold(*args)
|
||||
else:
|
||||
return datetime(*args)
|
||||
|
||||
|
||||
def _validate_fromutc_inputs(f):
|
||||
"""
|
||||
The CPython version of ``fromutc`` checks that the input is a ``datetime``
|
||||
object and that ``self`` is attached as its ``tzinfo``.
|
||||
"""
|
||||
@wraps(f)
|
||||
def fromutc(self, dt):
|
||||
if not isinstance(dt, datetime):
|
||||
raise TypeError("fromutc() requires a datetime argument")
|
||||
if dt.tzinfo is not self:
|
||||
raise ValueError("dt.tzinfo is not self")
|
||||
|
||||
return f(self, dt)
|
||||
|
||||
return fromutc
|
||||
|
||||
|
||||
class _tzinfo(tzinfo):
|
||||
"""
|
||||
Base class for all ``dateutil`` ``tzinfo`` objects.
|
||||
"""
|
||||
|
||||
def is_ambiguous(self, dt):
|
||||
"""
|
||||
Whether or not the "wall time" of a given datetime is ambiguous in this
|
||||
zone.
|
||||
|
||||
:param dt:
|
||||
A :py:class:`datetime.datetime`, naive or time zone aware.
|
||||
|
||||
|
||||
:return:
|
||||
Returns ``True`` if ambiguous, ``False`` otherwise.
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
"""
|
||||
|
||||
dt = dt.replace(tzinfo=self)
|
||||
|
||||
wall_0 = enfold(dt, fold=0)
|
||||
wall_1 = enfold(dt, fold=1)
|
||||
|
||||
same_offset = wall_0.utcoffset() == wall_1.utcoffset()
|
||||
same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None)
|
||||
|
||||
return same_dt and not same_offset
|
||||
|
||||
def _fold_status(self, dt_utc, dt_wall):
|
||||
"""
|
||||
Determine the fold status of a "wall" datetime, given a representation
|
||||
of the same datetime as a (naive) UTC datetime. This is calculated based
|
||||
on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all
|
||||
datetimes, and that this offset is the actual number of hours separating
|
||||
``dt_utc`` and ``dt_wall``.
|
||||
|
||||
:param dt_utc:
|
||||
Representation of the datetime as UTC
|
||||
|
||||
:param dt_wall:
|
||||
Representation of the datetime as "wall time". This parameter must
|
||||
either have a `fold` attribute or have a fold-naive
|
||||
:class:`datetime.tzinfo` attached, otherwise the calculation may
|
||||
fail.
|
||||
"""
|
||||
if self.is_ambiguous(dt_wall):
|
||||
delta_wall = dt_wall - dt_utc
|
||||
_fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst()))
|
||||
else:
|
||||
_fold = 0
|
||||
|
||||
return _fold
|
||||
|
||||
def _fold(self, dt):
|
||||
return getattr(dt, 'fold', 0)
|
||||
|
||||
def _fromutc(self, dt):
|
||||
"""
|
||||
Given a timezone-aware datetime in a given timezone, calculates a
|
||||
timezone-aware datetime in a new timezone.
|
||||
|
||||
Since this is the one time that we *know* we have an unambiguous
|
||||
datetime object, we take this opportunity to determine whether the
|
||||
datetime is ambiguous and in a "fold" state (e.g. if it's the first
|
||||
occurence, chronologically, of the ambiguous datetime).
|
||||
|
||||
:param dt:
|
||||
A timezone-aware :class:`datetime.datetime` object.
|
||||
"""
|
||||
|
||||
# Re-implement the algorithm from Python's datetime.py
|
||||
dtoff = dt.utcoffset()
|
||||
if dtoff is None:
|
||||
raise ValueError("fromutc() requires a non-None utcoffset() "
|
||||
"result")
|
||||
|
||||
# The original datetime.py code assumes that `dst()` defaults to
|
||||
# zero during ambiguous times. PEP 495 inverts this presumption, so
|
||||
# for pre-PEP 495 versions of python, we need to tweak the algorithm.
|
||||
dtdst = dt.dst()
|
||||
if dtdst is None:
|
||||
raise ValueError("fromutc() requires a non-None dst() result")
|
||||
delta = dtoff - dtdst
|
||||
|
||||
dt += delta
|
||||
# Set fold=1 so we can default to being in the fold for
|
||||
# ambiguous dates.
|
||||
dtdst = enfold(dt, fold=1).dst()
|
||||
if dtdst is None:
|
||||
raise ValueError("fromutc(): dt.dst gave inconsistent "
|
||||
"results; cannot convert")
|
||||
return dt + dtdst
|
||||
|
||||
@_validate_fromutc_inputs
|
||||
def fromutc(self, dt):
|
||||
"""
|
||||
Given a timezone-aware datetime in a given timezone, calculates a
|
||||
timezone-aware datetime in a new timezone.
|
||||
|
||||
Since this is the one time that we *know* we have an unambiguous
|
||||
datetime object, we take this opportunity to determine whether the
|
||||
datetime is ambiguous and in a "fold" state (e.g. if it's the first
|
||||
occurance, chronologically, of the ambiguous datetime).
|
||||
|
||||
:param dt:
|
||||
A timezone-aware :class:`datetime.datetime` object.
|
||||
"""
|
||||
dt_wall = self._fromutc(dt)
|
||||
|
||||
# Calculate the fold status given the two datetimes.
|
||||
_fold = self._fold_status(dt, dt_wall)
|
||||
|
||||
# Set the default fold value for ambiguous dates
|
||||
return enfold(dt_wall, fold=_fold)
|
||||
|
||||
|
||||
class tzrangebase(_tzinfo):
|
||||
"""
|
||||
This is an abstract base class for time zones represented by an annual
|
||||
transition into and out of DST. Child classes should implement the following
|
||||
methods:
|
||||
|
||||
* ``__init__(self, *args, **kwargs)``
|
||||
* ``transitions(self, year)`` - this is expected to return a tuple of
|
||||
datetimes representing the DST on and off transitions in standard
|
||||
time.
|
||||
|
||||
A fully initialized ``tzrangebase`` subclass should also provide the
|
||||
following attributes:
|
||||
* ``hasdst``: Boolean whether or not the zone uses DST.
|
||||
* ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects
|
||||
representing the respective UTC offsets.
|
||||
* ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short
|
||||
abbreviations in DST and STD, respectively.
|
||||
* ``_hasdst``: Whether or not the zone has DST.
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
"""
|
||||
def __init__(self):
|
||||
raise NotImplementedError('tzrangebase is an abstract base class')
|
||||
|
||||
def utcoffset(self, dt):
|
||||
isdst = self._isdst(dt)
|
||||
|
||||
if isdst is None:
|
||||
return None
|
||||
elif isdst:
|
||||
return self._dst_offset
|
||||
else:
|
||||
return self._std_offset
|
||||
|
||||
def dst(self, dt):
|
||||
isdst = self._isdst(dt)
|
||||
|
||||
if isdst is None:
|
||||
return None
|
||||
elif isdst:
|
||||
return self._dst_base_offset
|
||||
else:
|
||||
return ZERO
|
||||
|
||||
@tzname_in_python2
|
||||
def tzname(self, dt):
|
||||
if self._isdst(dt):
|
||||
return self._dst_abbr
|
||||
else:
|
||||
return self._std_abbr
|
||||
|
||||
def fromutc(self, dt):
|
||||
""" Given a datetime in UTC, return local time """
|
||||
if not isinstance(dt, datetime):
|
||||
raise TypeError("fromutc() requires a datetime argument")
|
||||
|
||||
if dt.tzinfo is not self:
|
||||
raise ValueError("dt.tzinfo is not self")
|
||||
|
||||
# Get transitions - if there are none, fixed offset
|
||||
transitions = self.transitions(dt.year)
|
||||
if transitions is None:
|
||||
return dt + self.utcoffset(dt)
|
||||
|
||||
# Get the transition times in UTC
|
||||
dston, dstoff = transitions
|
||||
|
||||
dston -= self._std_offset
|
||||
dstoff -= self._std_offset
|
||||
|
||||
utc_transitions = (dston, dstoff)
|
||||
dt_utc = dt.replace(tzinfo=None)
|
||||
|
||||
isdst = self._naive_isdst(dt_utc, utc_transitions)
|
||||
|
||||
if isdst:
|
||||
dt_wall = dt + self._dst_offset
|
||||
else:
|
||||
dt_wall = dt + self._std_offset
|
||||
|
||||
_fold = int(not isdst and self.is_ambiguous(dt_wall))
|
||||
|
||||
return enfold(dt_wall, fold=_fold)
|
||||
|
||||
def is_ambiguous(self, dt):
|
||||
"""
|
||||
Whether or not the "wall time" of a given datetime is ambiguous in this
|
||||
zone.
|
||||
|
||||
:param dt:
|
||||
A :py:class:`datetime.datetime`, naive or time zone aware.
|
||||
|
||||
|
||||
:return:
|
||||
Returns ``True`` if ambiguous, ``False`` otherwise.
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
"""
|
||||
if not self.hasdst:
|
||||
return False
|
||||
|
||||
start, end = self.transitions(dt.year)
|
||||
|
||||
dt = dt.replace(tzinfo=None)
|
||||
return (end <= dt < end + self._dst_base_offset)
|
||||
|
||||
def _isdst(self, dt):
|
||||
if not self.hasdst:
|
||||
return False
|
||||
elif dt is None:
|
||||
return None
|
||||
|
||||
transitions = self.transitions(dt.year)
|
||||
|
||||
if transitions is None:
|
||||
return False
|
||||
|
||||
dt = dt.replace(tzinfo=None)
|
||||
|
||||
isdst = self._naive_isdst(dt, transitions)
|
||||
|
||||
# Handle ambiguous dates
|
||||
if not isdst and self.is_ambiguous(dt):
|
||||
return not self._fold(dt)
|
||||
else:
|
||||
return isdst
|
||||
|
||||
def _naive_isdst(self, dt, transitions):
|
||||
dston, dstoff = transitions
|
||||
|
||||
dt = dt.replace(tzinfo=None)
|
||||
|
||||
if dston < dstoff:
|
||||
isdst = dston <= dt < dstoff
|
||||
else:
|
||||
isdst = not dstoff <= dt < dston
|
||||
|
||||
return isdst
|
||||
|
||||
@property
|
||||
def _dst_base_offset(self):
|
||||
return self._dst_offset - self._std_offset
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(...)" % self.__class__.__name__
|
||||
|
||||
__reduce__ = object.__reduce__
|
49
resources/lib/libraries/dateutil/tz/_factories.py
Normal file
49
resources/lib/libraries/dateutil/tz/_factories.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
from datetime import timedelta
|
||||
|
||||
|
||||
class _TzSingleton(type):
|
||||
def __init__(cls, *args, **kwargs):
|
||||
cls.__instance = None
|
||||
super(_TzSingleton, cls).__init__(*args, **kwargs)
|
||||
|
||||
def __call__(cls):
|
||||
if cls.__instance is None:
|
||||
cls.__instance = super(_TzSingleton, cls).__call__()
|
||||
return cls.__instance
|
||||
|
||||
class _TzFactory(type):
|
||||
def instance(cls, *args, **kwargs):
|
||||
"""Alternate constructor that returns a fresh instance"""
|
||||
return type.__call__(cls, *args, **kwargs)
|
||||
|
||||
|
||||
class _TzOffsetFactory(_TzFactory):
|
||||
def __init__(cls, *args, **kwargs):
|
||||
cls.__instances = {}
|
||||
|
||||
def __call__(cls, name, offset):
|
||||
if isinstance(offset, timedelta):
|
||||
key = (name, offset.total_seconds())
|
||||
else:
|
||||
key = (name, offset)
|
||||
|
||||
instance = cls.__instances.get(key, None)
|
||||
if instance is None:
|
||||
instance = cls.__instances.setdefault(key,
|
||||
cls.instance(name, offset))
|
||||
return instance
|
||||
|
||||
|
||||
class _TzStrFactory(_TzFactory):
|
||||
def __init__(cls, *args, **kwargs):
|
||||
cls.__instances = {}
|
||||
|
||||
def __call__(cls, s, posix_offset=False):
|
||||
key = (s, posix_offset)
|
||||
instance = cls.__instances.get(key, None)
|
||||
|
||||
if instance is None:
|
||||
instance = cls.__instances.setdefault(key,
|
||||
cls.instance(s, posix_offset))
|
||||
return instance
|
||||
|
1785
resources/lib/libraries/dateutil/tz/tz.py
Normal file
1785
resources/lib/libraries/dateutil/tz/tz.py
Normal file
File diff suppressed because it is too large
Load diff
331
resources/lib/libraries/dateutil/tz/win.py
Normal file
331
resources/lib/libraries/dateutil/tz/win.py
Normal file
|
@ -0,0 +1,331 @@
|
|||
# This code was originally contributed by Jeffrey Harris.
|
||||
import datetime
|
||||
import struct
|
||||
|
||||
from six.moves import winreg
|
||||
from six import text_type
|
||||
|
||||
try:
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
except ValueError:
|
||||
# ValueError is raised on non-Windows systems for some horrible reason.
|
||||
raise ImportError("Running tzwin on non-Windows system")
|
||||
|
||||
from ._common import tzrangebase
|
||||
|
||||
__all__ = ["tzwin", "tzwinlocal", "tzres"]
|
||||
|
||||
ONEWEEK = datetime.timedelta(7)
|
||||
|
||||
TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
|
||||
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
|
||||
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
||||
|
||||
|
||||
def _settzkeyname():
|
||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
||||
try:
|
||||
winreg.OpenKey(handle, TZKEYNAMENT).Close()
|
||||
TZKEYNAME = TZKEYNAMENT
|
||||
except WindowsError:
|
||||
TZKEYNAME = TZKEYNAME9X
|
||||
handle.Close()
|
||||
return TZKEYNAME
|
||||
|
||||
|
||||
TZKEYNAME = _settzkeyname()
|
||||
|
||||
|
||||
class tzres(object):
|
||||
"""
|
||||
Class for accessing `tzres.dll`, which contains timezone name related
|
||||
resources.
|
||||
|
||||
.. versionadded:: 2.5.0
|
||||
"""
|
||||
p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char
|
||||
|
||||
def __init__(self, tzres_loc='tzres.dll'):
|
||||
# Load the user32 DLL so we can load strings from tzres
|
||||
user32 = ctypes.WinDLL('user32')
|
||||
|
||||
# Specify the LoadStringW function
|
||||
user32.LoadStringW.argtypes = (wintypes.HINSTANCE,
|
||||
wintypes.UINT,
|
||||
wintypes.LPWSTR,
|
||||
ctypes.c_int)
|
||||
|
||||
self.LoadStringW = user32.LoadStringW
|
||||
self._tzres = ctypes.WinDLL(tzres_loc)
|
||||
self.tzres_loc = tzres_loc
|
||||
|
||||
def load_name(self, offset):
|
||||
"""
|
||||
Load a timezone name from a DLL offset (integer).
|
||||
|
||||
>>> from dateutil.tzwin import tzres
|
||||
>>> tzr = tzres()
|
||||
>>> print(tzr.load_name(112))
|
||||
'Eastern Standard Time'
|
||||
|
||||
:param offset:
|
||||
A positive integer value referring to a string from the tzres dll.
|
||||
|
||||
..note:
|
||||
Offsets found in the registry are generally of the form
|
||||
`@tzres.dll,-114`. The offset in this case if 114, not -114.
|
||||
|
||||
"""
|
||||
resource = self.p_wchar()
|
||||
lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR)
|
||||
nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0)
|
||||
return resource[:nchar]
|
||||
|
||||
def name_from_string(self, tzname_str):
|
||||
"""
|
||||
Parse strings as returned from the Windows registry into the time zone
|
||||
name as defined in the registry.
|
||||
|
||||
>>> from dateutil.tzwin import tzres
|
||||
>>> tzr = tzres()
|
||||
>>> print(tzr.name_from_string('@tzres.dll,-251'))
|
||||
'Dateline Daylight Time'
|
||||
>>> print(tzr.name_from_string('Eastern Standard Time'))
|
||||
'Eastern Standard Time'
|
||||
|
||||
:param tzname_str:
|
||||
A timezone name string as returned from a Windows registry key.
|
||||
|
||||
:return:
|
||||
Returns the localized timezone string from tzres.dll if the string
|
||||
is of the form `@tzres.dll,-offset`, else returns the input string.
|
||||
"""
|
||||
if not tzname_str.startswith('@'):
|
||||
return tzname_str
|
||||
|
||||
name_splt = tzname_str.split(',-')
|
||||
try:
|
||||
offset = int(name_splt[1])
|
||||
except:
|
||||
raise ValueError("Malformed timezone string.")
|
||||
|
||||
return self.load_name(offset)
|
||||
|
||||
|
||||
class tzwinbase(tzrangebase):
|
||||
"""tzinfo class based on win32's timezones available in the registry."""
|
||||
def __init__(self):
|
||||
raise NotImplementedError('tzwinbase is an abstract base class')
|
||||
|
||||
def __eq__(self, other):
|
||||
# Compare on all relevant dimensions, including name.
|
||||
if not isinstance(other, tzwinbase):
|
||||
return NotImplemented
|
||||
|
||||
return (self._std_offset == other._std_offset and
|
||||
self._dst_offset == other._dst_offset and
|
||||
self._stddayofweek == other._stddayofweek and
|
||||
self._dstdayofweek == other._dstdayofweek and
|
||||
self._stdweeknumber == other._stdweeknumber and
|
||||
self._dstweeknumber == other._dstweeknumber and
|
||||
self._stdhour == other._stdhour and
|
||||
self._dsthour == other._dsthour and
|
||||
self._stdminute == other._stdminute and
|
||||
self._dstminute == other._dstminute and
|
||||
self._std_abbr == other._std_abbr and
|
||||
self._dst_abbr == other._dst_abbr)
|
||||
|
||||
@staticmethod
|
||||
def list():
|
||||
"""Return a list of all time zones known to the system."""
|
||||
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||
with winreg.OpenKey(handle, TZKEYNAME) as tzkey:
|
||||
result = [winreg.EnumKey(tzkey, i)
|
||||
for i in range(winreg.QueryInfoKey(tzkey)[0])]
|
||||
return result
|
||||
|
||||
def display(self):
|
||||
return self._display
|
||||
|
||||
def transitions(self, year):
|
||||
"""
|
||||
For a given year, get the DST on and off transition times, expressed
|
||||
always on the standard time side. For zones with no transitions, this
|
||||
function returns ``None``.
|
||||
|
||||
:param year:
|
||||
The year whose transitions you would like to query.
|
||||
|
||||
:return:
|
||||
Returns a :class:`tuple` of :class:`datetime.datetime` objects,
|
||||
``(dston, dstoff)`` for zones with an annual DST transition, or
|
||||
``None`` for fixed offset zones.
|
||||
"""
|
||||
|
||||
if not self.hasdst:
|
||||
return None
|
||||
|
||||
dston = picknthweekday(year, self._dstmonth, self._dstdayofweek,
|
||||
self._dsthour, self._dstminute,
|
||||
self._dstweeknumber)
|
||||
|
||||
dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek,
|
||||
self._stdhour, self._stdminute,
|
||||
self._stdweeknumber)
|
||||
|
||||
# Ambiguous dates default to the STD side
|
||||
dstoff -= self._dst_base_offset
|
||||
|
||||
return dston, dstoff
|
||||
|
||||
def _get_hasdst(self):
|
||||
return self._dstmonth != 0
|
||||
|
||||
@property
|
||||
def _dst_base_offset(self):
|
||||
return self._dst_base_offset_
|
||||
|
||||
|
||||
class tzwin(tzwinbase):
|
||||
|
||||
def __init__(self, name):
|
||||
self._name = name
|
||||
|
||||
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||
tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name)
|
||||
with winreg.OpenKey(handle, tzkeyname) as tzkey:
|
||||
keydict = valuestodict(tzkey)
|
||||
|
||||
self._std_abbr = keydict["Std"]
|
||||
self._dst_abbr = keydict["Dlt"]
|
||||
|
||||
self._display = keydict["Display"]
|
||||
|
||||
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
||||
tup = struct.unpack("=3l16h", keydict["TZI"])
|
||||
stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
|
||||
dstoffset = stdoffset-tup[2] # + DaylightBias * -1
|
||||
self._std_offset = datetime.timedelta(minutes=stdoffset)
|
||||
self._dst_offset = datetime.timedelta(minutes=dstoffset)
|
||||
|
||||
# for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
|
||||
(self._stdmonth,
|
||||
self._stddayofweek, # Sunday = 0
|
||||
self._stdweeknumber, # Last = 5
|
||||
self._stdhour,
|
||||
self._stdminute) = tup[4:9]
|
||||
|
||||
(self._dstmonth,
|
||||
self._dstdayofweek, # Sunday = 0
|
||||
self._dstweeknumber, # Last = 5
|
||||
self._dsthour,
|
||||
self._dstminute) = tup[12:17]
|
||||
|
||||
self._dst_base_offset_ = self._dst_offset - self._std_offset
|
||||
self.hasdst = self._get_hasdst()
|
||||
|
||||
def __repr__(self):
|
||||
return "tzwin(%s)" % repr(self._name)
|
||||
|
||||
def __reduce__(self):
|
||||
return (self.__class__, (self._name,))
|
||||
|
||||
|
||||
class tzwinlocal(tzwinbase):
|
||||
def __init__(self):
|
||||
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
||||
keydict = valuestodict(tzlocalkey)
|
||||
|
||||
self._std_abbr = keydict["StandardName"]
|
||||
self._dst_abbr = keydict["DaylightName"]
|
||||
|
||||
try:
|
||||
tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME,
|
||||
sn=self._std_abbr)
|
||||
with winreg.OpenKey(handle, tzkeyname) as tzkey:
|
||||
_keydict = valuestodict(tzkey)
|
||||
self._display = _keydict["Display"]
|
||||
except OSError:
|
||||
self._display = None
|
||||
|
||||
stdoffset = -keydict["Bias"]-keydict["StandardBias"]
|
||||
dstoffset = stdoffset-keydict["DaylightBias"]
|
||||
|
||||
self._std_offset = datetime.timedelta(minutes=stdoffset)
|
||||
self._dst_offset = datetime.timedelta(minutes=dstoffset)
|
||||
|
||||
# For reasons unclear, in this particular key, the day of week has been
|
||||
# moved to the END of the SYSTEMTIME structure.
|
||||
tup = struct.unpack("=8h", keydict["StandardStart"])
|
||||
|
||||
(self._stdmonth,
|
||||
self._stdweeknumber, # Last = 5
|
||||
self._stdhour,
|
||||
self._stdminute) = tup[1:5]
|
||||
|
||||
self._stddayofweek = tup[7]
|
||||
|
||||
tup = struct.unpack("=8h", keydict["DaylightStart"])
|
||||
|
||||
(self._dstmonth,
|
||||
self._dstweeknumber, # Last = 5
|
||||
self._dsthour,
|
||||
self._dstminute) = tup[1:5]
|
||||
|
||||
self._dstdayofweek = tup[7]
|
||||
|
||||
self._dst_base_offset_ = self._dst_offset - self._std_offset
|
||||
self.hasdst = self._get_hasdst()
|
||||
|
||||
def __repr__(self):
|
||||
return "tzwinlocal()"
|
||||
|
||||
def __str__(self):
|
||||
# str will return the standard name, not the daylight name.
|
||||
return "tzwinlocal(%s)" % repr(self._std_abbr)
|
||||
|
||||
def __reduce__(self):
|
||||
return (self.__class__, ())
|
||||
|
||||
|
||||
def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
|
||||
""" dayofweek == 0 means Sunday, whichweek 5 means last instance """
|
||||
first = datetime.datetime(year, month, 1, hour, minute)
|
||||
|
||||
# This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6),
|
||||
# Because 7 % 7 = 0
|
||||
weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1)
|
||||
wd = weekdayone + ((whichweek - 1) * ONEWEEK)
|
||||
if (wd.month != month):
|
||||
wd -= ONEWEEK
|
||||
|
||||
return wd
|
||||
|
||||
|
||||
def valuestodict(key):
|
||||
"""Convert a registry key's values to a dictionary."""
|
||||
dout = {}
|
||||
size = winreg.QueryInfoKey(key)[1]
|
||||
tz_res = None
|
||||
|
||||
for i in range(size):
|
||||
key_name, value, dtype = winreg.EnumValue(key, i)
|
||||
if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN:
|
||||
# If it's a DWORD (32-bit integer), it's stored as unsigned - convert
|
||||
# that to a proper signed integer
|
||||
if value & (1 << 31):
|
||||
value = value - (1 << 32)
|
||||
elif dtype == winreg.REG_SZ:
|
||||
# If it's a reference to the tzres DLL, load the actual string
|
||||
if value.startswith('@tzres'):
|
||||
tz_res = tz_res or tzres()
|
||||
value = tz_res.name_from_string(value)
|
||||
|
||||
value = value.rstrip('\x00') # Remove trailing nulls
|
||||
|
||||
dout[key_name] = value
|
||||
|
||||
return dout
|
2
resources/lib/libraries/dateutil/tzwin.py
Normal file
2
resources/lib/libraries/dateutil/tzwin.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
# tzwin has moved to dateutil.tz.win
|
||||
from .tz.win import *
|
71
resources/lib/libraries/dateutil/utils.py
Normal file
71
resources/lib/libraries/dateutil/utils.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This module offers general convenience and utility functions for dealing with
|
||||
datetimes.
|
||||
|
||||
.. versionadded:: 2.7.0
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from datetime import datetime, time
|
||||
|
||||
|
||||
def today(tzinfo=None):
|
||||
"""
|
||||
Returns a :py:class:`datetime` representing the current day at midnight
|
||||
|
||||
:param tzinfo:
|
||||
The time zone to attach (also used to determine the current day).
|
||||
|
||||
:return:
|
||||
A :py:class:`datetime.datetime` object representing the current day
|
||||
at midnight.
|
||||
"""
|
||||
|
||||
dt = datetime.now(tzinfo)
|
||||
return datetime.combine(dt.date(), time(0, tzinfo=tzinfo))
|
||||
|
||||
|
||||
def default_tzinfo(dt, tzinfo):
|
||||
"""
|
||||
Sets the the ``tzinfo`` parameter on naive datetimes only
|
||||
|
||||
This is useful for example when you are provided a datetime that may have
|
||||
either an implicit or explicit time zone, such as when parsing a time zone
|
||||
string.
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> from dateutil.tz import tzoffset
|
||||
>>> from dateutil.parser import parse
|
||||
>>> from dateutil.utils import default_tzinfo
|
||||
>>> dflt_tz = tzoffset("EST", -18000)
|
||||
>>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz))
|
||||
2014-01-01 12:30:00+00:00
|
||||
>>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz))
|
||||
2014-01-01 12:30:00-05:00
|
||||
|
||||
:param dt:
|
||||
The datetime on which to replace the time zone
|
||||
|
||||
:param tzinfo:
|
||||
The :py:class:`datetime.tzinfo` subclass instance to assign to
|
||||
``dt`` if (and only if) it is naive.
|
||||
|
||||
:return:
|
||||
Returns an aware :py:class:`datetime.datetime`.
|
||||
"""
|
||||
if dt.tzinfo is not None:
|
||||
return dt
|
||||
else:
|
||||
return dt.replace(tzinfo=tzinfo)
|
||||
|
||||
|
||||
def within_delta(dt1, dt2, delta):
|
||||
"""
|
||||
Useful for comparing two datetimes that may a negilible difference
|
||||
to be considered equal.
|
||||
"""
|
||||
delta = abs(delta)
|
||||
difference = dt1 - dt2
|
||||
return -delta <= difference <= delta
|
167
resources/lib/libraries/dateutil/zoneinfo/__init__.py
Normal file
167
resources/lib/libraries/dateutil/zoneinfo/__init__.py
Normal file
|
@ -0,0 +1,167 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import warnings
|
||||
import json
|
||||
|
||||
from tarfile import TarFile
|
||||
from pkgutil import get_data
|
||||
from io import BytesIO
|
||||
|
||||
from dateutil.tz import tzfile as _tzfile
|
||||
|
||||
__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"]
|
||||
|
||||
ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
||||
METADATA_FN = 'METADATA'
|
||||
|
||||
|
||||
class tzfile(_tzfile):
|
||||
def __reduce__(self):
|
||||
return (gettz, (self._filename,))
|
||||
|
||||
|
||||
def getzoneinfofile_stream():
|
||||
try:
|
||||
return BytesIO(get_data(__name__, ZONEFILENAME))
|
||||
except IOError as e: # TODO switch to FileNotFoundError?
|
||||
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
||||
return None
|
||||
|
||||
|
||||
class ZoneInfoFile(object):
|
||||
def __init__(self, zonefile_stream=None):
|
||||
if zonefile_stream is not None:
|
||||
with TarFile.open(fileobj=zonefile_stream) as tf:
|
||||
self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name)
|
||||
for zf in tf.getmembers()
|
||||
if zf.isfile() and zf.name != METADATA_FN}
|
||||
# deal with links: They'll point to their parent object. Less
|
||||
# waste of memory
|
||||
links = {zl.name: self.zones[zl.linkname]
|
||||
for zl in tf.getmembers() if
|
||||
zl.islnk() or zl.issym()}
|
||||
self.zones.update(links)
|
||||
try:
|
||||
metadata_json = tf.extractfile(tf.getmember(METADATA_FN))
|
||||
metadata_str = metadata_json.read().decode('UTF-8')
|
||||
self.metadata = json.loads(metadata_str)
|
||||
except KeyError:
|
||||
# no metadata in tar file
|
||||
self.metadata = None
|
||||
else:
|
||||
self.zones = {}
|
||||
self.metadata = None
|
||||
|
||||
def get(self, name, default=None):
|
||||
"""
|
||||
Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method
|
||||
for retrieving zones from the zone dictionary.
|
||||
|
||||
:param name:
|
||||
The name of the zone to retrieve. (Generally IANA zone names)
|
||||
|
||||
:param default:
|
||||
The value to return in the event of a missing key.
|
||||
|
||||
.. versionadded:: 2.6.0
|
||||
|
||||
"""
|
||||
return self.zones.get(name, default)
|
||||
|
||||
|
||||
# The current API has gettz as a module function, although in fact it taps into
|
||||
# a stateful class. So as a workaround for now, without changing the API, we
|
||||
# will create a new "global" class instance the first time a user requests a
|
||||
# timezone. Ugly, but adheres to the api.
|
||||
#
|
||||
# TODO: Remove after deprecation period.
|
||||
_CLASS_ZONE_INSTANCE = []
|
||||
|
||||
|
||||
def get_zonefile_instance(new_instance=False):
|
||||
"""
|
||||
This is a convenience function which provides a :class:`ZoneInfoFile`
|
||||
instance using the data provided by the ``dateutil`` package. By default, it
|
||||
caches a single instance of the ZoneInfoFile object and returns that.
|
||||
|
||||
:param new_instance:
|
||||
If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and
|
||||
used as the cached instance for the next call. Otherwise, new instances
|
||||
are created only as necessary.
|
||||
|
||||
:return:
|
||||
Returns a :class:`ZoneInfoFile` object.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
"""
|
||||
if new_instance:
|
||||
zif = None
|
||||
else:
|
||||
zif = getattr(get_zonefile_instance, '_cached_instance', None)
|
||||
|
||||
if zif is None:
|
||||
zif = ZoneInfoFile(getzoneinfofile_stream())
|
||||
|
||||
get_zonefile_instance._cached_instance = zif
|
||||
|
||||
return zif
|
||||
|
||||
|
||||
def gettz(name):
|
||||
"""
|
||||
This retrieves a time zone from the local zoneinfo tarball that is packaged
|
||||
with dateutil.
|
||||
|
||||
:param name:
|
||||
An IANA-style time zone name, as found in the zoneinfo file.
|
||||
|
||||
:return:
|
||||
Returns a :class:`dateutil.tz.tzfile` time zone object.
|
||||
|
||||
.. warning::
|
||||
It is generally inadvisable to use this function, and it is only
|
||||
provided for API compatibility with earlier versions. This is *not*
|
||||
equivalent to ``dateutil.tz.gettz()``, which selects an appropriate
|
||||
time zone based on the inputs, favoring system zoneinfo. This is ONLY
|
||||
for accessing the dateutil-specific zoneinfo (which may be out of
|
||||
date compared to the system zoneinfo).
|
||||
|
||||
.. deprecated:: 2.6
|
||||
If you need to use a specific zoneinfofile over the system zoneinfo,
|
||||
instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call
|
||||
:func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead.
|
||||
|
||||
Use :func:`get_zonefile_instance` to retrieve an instance of the
|
||||
dateutil-provided zoneinfo.
|
||||
"""
|
||||
warnings.warn("zoneinfo.gettz() will be removed in future versions, "
|
||||
"to use the dateutil-provided zoneinfo files, instantiate a "
|
||||
"ZoneInfoFile object and use ZoneInfoFile.zones.get() "
|
||||
"instead. See the documentation for details.",
|
||||
DeprecationWarning)
|
||||
|
||||
if len(_CLASS_ZONE_INSTANCE) == 0:
|
||||
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
||||
return _CLASS_ZONE_INSTANCE[0].zones.get(name)
|
||||
|
||||
|
||||
def gettz_db_metadata():
|
||||
""" Get the zonefile metadata
|
||||
|
||||
See `zonefile_metadata`_
|
||||
|
||||
:returns:
|
||||
A dictionary with the database metadata
|
||||
|
||||
.. deprecated:: 2.6
|
||||
See deprecation warning in :func:`zoneinfo.gettz`. To get metadata,
|
||||
query the attribute ``zoneinfo.ZoneInfoFile.metadata``.
|
||||
"""
|
||||
warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future "
|
||||
"versions, to use the dateutil-provided zoneinfo files, "
|
||||
"ZoneInfoFile object and query the 'metadata' attribute "
|
||||
"instead. See the documentation for details.",
|
||||
DeprecationWarning)
|
||||
|
||||
if len(_CLASS_ZONE_INSTANCE) == 0:
|
||||
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
||||
return _CLASS_ZONE_INSTANCE[0].metadata
|
Binary file not shown.
53
resources/lib/libraries/dateutil/zoneinfo/rebuild.py
Normal file
53
resources/lib/libraries/dateutil/zoneinfo/rebuild.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import shutil
|
||||
import json
|
||||
from subprocess import check_call
|
||||
from tarfile import TarFile
|
||||
|
||||
from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME
|
||||
|
||||
|
||||
def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None):
|
||||
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
||||
|
||||
filename is the timezone tarball from ``ftp.iana.org/tz``.
|
||||
|
||||
"""
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
zonedir = os.path.join(tmpdir, "zoneinfo")
|
||||
moduledir = os.path.dirname(__file__)
|
||||
try:
|
||||
with TarFile.open(filename) as tf:
|
||||
for name in zonegroups:
|
||||
tf.extract(name, tmpdir)
|
||||
filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
|
||||
try:
|
||||
check_call(["zic", "-d", zonedir] + filepaths)
|
||||
except OSError as e:
|
||||
_print_on_nosuchfile(e)
|
||||
raise
|
||||
# write metadata file
|
||||
with open(os.path.join(zonedir, METADATA_FN), 'w') as f:
|
||||
json.dump(metadata, f, indent=4, sort_keys=True)
|
||||
target = os.path.join(moduledir, ZONEFILENAME)
|
||||
with TarFile.open(target, "w:%s" % format) as tf:
|
||||
for entry in os.listdir(zonedir):
|
||||
entrypath = os.path.join(zonedir, entry)
|
||||
tf.add(entrypath, entry)
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
def _print_on_nosuchfile(e):
|
||||
"""Print helpful troubleshooting message
|
||||
|
||||
e is an exception raised by subprocess.check_call()
|
||||
|
||||
"""
|
||||
if e.errno == 2:
|
||||
logging.error(
|
||||
"Could not find zic. Perhaps you need to install "
|
||||
"libc-bin or some other package that provides it, "
|
||||
"or it's not in your PATH?")
|
Loading…
Add table
Reference in a new issue