Replace local copy of dateutil 1.5, use dependency
Having a local copy of a library is a bad practice. Files are duplicated on the system, it's harder to update it. For example, the local copy of dateutil 1.5 is not compatible with Python 3. Replace the local copy with a dependency to dateutil, at least the version 2.4.2. dateutil 2.4.2 is compatible with Python 3. Partially implements blueprint: support-python3 Change-Id: I366c8004e5eb683b41fec2c31cf4a5afdb48180c
This commit is contained in:
parent
41d26396d6
commit
ef58545cf6
|
@ -19,7 +19,7 @@ import datetime
|
|||
import six
|
||||
from six.moves import range
|
||||
|
||||
from thirdparty_dateutil import parser as datetime_parser
|
||||
from dateutil import parser as datetime_parser
|
||||
|
||||
|
||||
class DatetimeBuiltins(object):
|
||||
|
|
|
@ -19,6 +19,7 @@ python-cinderclient>=1.3.1
|
|||
python-swiftclient>=2.2.0
|
||||
python-ironicclient>=0.8.0
|
||||
alembic>=0.8.0
|
||||
python-dateutil>=2.4.2
|
||||
python-glanceclient>=0.18.0
|
||||
Routes!=2.0,!=2.1,>=1.12.3;python_version=='2.7'
|
||||
Routes!=2.0,>=1.12.3;python_version!='2.7'
|
||||
|
|
|
@ -24,7 +24,6 @@ classifier =
|
|||
packages =
|
||||
congress
|
||||
antlr3
|
||||
thirdparty_dateutil
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
|
|
|
@ -1,259 +0,0 @@
|
|||
A. HISTORY OF THE SOFTWARE
|
||||
==========================
|
||||
|
||||
Python was created in the early 1990s by Guido van Rossum at Stichting
|
||||
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
||||
as a successor of a language called ABC. Guido remains Python's
|
||||
principal author, although it includes many contributions from others.
|
||||
|
||||
In 1995, Guido continued his work on Python at the Corporation for
|
||||
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
||||
in Reston, Virginia where he released several versions of the
|
||||
software.
|
||||
|
||||
In May 2000, Guido and the Python core development team moved to
|
||||
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
||||
year, the PythonLabs team moved to Digital Creations (now Zope
|
||||
Corporation, see http://www.zope.com). In 2001, the Python Software
|
||||
Foundation (PSF, see http://www.python.org/psf/) was formed, a
|
||||
non-profit organization created specifically to own Python-related
|
||||
Intellectual Property. Zope Corporation is a sponsoring member of
|
||||
the PSF.
|
||||
|
||||
All Python releases are Open Source (see http://www.opensource.org for
|
||||
the Open Source Definition). Historically, most, but not all, Python
|
||||
releases have also been GPL-compatible; the table below summarizes
|
||||
the various releases.
|
||||
|
||||
Release Derived Year Owner GPL-
|
||||
from compatible? (1)
|
||||
|
||||
0.9.0 thru 1.2 1991-1995 CWI yes
|
||||
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
||||
1.6 1.5.2 2000 CNRI no
|
||||
2.0 1.6 2000 BeOpen.com no
|
||||
1.6.1 1.6 2001 CNRI yes (2)
|
||||
2.1 2.0+1.6.1 2001 PSF no
|
||||
2.0.1 2.0+1.6.1 2001 PSF yes
|
||||
2.1.1 2.1+2.0.1 2001 PSF yes
|
||||
2.2 2.1.1 2001 PSF yes
|
||||
2.1.2 2.1.1 2002 PSF yes
|
||||
2.1.3 2.1.2 2002 PSF yes
|
||||
2.2.1 2.2 2002 PSF yes
|
||||
2.2.2 2.2.1 2002 PSF yes
|
||||
2.2.3 2.2.2 2003 PSF yes
|
||||
2.3 2.2.2 2002-2003 PSF yes
|
||||
|
||||
Footnotes:
|
||||
|
||||
(1) GPL-compatible doesn't mean that we're distributing Python under
|
||||
the GPL. All Python licenses, unlike the GPL, let you distribute
|
||||
a modified version without making your changes open source. The
|
||||
GPL-compatible licenses make it possible to combine Python with
|
||||
other software that is released under the GPL; the others don't.
|
||||
|
||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
||||
because its license has a choice of law clause. According to
|
||||
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
||||
is "not incompatible" with the GPL.
|
||||
|
||||
Thanks to the many outside volunteers who have worked under Guido's
|
||||
direction to make these releases possible.
|
||||
|
||||
|
||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||
===============================================================
|
||||
|
||||
PSF LICENSE AGREEMENT FOR PYTHON 2.3
|
||||
------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using Python 2.3 software in source or binary form and its
|
||||
associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python 2.3
|
||||
alone or in any derivative version, provided, however, that PSF's
|
||||
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
|
||||
2001, 2002, 2003 Python Software Foundation; All Rights Reserved" are
|
||||
retained in Python 2.3 alone or in any derivative version prepared by
|
||||
Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python 2.3 or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python 2.3.
|
||||
|
||||
4. PSF is making Python 2.3 available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.3 WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
2.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.3,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python 2.3, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
||||
-------------------------------------------
|
||||
|
||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
||||
|
||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
||||
Individual or Organization ("Licensee") accessing and otherwise using
|
||||
this software in source or binary form and its associated
|
||||
documentation ("the Software").
|
||||
|
||||
2. Subject to the terms and conditions of this BeOpen Python License
|
||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
||||
royalty-free, world-wide license to reproduce, analyze, test, perform
|
||||
and/or display publicly, prepare derivative works, distribute, and
|
||||
otherwise use the Software alone or in any derivative version,
|
||||
provided, however, that the BeOpen Python License is retained in the
|
||||
Software, alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
||||
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
5. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
6. This License Agreement shall be governed by and interpreted in all
|
||||
respects by the law of the State of California, excluding conflict of
|
||||
law provisions. Nothing in this License Agreement shall be deemed to
|
||||
create any relationship of agency, partnership, or joint venture
|
||||
between BeOpen and Licensee. This License Agreement does not grant
|
||||
permission to use BeOpen trademarks or trade names in a trademark
|
||||
sense to endorse or promote products or services of Licensee, or any
|
||||
third party. As an exception, the "BeOpen Python" logos available at
|
||||
http://www.pythonlabs.com/logos.html may be used according to the
|
||||
permissions granted on that web page.
|
||||
|
||||
7. By copying, installing or otherwise using the software, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
||||
---------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Corporation for National
|
||||
Research Initiatives, having an office at 1895 Preston White Drive,
|
||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
||||
source or binary form and its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, CNRI
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
||||
alone or in any derivative version, provided, however, that CNRI's
|
||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
||||
1995-2001 Corporation for National Research Initiatives; All Rights
|
||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
||||
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
||||
Agreement, Licensee may substitute the following text (omitting the
|
||||
quotes): "Python 1.6.1 is made available subject to the terms and
|
||||
conditions in CNRI's License Agreement. This Agreement together with
|
||||
Python 1.6.1 may be located on the Internet using the following
|
||||
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
||||
Agreement may also be obtained from a proxy server on the Internet
|
||||
using the following URL: http://hdl.handle.net/1895.22/1013".
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python 1.6.1.
|
||||
|
||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
||||
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. This License Agreement shall be governed by the federal
|
||||
intellectual property law of the United States, including without
|
||||
limitation the federal copyright law, and, to the extent such
|
||||
U.S. federal law does not apply, by the law of the Commonwealth of
|
||||
Virginia, excluding Virginia's conflict of law provisions.
|
||||
Notwithstanding the foregoing, with regard to derivative works based
|
||||
on Python 1.6.1 that incorporate non-separable material that was
|
||||
previously distributed under the GNU General Public License (GPL), the
|
||||
law of the Commonwealth of Virginia shall govern this License
|
||||
Agreement only as to issues arising under or with respect to
|
||||
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
||||
License Agreement shall be deemed to create any relationship of
|
||||
agency, partnership, or joint venture between CNRI and Licensee. This
|
||||
License Agreement does not grant permission to use CNRI trademarks or
|
||||
trade name in a trademark sense to endorse or promote products or
|
||||
services of Licensee, or any third party.
|
||||
|
||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
||||
bound by the terms and conditions of this License Agreement.
|
||||
|
||||
ACCEPT
|
||||
|
||||
|
||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
||||
--------------------------------------------------
|
||||
|
||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
||||
The Netherlands. All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted,
|
||||
provided that the above copyright notice appear in all copies and that
|
||||
both that copyright notice and this permission notice appear in
|
||||
supporting documentation, and that the name of Stichting Mathematisch
|
||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||
distribution of the software without specific, written prior
|
||||
permission.
|
||||
|
||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -1,9 +0,0 @@
|
|||
"""
|
||||
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net>
|
||||
|
||||
This module offers extensions to the standard python 2.3+
|
||||
datetime module.
|
||||
"""
|
||||
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
|
||||
__license__ = "PSF License"
|
||||
__version__ = "1.5"
|
|
@ -1,886 +0,0 @@
|
|||
# -*- coding:iso-8859-1 -*-
|
||||
"""
|
||||
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
|
||||
|
||||
This module offers extensions to the standard python 2.3+
|
||||
datetime module.
|
||||
"""
|
||||
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
|
||||
__license__ = "PSF License"
|
||||
|
||||
import datetime
|
||||
import string
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
import relativedelta
|
||||
#import tz
|
||||
|
||||
|
||||
__all__ = ["parse", "parserinfo"]
|
||||
|
||||
|
||||
# Some pointers:
|
||||
#
|
||||
# http://www.cl.cam.ac.uk/~mgk25/iso-time.html
|
||||
# http://www.iso.ch/iso/en/prods-services/popstds/datesandtime.html
|
||||
# http://www.w3.org/TR/NOTE-datetime
|
||||
# http://ringmaster.arc.nasa.gov/tools/time_formats.html
|
||||
# http://search.cpan.org/author/MUIR/Time-modules-2003.0211/lib/Time/ParseDate.pm
|
||||
# http://stein.cshl.org/jade/distrib/docs/java.text.SimpleDateFormat.html
|
||||
|
||||
|
||||
class _timelex(object):
|
||||
|
||||
def __init__(self, instream):
|
||||
if isinstance(instream, basestring):
|
||||
instream = StringIO(instream)
|
||||
self.instream = instream
|
||||
self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
|
||||
'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
|
||||
'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
|
||||
self.numchars = '0123456789'
|
||||
self.whitespace = ' \t\r\n'
|
||||
self.charstack = []
|
||||
self.tokenstack = []
|
||||
self.eof = False
|
||||
|
||||
def get_token(self):
|
||||
if self.tokenstack:
|
||||
return self.tokenstack.pop(0)
|
||||
seenletters = False
|
||||
token = None
|
||||
state = None
|
||||
wordchars = self.wordchars
|
||||
numchars = self.numchars
|
||||
whitespace = self.whitespace
|
||||
while not self.eof:
|
||||
if self.charstack:
|
||||
nextchar = self.charstack.pop(0)
|
||||
else:
|
||||
nextchar = self.instream.read(1)
|
||||
while nextchar == '\x00':
|
||||
nextchar = self.instream.read(1)
|
||||
if not nextchar:
|
||||
self.eof = True
|
||||
break
|
||||
elif not state:
|
||||
token = nextchar
|
||||
if nextchar in wordchars:
|
||||
state = 'a'
|
||||
elif nextchar in numchars:
|
||||
state = '0'
|
||||
elif nextchar in whitespace:
|
||||
token = ' '
|
||||
break # emit token
|
||||
else:
|
||||
break # emit token
|
||||
elif state == 'a':
|
||||
seenletters = True
|
||||
if nextchar in wordchars:
|
||||
token += nextchar
|
||||
elif nextchar == '.':
|
||||
token += nextchar
|
||||
state = 'a.'
|
||||
else:
|
||||
self.charstack.append(nextchar)
|
||||
break # emit token
|
||||
elif state == '0':
|
||||
if nextchar in numchars:
|
||||
token += nextchar
|
||||
elif nextchar == '.':
|
||||
token += nextchar
|
||||
state = '0.'
|
||||
else:
|
||||
self.charstack.append(nextchar)
|
||||
break # emit token
|
||||
elif state == 'a.':
|
||||
seenletters = True
|
||||
if nextchar == '.' or nextchar in wordchars:
|
||||
token += nextchar
|
||||
elif nextchar in numchars and token[-1] == '.':
|
||||
token += nextchar
|
||||
state = '0.'
|
||||
else:
|
||||
self.charstack.append(nextchar)
|
||||
break # emit token
|
||||
elif state == '0.':
|
||||
if nextchar == '.' or nextchar in numchars:
|
||||
token += nextchar
|
||||
elif nextchar in wordchars and token[-1] == '.':
|
||||
token += nextchar
|
||||
state = 'a.'
|
||||
else:
|
||||
self.charstack.append(nextchar)
|
||||
break # emit token
|
||||
if (state in ('a.', '0.') and
|
||||
(seenletters or token.count('.') > 1 or token[-1] == '.')):
|
||||
l = token.split('.')
|
||||
token = l[0]
|
||||
for tok in l[1:]:
|
||||
self.tokenstack.append('.')
|
||||
if tok:
|
||||
self.tokenstack.append(tok)
|
||||
return token
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
token = self.get_token()
|
||||
if token is None:
|
||||
raise StopIteration
|
||||
return token
|
||||
|
||||
def split(cls, s):
|
||||
return list(cls(s))
|
||||
split = classmethod(split)
|
||||
|
||||
|
||||
class _resultbase(object):
|
||||
|
||||
def __init__(self):
|
||||
for attr in self.__slots__:
|
||||
setattr(self, attr, None)
|
||||
|
||||
def _repr(self, classname):
|
||||
l = []
|
||||
for attr in self.__slots__:
|
||||
value = getattr(self, attr)
|
||||
if value is not None:
|
||||
l.append("%s=%s" % (attr, `value`))
|
||||
return "%s(%s)" % (classname, ", ".join(l))
|
||||
|
||||
def __repr__(self):
|
||||
return self._repr(self.__class__.__name__)
|
||||
|
||||
|
||||
class parserinfo(object):
|
||||
|
||||
# m from a.m/p.m, t from ISO T separator
|
||||
JUMP = [" ", ".", ",", ";", "-", "/", "'",
|
||||
"at", "on", "and", "ad", "m", "t", "of",
|
||||
"st", "nd", "rd", "th"]
|
||||
|
||||
WEEKDAYS = [("Mon", "Monday"),
|
||||
("Tue", "Tuesday"),
|
||||
("Wed", "Wednesday"),
|
||||
("Thu", "Thursday"),
|
||||
("Fri", "Friday"),
|
||||
("Sat", "Saturday"),
|
||||
("Sun", "Sunday")]
|
||||
MONTHS = [("Jan", "January"),
|
||||
("Feb", "February"),
|
||||
("Mar", "March"),
|
||||
("Apr", "April"),
|
||||
("May", "May"),
|
||||
("Jun", "June"),
|
||||
("Jul", "July"),
|
||||
("Aug", "August"),
|
||||
("Sep", "September"),
|
||||
("Oct", "October"),
|
||||
("Nov", "November"),
|
||||
("Dec", "December")]
|
||||
HMS = [("h", "hour", "hours"),
|
||||
("m", "minute", "minutes"),
|
||||
("s", "second", "seconds")]
|
||||
AMPM = [("am", "a"),
|
||||
("pm", "p")]
|
||||
UTCZONE = ["UTC", "GMT", "Z"]
|
||||
PERTAIN = ["of"]
|
||||
TZOFFSET = {}
|
||||
|
||||
def __init__(self, dayfirst=False, yearfirst=False):
|
||||
self._jump = self._convert(self.JUMP)
|
||||
self._weekdays = self._convert(self.WEEKDAYS)
|
||||
self._months = self._convert(self.MONTHS)
|
||||
self._hms = self._convert(self.HMS)
|
||||
self._ampm = self._convert(self.AMPM)
|
||||
self._utczone = self._convert(self.UTCZONE)
|
||||
self._pertain = self._convert(self.PERTAIN)
|
||||
|
||||
self.dayfirst = dayfirst
|
||||
self.yearfirst = yearfirst
|
||||
|
||||
self._year = time.localtime().tm_year
|
||||
self._century = self._year//100*100
|
||||
|
||||
def _convert(self, lst):
|
||||
dct = {}
|
||||
for i in range(len(lst)):
|
||||
v = lst[i]
|
||||
if isinstance(v, tuple):
|
||||
for v in v:
|
||||
dct[v.lower()] = i
|
||||
else:
|
||||
dct[v.lower()] = i
|
||||
return dct
|
||||
|
||||
def jump(self, name):
|
||||
return name.lower() in self._jump
|
||||
|
||||
def weekday(self, name):
|
||||
if len(name) >= 3:
|
||||
try:
|
||||
return self._weekdays[name.lower()]
|
||||
except KeyError:
|
||||
pass
|
||||
return None
|
||||
|
||||
def month(self, name):
|
||||
if len(name) >= 3:
|
||||
try:
|
||||
return self._months[name.lower()]+1
|
||||
except KeyError:
|
||||
pass
|
||||
return None
|
||||
|
||||
def hms(self, name):
|
||||
try:
|
||||
return self._hms[name.lower()]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def ampm(self, name):
|
||||
try:
|
||||
return self._ampm[name.lower()]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def pertain(self, name):
|
||||
return name.lower() in self._pertain
|
||||
|
||||
def utczone(self, name):
|
||||
return name.lower() in self._utczone
|
||||
|
||||
def tzoffset(self, name):
|
||||
if name in self._utczone:
|
||||
return 0
|
||||
return self.TZOFFSET.get(name)
|
||||
|
||||
def convertyear(self, year):
|
||||
if year < 100:
|
||||
year += self._century
|
||||
if abs(year-self._year) >= 50:
|
||||
if year < self._year:
|
||||
year += 100
|
||||
else:
|
||||
year -= 100
|
||||
return year
|
||||
|
||||
def validate(self, res):
|
||||
# move to info
|
||||
if res.year is not None:
|
||||
res.year = self.convertyear(res.year)
|
||||
if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z':
|
||||
res.tzname = "UTC"
|
||||
res.tzoffset = 0
|
||||
elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname):
|
||||
res.tzoffset = 0
|
||||
return True
|
||||
|
||||
|
||||
class parser(object):
|
||||
|
||||
def __init__(self, info=None):
|
||||
self.info = info or parserinfo()
|
||||
|
||||
def parse(self, timestr, default=None,
|
||||
ignoretz=False, tzinfos=None,
|
||||
**kwargs):
|
||||
if not default:
|
||||
default = datetime.datetime.now().replace(hour=0, minute=0,
|
||||
second=0, microsecond=0)
|
||||
res = self._parse(timestr, **kwargs)
|
||||
if res is None:
|
||||
raise ValueError, "unknown string format"
|
||||
repl = {}
|
||||
for attr in ["year", "month", "day", "hour",
|
||||
"minute", "second", "microsecond"]:
|
||||
value = getattr(res, attr)
|
||||
if value is not None:
|
||||
repl[attr] = value
|
||||
ret = default.replace(**repl)
|
||||
if res.weekday is not None and not res.day:
|
||||
ret = ret+relativedelta.relativedelta(weekday=res.weekday)
|
||||
if not ignoretz:
|
||||
if callable(tzinfos) or tzinfos and res.tzname in tzinfos:
|
||||
if callable(tzinfos):
|
||||
tzdata = tzinfos(res.tzname, res.tzoffset)
|
||||
else:
|
||||
tzdata = tzinfos.get(res.tzname)
|
||||
if isinstance(tzdata, datetime.tzinfo):
|
||||
tzinfo = tzdata
|
||||
elif isinstance(tzdata, basestring):
|
||||
tzinfo = tz.tzstr(tzdata)
|
||||
elif isinstance(tzdata, int):
|
||||
tzinfo = tz.tzoffset(res.tzname, tzdata)
|
||||
else:
|
||||
raise ValueError, "offset must be tzinfo subclass, " \
|
||||
"tz string, or int offset"
|
||||
ret = ret.replace(tzinfo=tzinfo)
|
||||
elif res.tzname and res.tzname in time.tzname:
|
||||
ret = ret.replace(tzinfo=tz.tzlocal())
|
||||
elif res.tzoffset == 0:
|
||||
ret = ret.replace(tzinfo=tz.tzutc())
|
||||
elif res.tzoffset:
|
||||
ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset))
|
||||
return ret
|
||||
|
||||
class _result(_resultbase):
|
||||
__slots__ = ["year", "month", "day", "weekday",
|
||||
"hour", "minute", "second", "microsecond",
|
||||
"tzname", "tzoffset"]
|
||||
|
||||
def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False):
|
||||
info = self.info
|
||||
if dayfirst is None:
|
||||
dayfirst = info.dayfirst
|
||||
if yearfirst is None:
|
||||
yearfirst = info.yearfirst
|
||||
res = self._result()
|
||||
l = _timelex.split(timestr)
|
||||
try:
|
||||
|
||||
# year/month/day list
|
||||
ymd = []
|
||||
|
||||
# Index of the month string in ymd
|
||||
mstridx = -1
|
||||
|
||||
len_l = len(l)
|
||||
i = 0
|
||||
while i < len_l:
|
||||
|
||||
# Check if it's a number
|
||||
try:
|
||||
value_repr = l[i]
|
||||
value = float(value_repr)
|
||||
except ValueError:
|
||||
value = None
|
||||
|
||||
if value is not None:
|
||||
# Token is a number
|
||||
len_li = len(l[i])
|
||||
i += 1
|
||||
if (len(ymd) == 3 and len_li in (2, 4)
|
||||
and (i >= len_l or (l[i] != ':' and
|
||||
info.hms(l[i]) is None))):
|
||||
# 19990101T23[59]
|
||||
s = l[i-1]
|
||||
res.hour = int(s[:2])
|
||||
if len_li == 4:
|
||||
res.minute = int(s[2:])
|
||||
elif len_li == 6 or (len_li > 6 and l[i-1].find('.') == 6):
|
||||
# YYMMDD or HHMMSS[.ss]
|
||||
s = l[i-1]
|
||||
if not ymd and l[i-1].find('.') == -1:
|
||||
ymd.append(info.convertyear(int(s[:2])))
|
||||
ymd.append(int(s[2:4]))
|
||||
ymd.append(int(s[4:]))
|
||||
else:
|
||||
# 19990101T235959[.59]
|
||||
res.hour = int(s[:2])
|
||||
res.minute = int(s[2:4])
|
||||
res.second, res.microsecond = _parsems(s[4:])
|
||||
elif len_li == 8:
|
||||
# YYYYMMDD
|
||||
s = l[i-1]
|
||||
ymd.append(int(s[:4]))
|
||||
ymd.append(int(s[4:6]))
|
||||
ymd.append(int(s[6:]))
|
||||
elif len_li in (12, 14):
|
||||
# YYYYMMDDhhmm[ss]
|
||||
s = l[i-1]
|
||||
ymd.append(int(s[:4]))
|
||||
ymd.append(int(s[4:6]))
|
||||
ymd.append(int(s[6:8]))
|
||||
res.hour = int(s[8:10])
|
||||
res.minute = int(s[10:12])
|
||||
if len_li == 14:
|
||||
res.second = int(s[12:])
|
||||
elif ((i < len_l and info.hms(l[i]) is not None) or
|
||||
(i+1 < len_l and l[i] == ' ' and
|
||||
info.hms(l[i+1]) is not None)):
|
||||
# HH[ ]h or MM[ ]m or SS[.ss][ ]s
|
||||
if l[i] == ' ':
|
||||
i += 1
|
||||
idx = info.hms(l[i])
|
||||
while True:
|
||||
if idx == 0:
|
||||
res.hour = int(value)
|
||||
if value%1:
|
||||
res.minute = int(60*(value%1))
|
||||
elif idx == 1:
|
||||
res.minute = int(value)
|
||||
if value%1:
|
||||
res.second = int(60*(value%1))
|
||||
elif idx == 2:
|
||||
res.second, res.microsecond = \
|
||||
_parsems(value_repr)
|
||||
i += 1
|
||||
if i >= len_l or idx == 2:
|
||||
break
|
||||
# 12h00
|
||||
try:
|
||||
value_repr = l[i]
|
||||
value = float(value_repr)
|
||||
except ValueError:
|
||||
break
|
||||
else:
|
||||
i += 1
|
||||
idx += 1
|
||||
if i < len_l:
|
||||
newidx = info.hms(l[i])
|
||||
if newidx is not None:
|
||||
idx = newidx
|
||||
elif i+1 < len_l and l[i] == ':':
|
||||
# HH:MM[:SS[.ss]]
|
||||
res.hour = int(value)
|
||||
i += 1
|
||||
value = float(l[i])
|
||||
res.minute = int(value)
|
||||
if value%1:
|
||||
res.second = int(60*(value%1))
|
||||
i += 1
|
||||
if i < len_l and l[i] == ':':
|
||||
res.second, res.microsecond = _parsems(l[i+1])
|
||||
i += 2
|
||||
elif i < len_l and l[i] in ('-', '/', '.'):
|
||||
sep = l[i]
|
||||
ymd.append(int(value))
|
||||
i += 1
|
||||
if i < len_l and not info.jump(l[i]):
|
||||
try:
|
||||
# 01-01[-01]
|
||||
ymd.append(int(l[i]))
|
||||
except ValueError:
|
||||
# 01-Jan[-01]
|
||||
value = info.month(l[i])
|
||||
if value is not None:
|
||||
ymd.append(value)
|
||||
assert mstridx == -1
|
||||
mstridx = len(ymd)-1
|
||||
else:
|
||||
return None
|
||||
i += 1
|
||||
if i < len_l and l[i] == sep:
|
||||
# We have three members
|
||||
i += 1
|
||||
value = info.month(l[i])
|
||||
if value is not None:
|
||||
ymd.append(value)
|
||||
mstridx = len(ymd)-1
|
||||
assert mstridx == -1
|
||||
else:
|
||||
ymd.append(int(l[i]))
|
||||
i += 1
|
||||
elif i >= len_l or info.jump(l[i]):
|
||||
if i+1 < len_l and info.ampm(l[i+1]) is not None:
|
||||
# 12 am
|
||||
res.hour = int(value)
|
||||
if res.hour < 12 and info.ampm(l[i+1]) == 1:
|
||||
res.hour += 12
|
||||
elif res.hour == 12 and info.ampm(l[i+1]) == 0:
|
||||
res.hour = 0
|
||||
i += 1
|
||||
else:
|
||||
# Year, month or day
|
||||
ymd.append(int(value))
|
||||
i += 1
|
||||
elif info.ampm(l[i]) is not None:
|
||||
# 12am
|
||||
res.hour = int(value)
|
||||
if res.hour < 12 and info.ampm(l[i]) == 1:
|
||||
res.hour += 12
|
||||
elif res.hour == 12 and info.ampm(l[i]) == 0:
|
||||
res.hour = 0
|
||||
i += 1
|
||||
elif not fuzzy:
|
||||
return None
|
||||
else:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Check weekday
|
||||
value = info.weekday(l[i])
|
||||
if value is not None:
|
||||
res.weekday = value
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Check month name
|
||||
value = info.month(l[i])
|
||||
if value is not None:
|
||||
ymd.append(value)
|
||||
assert mstridx == -1
|
||||
mstridx = len(ymd)-1
|
||||
i += 1
|
||||
if i < len_l:
|
||||
if l[i] in ('-', '/'):
|
||||
# Jan-01[-99]
|
||||
sep = l[i]
|
||||
i += 1
|
||||
ymd.append(int(l[i]))
|
||||
i += 1
|
||||
if i < len_l and l[i] == sep:
|
||||
# Jan-01-99
|
||||
i += 1
|
||||
ymd.append(int(l[i]))
|
||||
i += 1
|
||||
elif (i+3 < len_l and l[i] == l[i+2] == ' '
|
||||
and info.pertain(l[i+1])):
|
||||
# Jan of 01
|
||||
# In this case, 01 is clearly year
|
||||
try:
|
||||
value = int(l[i+3])
|
||||
except ValueError:
|
||||
# Wrong guess
|
||||
pass
|
||||
else:
|
||||
# Convert it here to become unambiguous
|
||||
ymd.append(info.convertyear(value))
|
||||
i += 4
|
||||
continue
|
||||
|
||||
# Check am/pm
|
||||
value = info.ampm(l[i])
|
||||
if value is not None:
|
||||
if value == 1 and res.hour < 12:
|
||||
res.hour += 12
|
||||
elif value == 0 and res.hour == 12:
|
||||
res.hour = 0
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Check for a timezone name
|
||||
if (res.hour is not None and len(l[i]) <= 5 and
|
||||
res.tzname is None and res.tzoffset is None and
|
||||
not [x for x in l[i] if x not in string.ascii_uppercase]):
|
||||
res.tzname = l[i]
|
||||
res.tzoffset = info.tzoffset(res.tzname)
|
||||
i += 1
|
||||
|
||||
# Check for something like GMT+3, or BRST+3. Notice
|
||||
# that it doesn't mean "I am 3 hours after GMT", but
|
||||
# "my time +3 is GMT". If found, we reverse the
|
||||
# logic so that timezone parsing code will get it
|
||||
# right.
|
||||
if i < len_l and l[i] in ('+', '-'):
|
||||
l[i] = ('+', '-')[l[i] == '+']
|
||||
res.tzoffset = None
|
||||
if info.utczone(res.tzname):
|
||||
# With something like GMT+3, the timezone
|
||||
# is *not* GMT.
|
||||
res.tzname = None
|
||||
|
||||
continue
|
||||
|
||||
# Check for a numbered timezone
|
||||
if res.hour is not None and l[i] in ('+', '-'):
|
||||
signal = (-1,1)[l[i] == '+']
|
||||
i += 1
|
||||
len_li = len(l[i])
|
||||
if len_li == 4:
|
||||
# -0300
|
||||
res.tzoffset = int(l[i][:2])*3600+int(l[i][2:])*60
|
||||
elif i+1 < len_l and l[i+1] == ':':
|
||||
# -03:00
|
||||
res.tzoffset = int(l[i])*3600+int(l[i+2])*60
|
||||
i += 2
|
||||
elif len_li <= 2:
|
||||
# -[0]3
|
||||
res.tzoffset = int(l[i][:2])*3600
|
||||
else:
|
||||
return None
|
||||
i += 1
|
||||
res.tzoffset *= signal
|
||||
|
||||
# Look for a timezone name between parenthesis
|
||||
if (i+3 < len_l and
|
||||
info.jump(l[i]) and l[i+1] == '(' and l[i+3] == ')' and
|
||||
3 <= len(l[i+2]) <= 5 and
|
||||
not [x for x in l[i+2]
|
||||
if x not in string.ascii_uppercase]):
|
||||
# -0300 (BRST)
|
||||
res.tzname = l[i+2]
|
||||
i += 4
|
||||
continue
|
||||
|
||||
# Check jumps
|
||||
if not (info.jump(l[i]) or fuzzy):
|
||||
return None
|
||||
|
||||
i += 1
|
||||
|
||||
# Process year/month/day
|
||||
len_ymd = len(ymd)
|
||||
if len_ymd > 3:
|
||||
# More than three members!?
|
||||
return None
|
||||
elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2):
|
||||
# One member, or two members with a month string
|
||||
if mstridx != -1:
|
||||
res.month = ymd[mstridx]
|
||||
del ymd[mstridx]
|
||||
if len_ymd > 1 or mstridx == -1:
|
||||
if ymd[0] > 31:
|
||||
res.year = ymd[0]
|
||||
else:
|
||||
res.day = ymd[0]
|
||||
elif len_ymd == 2:
|
||||
# Two members with numbers
|
||||
if ymd[0] > 31:
|
||||
# 99-01
|
||||
res.year, res.month = ymd
|
||||
elif ymd[1] > 31:
|
||||
# 01-99
|
||||
res.month, res.year = ymd
|
||||
elif dayfirst and ymd[1] <= 12:
|
||||
# 13-01
|
||||
res.day, res.month = ymd
|
||||
else:
|
||||
# 01-13
|
||||
res.month, res.day = ymd
|
||||
if len_ymd == 3:
|
||||
# Three members
|
||||
if mstridx == 0:
|
||||
res.month, res.day, res.year = ymd
|
||||
elif mstridx == 1:
|
||||
if ymd[0] > 31 or (yearfirst and ymd[2] <= 31):
|
||||
# 99-Jan-01
|
||||
res.year, res.month, res.day = ymd
|
||||
else:
|
||||
# 01-Jan-01
|
||||
# Give precendence to day-first, since
|
||||
# two-digit years is usually hand-written.
|
||||
res.day, res.month, res.year = ymd
|
||||
elif mstridx == 2:
|
||||
# WTF!?
|
||||
if ymd[1] > 31:
|
||||
# 01-99-Jan
|
||||
res.day, res.year, res.month = ymd
|
||||
else:
|
||||
# 99-01-Jan
|
||||
res.year, res.day, res.month = ymd
|
||||
else:
|
||||
if ymd[0] > 31 or \
|
||||
(yearfirst and ymd[1] <= 12 and ymd[2] <= 31):
|
||||
# 99-01-01
|
||||
res.year, res.month, res.day = ymd
|
||||
elif ymd[0] > 12 or (dayfirst and ymd[1] <= 12):
|
||||
# 13-01-01
|
||||
res.day, res.month, res.year = ymd
|
||||
else:
|
||||
# 01-13-01
|
||||
res.month, res.day, res.year = ymd
|
||||
|
||||
except (IndexError, ValueError, AssertionError):
|
||||
return None
|
||||
|
||||
if not info.validate(res):
|
||||
return None
|
||||
return res
|
||||
|
||||
DEFAULTPARSER = parser()
|
||||
def parse(timestr, parserinfo=None, **kwargs):
|
||||
if parserinfo:
|
||||
return parser(parserinfo).parse(timestr, **kwargs)
|
||||
else:
|
||||
return DEFAULTPARSER.parse(timestr, **kwargs)
|
||||
|
||||
|
||||
class _tzparser(object):
|
||||
|
||||
class _result(_resultbase):
|
||||
|
||||
__slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset",
|
||||
"start", "end"]
|
||||
|
||||
class _attr(_resultbase):
|
||||
__slots__ = ["month", "week", "weekday",
|
||||
"yday", "jyday", "day", "time"]
|
||||
|
||||
def __repr__(self):
|
||||
return self._repr("")
|
||||
|
||||
def __init__(self):
|
||||
_resultbase.__init__(self)
|
||||
self.start = self._attr()
|
||||
self.end = self._attr()
|
||||
|
||||
def parse(self, tzstr):
|
||||
res = self._result()
|
||||
l = _timelex.split(tzstr)
|
||||
try:
|
||||
|
||||
len_l = len(l)
|
||||
|
||||
i = 0
|
||||
while i < len_l:
|
||||
# BRST+3[BRDT[+2]]
|
||||
j = i
|
||||
while j < len_l and not [x for x in l[j]
|
||||
if x in "0123456789:,-+"]:
|
||||
j += 1
|
||||
if j != i:
|
||||
if not res.stdabbr:
|
||||
offattr = "stdoffset"
|
||||
res.stdabbr = "".join(l[i:j])
|
||||
else:
|
||||
offattr = "dstoffset"
|
||||
res.dstabbr = "".join(l[i:j])
|
||||
i = j
|
||||
if (i < len_l and
|
||||
(l[i] in ('+', '-') or l[i][0] in "0123456789")):
|
||||
if l[i] in ('+', '-'):
|
||||
# Yes, that's right. See the TZ variable
|
||||
# documentation.
|
||||
signal = (1,-1)[l[i] == '+']
|
||||
i += 1
|
||||
else:
|
||||
signal = -1
|
||||
len_li = len(l[i])
|
||||
if len_li == 4:
|
||||
# -0300
|
||||
setattr(res, offattr,
|
||||
(int(l[i][:2])*3600+int(l[i][2:])*60)*signal)
|
||||
elif i+1 < len_l and l[i+1] == ':':
|
||||
# -03:00
|
||||
setattr(res, offattr,
|
||||
(int(l[i])*3600+int(l[i+2])*60)*signal)
|
||||
i += 2
|
||||
elif len_li <= 2:
|
||||
# -[0]3
|
||||
setattr(res, offattr,
|
||||
int(l[i][:2])*3600*signal)
|
||||
else:
|
||||
return None
|
||||
i += 1
|
||||
if res.dstabbr:
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
if i < len_l:
|
||||
for j in range(i, len_l):
|
||||
if l[j] == ';': l[j] = ','
|
||||
|
||||
assert l[i] == ','
|
||||
|
||||
i += 1
|
||||
|
||||
if i >= len_l:
|
||||
pass
|
||||
elif (8 <= l.count(',') <= 9 and
|
||||
not [y for x in l[i:] if x != ','
|
||||
for y in x if y not in "0123456789"]):
|
||||
# GMT0BST,3,0,30,3600,10,0,26,7200[,3600]
|
||||
for x in (res.start, res.end):
|
||||
x.month = int(l[i])
|
||||
i += 2
|
||||
if l[i] == '-':
|
||||
value = int(l[i+1])*-1
|
||||
i += 1
|
||||
else:
|
||||
value = int(l[i])
|
||||
i += 2
|
||||
if value:
|
||||
x.week = value
|
||||
x.weekday = (int(l[i])-1)%7
|
||||
else:
|
||||
x.day = int(l[i])
|
||||
i += 2
|
||||
x.time = int(l[i])
|
||||
i += 2
|
||||
if i < len_l:
|
||||
if l[i] in ('-','+'):
|
||||
signal = (-1,1)[l[i] == "+"]
|
||||
i += 1
|
||||
else:
|
||||
signal = 1
|
||||
res.dstoffset = (res.stdoffset+int(l[i]))*signal
|
||||
elif (l.count(',') == 2 and l[i:].count('/') <= 2 and
|
||||
not [y for x in l[i:] if x not in (',','/','J','M',
|
||||
'.','-',':')
|
||||
for y in x if y not in "0123456789"]):
|
||||
for x in (res.start, res.end):
|
||||
if l[i] == 'J':
|
||||
# non-leap year day (1 based)
|
||||
i += 1
|
||||
x.jyday = int(l[i])
|
||||
elif l[i] == 'M':
|
||||
# month[-.]week[-.]weekday
|
||||
i += 1
|
||||
x.month = int(l[i])
|
||||
i += 1
|
||||
assert l[i] in ('-', '.')
|
||||
i += 1
|
||||
x.week = int(l[i])
|
||||
if x.week == 5:
|
||||
x.week = -1
|
||||
i += 1
|
||||
assert l[i] in ('-', '.')
|
||||
i += 1
|
||||
x.weekday = (int(l[i])-1)%7
|
||||
else:
|
||||
# year day (zero based)
|
||||
x.yday = int(l[i])+1
|
||||
|
||||
i += 1
|
||||
|
||||
if i < len_l and l[i] == '/':
|
||||
i += 1
|
||||
# start time
|
||||
len_li = len(l[i])
|
||||
if len_li == 4:
|
||||
# -0300
|
||||
x.time = (int(l[i][:2])*3600+int(l[i][2:])*60)
|
||||
elif i+1 < len_l and l[i+1] == ':':
|
||||
# -03:00
|
||||
x.time = int(l[i])*3600+int(l[i+2])*60
|
||||
i += 2
|
||||
if i+1 < len_l and l[i+1] == ':':
|
||||
i += 2
|
||||
x.time += int(l[i])
|
||||
elif len_li <= 2:
|
||||
# -[0]3
|
||||
x.time = (int(l[i][:2])*3600)
|
||||
else:
|
||||
return None
|
||||
i += 1
|
||||
|
||||
assert i == len_l or l[i] == ','
|
||||
|
||||
i += 1
|
||||
|
||||
assert i >= len_l
|
||||
|
||||
except (IndexError, ValueError, AssertionError):
|
||||
return None
|
||||
|
||||
return res
|
||||
|
||||
|
||||
DEFAULTTZPARSER = _tzparser()
|
||||
def _parsetz(tzstr):
|
||||
return DEFAULTTZPARSER.parse(tzstr)
|
||||
|
||||
|
||||
def _parsems(value):
|
||||
"""Parse a I[.F] seconds value into (seconds, microseconds)."""
|
||||
if "." not in value:
|
||||
return int(value), 0
|
||||
else:
|
||||
i, f = value.split(".")
|
||||
return int(i), int(f.ljust(6, "0")[:6])
|
||||
|
||||
|
||||
# vim:ts=4:sw=4:et
|
|
@ -1,432 +0,0 @@
|
|||
"""
|
||||
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net>
|
||||
|
||||
This module offers extensions to the standard python 2.3+
|
||||
datetime module.
|
||||
"""
|
||||
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
|
||||
__license__ = "PSF License"
|
||||
|
||||
import datetime
|
||||
import calendar
|
||||
|
||||
__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
|
||||
|
||||
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 __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)
|
||||
|
||||
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)])
|
||||
|
||||
class relativedelta:
|
||||
"""
|
||||
The relativedelta type is based on the specification of the excelent
|
||||
work done by M.-A. Lemburg in his mx.DateTime 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's two different ways to build a relativedelta instance. The
|
||||
first one is passing it two date/datetime classes:
|
||||
|
||||
relativedelta(datetime1, datetime2)
|
||||
|
||||
And the other way is to use the following keyword arguments:
|
||||
|
||||
year, month, day, hour, minute, second, microsecond:
|
||||
Absolute information.
|
||||
|
||||
years, months, weeks, days, hours, minutes, seconds, microseconds:
|
||||
Relative information, may be negative.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Here is the behavior of operations with relativedelta:
|
||||
|
||||
1) Calculate the absolute year, using the 'year' argument, or the
|
||||
original datetime year, if the argument is not present.
|
||||
|
||||
2) Add the relative 'years' argument to the absolute year.
|
||||
|
||||
3) Do steps 1 and 2 for month/months.
|
||||
|
||||
4) Calculate the absolute day, using the 'day' argument, or the
|
||||
original datetime day, if the argument is not present. Then,
|
||||
subtract from the day until it fits in the year and month
|
||||
found after their operations.
|
||||
|
||||
5) Add the relative 'days' argument to the absolute day. Notice
|
||||
that the 'weeks' argument is multiplied by 7 and added to
|
||||
'days'.
|
||||
|
||||
6) Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds,
|
||||
microsecond/microseconds.
|
||||
|
||||
7) If the 'weekday' argument is present, calculate the weekday,
|
||||
with the given (wday, nth) tuple. wday is the index of the
|
||||
weekday (0-6, 0=Mon), and nth is the number of weeks to add
|
||||
forward or backward, depending on its signal. Notice that if
|
||||
the calculated date is already Monday, for example, using
|
||||
(0, 1) or (0, -1) won't change the day.
|
||||
"""
|
||||
|
||||
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:
|
||||
if not isinstance(dt1, datetime.date) or \
|
||||
not isinstance(dt2, datetime.date):
|
||||
raise TypeError, "relativedelta only diffs datetime/date"
|
||||
if type(dt1) is not type(dt2):
|
||||
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
|
||||
|
||||
months = (dt1.year*12+dt1.month)-(dt2.year*12+dt2.month)
|
||||
self._set_months(months)
|
||||
dtm = self.__radd__(dt2)
|
||||
if dt1 < dt2:
|
||||
while dt1 > dtm:
|
||||
months += 1
|
||||
self._set_months(months)
|
||||
dtm = self.__radd__(dt2)
|
||||
else:
|
||||
while dt1 < dtm:
|
||||
months -= 1
|
||||
self._set_months(months)
|
||||
dtm = self.__radd__(dt2)
|
||||
delta = dt1 - dtm
|
||||
self.seconds = delta.seconds+delta.days*86400
|
||||
self.microseconds = delta.microseconds
|
||||
else:
|
||||
self.years = years
|
||||
self.months = months
|
||||
self.days = days+weeks*7
|
||||
self.leapdays = leapdays
|
||||
self.hours = hours
|
||||
self.minutes = minutes
|
||||
self.seconds = seconds
|
||||
self.microseconds = microseconds
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
self.hour = hour
|
||||
self.minute = minute
|
||||
self.second = second
|
||||
self.microsecond = microsecond
|
||||
|
||||
if type(weekday) is int:
|
||||
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 = self.microseconds//abs(self.microseconds)
|
||||
div, mod = divmod(self.microseconds*s, 1000000)
|
||||
self.microseconds = mod*s
|
||||
self.seconds += div*s
|
||||
if abs(self.seconds) > 59:
|
||||
s = self.seconds//abs(self.seconds)
|
||||
div, mod = divmod(self.seconds*s, 60)
|
||||
self.seconds = mod*s
|
||||
self.minutes += div*s
|
||||
if abs(self.minutes) > 59:
|
||||
s = self.minutes//abs(self.minutes)
|
||||
div, mod = divmod(self.minutes*s, 60)
|
||||
self.minutes = mod*s
|
||||
self.hours += div*s
|
||||
if abs(self.hours) > 23:
|
||||
s = self.hours//abs(self.hours)
|
||||
div, mod = divmod(self.hours*s, 24)
|
||||
self.hours = mod*s
|
||||
self.days += div*s
|
||||
if abs(self.months) > 11:
|
||||
s = self.months//abs(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
|
||||
|
||||
def _set_months(self, months):
|
||||
self.months = months
|
||||
if abs(self.months) > 11:
|
||||
s = self.months//abs(self.months)
|
||||
div, mod = divmod(self.months*s, 12)
|
||||
self.months = mod*s
|
||||
self.years = div*s
|
||||
else:
|
||||
self.years = 0
|
||||
|
||||
def __radd__(self, other):
|
||||
if not isinstance(other, datetime.date):
|
||||
raise TypeError, "unsupported type for add operation"
|
||||
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 __rsub__(self, other):
|
||||
return self.__neg__().__radd__(other)
|
||||
|
||||
def __add__(self, other):
|
||||
if not isinstance(other, relativedelta):
|
||||
raise TypeError, "unsupported type for add operation"
|
||||
return relativedelta(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 or self.year,
|
||||
month=other.month or self.month,
|
||||
day=other.day or self.day,
|
||||
weekday=other.weekday or self.weekday,
|
||||
hour=other.hour or self.hour,
|
||||
minute=other.minute or self.minute,
|
||||
second=other.second or self.second,
|
||||
microsecond=other.second or self.microsecond)
|
||||
|
||||
def __sub__(self, other):
|
||||
if not isinstance(other, relativedelta):
|
||||
raise TypeError, "unsupported type for sub operation"
|
||||
return relativedelta(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 or self.year,
|
||||
month=other.month or self.month,
|
||||
day=other.day or self.day,
|
||||
weekday=other.weekday or self.weekday,
|
||||
hour=other.hour or self.hour,
|
||||
minute=other.minute or self.minute,
|
||||
second=other.second or self.second,
|
||||
microsecond=other.second or self.microsecond)
|
||||
|
||||
def __neg__(self):
|
||||
return relativedelta(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 __nonzero__(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)
|
||||
|
||||
def __mul__(self, other):
|
||||
f = float(other)
|
||||
return relativedelta(years=self.years*f,
|
||||
months=self.months*f,
|
||||
days=self.days*f,
|
||||
hours=self.hours*f,
|
||||
minutes=self.minutes*f,
|
||||
seconds=self.seconds*f,
|
||||
microseconds=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)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, relativedelta):
|
||||
return False
|
||||
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.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 __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __div__(self, other):
|
||||
return self.__mul__(1/float(other))
|
||||
|
||||
def __repr__(self):
|
||||
l = []
|
||||
for attr in ["years", "months", "days", "leapdays",
|
||||
"hours", "minutes", "seconds", "microseconds"]:
|
||||
value = getattr(self, attr)
|
||||
if value:
|
||||
l.append("%s=%+d" % (attr, value))
|
||||
for attr in ["year", "month", "day", "weekday",
|
||||
"hour", "minute", "second", "microsecond"]:
|
||||
value = getattr(self, attr)
|
||||
if value is not None:
|
||||
l.append("%s=%s" % (attr, `value`))
|
||||
return "%s(%s)" % (self.__class__.__name__, ", ".join(l))
|
||||
|
||||
# vim:ts=4:sw=4:et
|
|
@ -1 +0,0 @@
|
|||
thirdparty/dateutil/
|
Loading…
Reference in New Issue