Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

added python dateutil for date parsing

  • Loading branch information...
commit 8d6bde61cca615dcc5645d895b73d046f21bfc9c 1 parent 210e230
@fwenzel fwenzel authored
Showing with 10,463 additions and 0 deletions.
  1. +1 −0  mdn.pth
  2. +259 −0 packages/python-dateutil/LICENSE
  3. +4 −0 packages/python-dateutil/MANIFEST.in
  4. +25 −0 packages/python-dateutil/Makefile
  5. +143 −0 packages/python-dateutil/NEWS
  6. +12 −0 packages/python-dateutil/PKG-INFO
  7. +1,970 −0 packages/python-dateutil/README
  8. +9 −0 packages/python-dateutil/dateutil/__init__.py
  9. +92 −0 packages/python-dateutil/dateutil/easter.py
  10. +886 −0 packages/python-dateutil/dateutil/parser.py
  11. +432 −0 packages/python-dateutil/dateutil/relativedelta.py
  12. +1,097 −0 packages/python-dateutil/dateutil/rrule.py
  13. +951 −0 packages/python-dateutil/dateutil/tz.py
  14. +180 −0 packages/python-dateutil/dateutil/tzwin.py
  15. +87 −0 packages/python-dateutil/dateutil/zoneinfo/__init__.py
  16. BIN  packages/python-dateutil/dateutil/zoneinfo/zoneinfo-2010g.tar.gz
  17. +15 −0 packages/python-dateutil/example.py
  18. +5 −0 packages/python-dateutil/pip-delete-this-directory.txt
  19. +12 −0 packages/python-dateutil/pip-egg-info/python_dateutil.egg-info/PKG-INFO
  20. +25 −0 packages/python-dateutil/pip-egg-info/python_dateutil.egg-info/SOURCES.txt
  21. +1 −0  packages/python-dateutil/pip-egg-info/python_dateutil.egg-info/dependency_links.txt
  22. +1 −0  packages/python-dateutil/pip-egg-info/python_dateutil.egg-info/not-zip-safe
  23. +1 −0  packages/python-dateutil/pip-egg-info/python_dateutil.egg-info/top_level.txt
  24. +12 −0 packages/python-dateutil/python_dateutil.egg-info/PKG-INFO
  25. +26 −0 packages/python-dateutil/python_dateutil.egg-info/SOURCES.txt
  26. +1 −0  packages/python-dateutil/python_dateutil.egg-info/dependency_links.txt
  27. +1 −0  packages/python-dateutil/python_dateutil.egg-info/not-zip-safe
  28. +1 −0  packages/python-dateutil/python_dateutil.egg-info/top_level.txt
  29. +16 −0 packages/python-dateutil/sandbox/rrulewrapper.py
  30. +157 −0 packages/python-dateutil/sandbox/scheduler.py
  31. +8 −0 packages/python-dateutil/setup.cfg
  32. +35 −0 packages/python-dateutil/setup.py
  33. +3,953 −0 packages/python-dateutil/test.py
  34. +45 −0 packages/python-dateutil/updatezinfo.py
View
1  mdn.pth
@@ -5,6 +5,7 @@ packages/GitPython/lib
packages/jinja2
packages/jsonpickle
packages/lockfile
+packages/python-dateutil
packages/python-memcached
packages/pytz
src/commonware
View
259 packages/python-dateutil/LICENSE
@@ -0,0 +1,259 @@
+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.
View
4 packages/python-dateutil/MANIFEST.in
@@ -0,0 +1,4 @@
+recursive-include dateutil *.py *.tar.*
+recursive-include sandbox *.py
+include setup.py setup.cfg MANIFEST.in README LICENSE NEWS Makefile
+include test.py example.py
View
25 packages/python-dateutil/Makefile
@@ -0,0 +1,25 @@
+#
+# Simple wrapper for setup.py script
+#
+
+DESTDIR=/
+PYTHON=python
+
+prefix=/usr
+bindir=$(prefix)/bin
+
+all:
+ $(PYTHON) setup.py build
+
+install:
+ $(PYTHON) setup.py install \
+ --root=$(DESTDIR) \
+ --prefix=$(prefix) \
+ --install-scripts=$(bindir)
+
+dist:
+ $(PYTHON) setup.py sdist
+
+rpm:
+ $(PYTHON) setup.py bdist_rpm
+
View
143 packages/python-dateutil/NEWS
@@ -0,0 +1,143 @@
+
+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 [1]. 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.
+
+[1] http://www.twinsun.com/tz/tz-link.htm
+
+
+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.
+
View
12 packages/python-dateutil/PKG-INFO
@@ -0,0 +1,12 @@
+Metadata-Version: 1.0
+Name: python-dateutil
+Version: 1.5
+Summary: Extensions to the standard python 2.3+ datetime module
+Home-page: http://labix.org/python-dateutil
+Author: Gustavo Niemeyer
+Author-email: gustavo@niemeyer.net
+License: PSF License
+Description: The dateutil module provides powerful extensions to the standard
+ datetime module, available in Python 2.3+.
+
+Platform: UNKNOWN
View
1,970 packages/python-dateutil/README
@@ -0,0 +1,1970 @@
+## This file is in the moin format. The latest version is found
+## at https://moin.conectiva.com.br/DateUtil
+
+== Contents ==
+[[TableOfContents]]
+
+== Description ==
+The '''dateutil''' module provides powerful extensions to
+the standard '''datetime''' module, available in Python 2.3+.
+
+== 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
+ [ftp://ftp.rfc-editor.org/in-notes/rfc2445.txt iCalendar]
+ 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;
+
+ * More than 400 test cases.
+
+== Quick example ==
+Here's a snapshot, just to give an idea about the power of the
+package. For more examples, look at the documentation below.
+
+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:
+{{{
+from dateutil.relativedelta import *
+from dateutil.easter import *
+from dateutil.rrule import *
+from dateutil.parser import *
+from datetime import *
+import commands
+import os
+now = parse(commands.getoutput("date"))
+today = now.date()
+year = rrule(YEARLY,bymonth=8,bymonthday=13,byweekday=FR)[0].year
+rdelta = relativedelta(easter(year), today)
+print "Today is:", today
+print "Year with next Aug 13th on a Friday is:", year
+print "How far is the Easter of that year:", rdelta
+print "And the Easter of that year is:", today+rdelta
+}}}
+
+And here's the output:
+{{{
+Today is: 2003-10-11
+Year with next Aug 13th on a Friday is: 2004
+How far is the Easter of that year: relativedelta(months=+6)
+And the Easter of that year is: 2004-04-11
+}}}
+
+{i} Being exactly 6 months ahead was '''really''' a coincidence :)
+
+== Download ==
+The following files are available.
+ * attachment:python-dateutil-1.0.tar.bz2
+ * attachment:python-dateutil-1.0-1.noarch.rpm
+
+== Author ==
+The dateutil module was written by GustavoNiemeyer <gustavo@niemeyer.net>.
+
+== Documentation ==
+The following modules are available.
+
+=== relativedelta ===
+This module offers the '''relativedelta''' type, which is based
+on the specification of the excelent work done by M.-A. Lemburg in his
+[http://www.egenix.com/files/python/mxDateTime.html mxDateTime]
+extension. However, notice that this type '''does not''' implement the
+same algorithm as his work. Do not expect it to behave like
+{{{mxDateTime}}}'s counterpart.
+
+==== relativedelta type ====
+
+There's two different ways to build a relativedelta instance. The
+first one is passing it two {{{date}}}/{{{datetime}}} instances:
+{{{
+relativedelta(datetime1, datetime2)
+}}}
+
+This will build the relative difference between {{{datetime1}}} and
+{{{datetime2}}}, so that the following constraint is always true:
+{{{
+datetime2+relativedelta(datetime1, datetime2) == datetime1
+}}}
+
+Notice that instead of {{{datetime}}} instances, you may use
+{{{date}}} instances, or a mix of both.
+
+And the other way is to use any of 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 {{{n}}}th
+ weekday, which could be positive or negative (like {{{MO(+2)}}} or
+ {{{MO(-3)}}}. Not specifying it is the same as specifying {{{+1}}}.
+ You can also use an integer, where {{{0=MO}}}. Notice that,
+ for example, if the calculated date is already Monday, using
+ {{{MO}}} or {{{MO(+1)}}} (which is the same thing in this context),
+ won't change the day.
+
+ leapdays::
+ Will add given days to the date found, but only if the computed
+ year is a leap year and the computed date 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.
+
+==== Behavior of operations ====
+If you're curious about exactly how the relative delta will act
+on operations, here is a description of its behavior.
+
+ 1. Calculate the absolute year, using the {{{year}}} argument, or the
+ original datetime year, if the argument is not present.
+ 1. Add the relative {{{years}}} argument to the absolute year.
+ 1. Do steps 1 and 2 for {{{month}}}/{{{months}}}.
+ 1. 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.
+ 1. Add the relative {{{days}}} argument to the absolute day. Notice
+ that the {{{weeks}}} argument is multiplied by 7 and added to {{{days}}}.
+ 1. If {{{leapdays}}} is present, the computed year is a leap year, and
+ the computed month is after february, remove one day from the found date.
+ 1. Do steps 1 and 2 for {{{hour}}}/{{{hours}}}, {{{minute}}}/{{{minutes}}},
+ {{{second}}}/{{{seconds}}}, {{{microsecond}}}/{{{microseconds}}}.
+ 1. If the {{{weekday}}} argument is present, calculate the {{{n}}}th
+ occurrence of the given weekday.
+
+==== Examples ====
+
+Let's begin our trip.
+{{{
+>>> from datetime import *; from dateutil.relativedelta import *
+>>> import calendar
+}}}
+
+Store some values.
+{{{
+>>> NOW = datetime.now()
+>>> TODAY = date.today()
+>>> NOW
+datetime.datetime(2003, 9, 17, 20, 54, 47, 282310)
+>>> TODAY
+datetime.date(2003, 9, 17)
+}}}
+
+Next month.
+{{{
+>>> NOW+relativedelta(months=+1)
+datetime.datetime(2003, 10, 17, 20, 54, 47, 282310)
+}}}
+
+Next month, plus one week.
+{{{
+>>> NOW+relativedelta(months=+1, weeks=+1)
+datetime.datetime(2003, 10, 24, 20, 54, 47, 282310)
+}}}
+
+Next month, plus one week, at 10am.
+{{{
+>>> TODAY+relativedelta(months=+1, weeks=+1, hour=10)
+datetime.datetime(2003, 10, 24, 10, 0)
+}}}
+
+Let's try the other way around. Notice that the
+hour setting we get in the relativedelta is relative,
+since it's a difference, and the weeks parameter
+has gone.
+{{{
+>>> relativedelta(datetime(2003, 10, 24, 10, 0), TODAY)
+relativedelta(months=+1, days=+7, hours=+10)
+}}}
+
+One month before one year.
+{{{
+>>> NOW+relativedelta(years=+1, months=-1)
+datetime.datetime(2004, 8, 17, 20, 54, 47, 282310)
+}}}
+
+How does it handle months with different numbers of days?
+Notice that adding one month will never cross the month
+boundary.
+{{{
+>>> date(2003,1,27)+relativedelta(months=+1)
+datetime.date(2003, 2, 27)
+>>> date(2003,1,31)+relativedelta(months=+1)
+datetime.date(2003, 2, 28)
+>>> date(2003,1,31)+relativedelta(months=+2)
+datetime.date(2003, 3, 31)
+}}}
+
+The logic for years is the same, even on leap years.
+{{{
+>>> date(2000,2,28)+relativedelta(years=+1)
+datetime.date(2001, 2, 28)
+>>> date(2000,2,29)+relativedelta(years=+1)
+datetime.date(2001, 2, 28)
+
+>>> date(1999,2,28)+relativedelta(years=+1)
+datetime.date(2000, 2, 28)
+>>> date(1999,3,1)+relativedelta(years=+1)
+datetime.date(2000, 3, 1)
+
+>>> date(2001,2,28)+relativedelta(years=-1)
+datetime.date(2000, 2, 28)
+>>> date(2001,3,1)+relativedelta(years=-1)
+datetime.date(2000, 3, 1)
+}}}
+
+Next friday.
+{{{
+>>> TODAY+relativedelta(weekday=FR)
+datetime.date(2003, 9, 19)
+
+>>> TODAY+relativedelta(weekday=calendar.FRIDAY)
+datetime.date(2003, 9, 19)
+}}}
+
+Last friday in this month.
+{{{
+>>> TODAY+relativedelta(day=31, weekday=FR(-1))
+datetime.date(2003, 9, 26)
+}}}
+
+Next wednesday (it's today!).
+{{{
+>>> TODAY+relativedelta(weekday=WE(+1))
+datetime.date(2003, 9, 17)
+}}}
+
+Next wednesday, but not today.
+{{{
+>>> TODAY+relativedelta(days=+1, weekday=WE(+1))
+datetime.date(2003, 9, 24)
+}}}
+
+Following
+[http://www.cl.cam.ac.uk/~mgk25/iso-time.html ISO year week number notation]
+find the first day of the 15th week of 1997.
+{{{
+>>> datetime(1997,1,1)+relativedelta(day=4, weekday=MO(-1), weeks=+14)
+datetime.datetime(1997, 4, 7, 0, 0)
+}}}
+
+How long ago has the millennium changed?
+{{{
+>>> relativedelta(NOW, date(2001,1,1))
+relativedelta(years=+2, months=+8, days=+16,
+ hours=+20, minutes=+54, seconds=+47, microseconds=+282310)
+}}}
+
+How old is John?
+{{{
+>>> johnbirthday = datetime(1978, 4, 5, 12, 0)
+>>> relativedelta(NOW, johnbirthday)
+relativedelta(years=+25, months=+5, days=+12,
+ hours=+8, minutes=+54, seconds=+47, microseconds=+282310)
+}}}
+
+It works with dates too.
+{{{
+>>> relativedelta(TODAY, johnbirthday)
+relativedelta(years=+25, months=+5, days=+11, hours=+12)
+}}}
+
+Obtain today's date using the yearday:
+{{{
+>>> date(2003, 1, 1)+relativedelta(yearday=260)
+datetime.date(2003, 9, 17)
+}}}
+
+We can use today's date, since yearday should be absolute
+in the given year:
+{{{
+>>> TODAY+relativedelta(yearday=260)
+datetime.date(2003, 9, 17)
+}}}
+
+Last year it should be in the same day:
+{{{
+>>> date(2002, 1, 1)+relativedelta(yearday=260)
+datetime.date(2002, 9, 17)
+}}}
+
+But not in a leap year:
+{{{
+>>> date(2000, 1, 1)+relativedelta(yearday=260)
+datetime.date(2000, 9, 16)
+}}}
+
+We can use the non-leap year day to ignore this:
+{{{
+>>> date(2000, 1, 1)+relativedelta(nlyearday=260)
+datetime.date(2000, 9, 17)
+}}}
+
+=== rrule ===
+The rrule module offers a small, complete, and very fast, implementation
+of the recurrence rules documented in the
+[ftp://ftp.rfc-editor.org/in-notes/rfc2445.txt iCalendar RFC], including
+support for caching of results.
+
+==== rrule type ====
+That's the base of the rrule operation. It accepts all the keywords
+defined in the RFC as its constructor parameters (except {{{byday}}},
+which was renamed to {{{byweekday}}}) and more. The constructor
+prototype is:
+{{{
+rrule(freq)
+}}}
+
+Where {{{freq}}} must be one of {{{YEARLY}}}, {{{MONTHLY}}},
+{{{WEEKLY}}}, {{{DAILY}}}, {{{HOURLY}}}, {{{MINUTELY}}},
+or {{{SECONDLY}}}.
+
+Additionally, it supports the following keyword arguments:
+
+ cache::
+ If given, it must be a boolean value specifying to enable
+ or disable caching of results. If you will use the same
+ {{{rrule}}} instance multiple times, enabling caching will
+ improve the performance considerably.
+
+ dtstart::
+ The recurrence start. Besides being the base for the
+ recurrence, missing parameters in the final recurrence
+ instances will also be extracted from this date. If not
+ given, {{{datetime.now()}}} will be used instead.
+
+ interval::
+ The interval between each {{{freq}}} iteration. For example,
+ when using {{{YEARLY}}}, an interval of {{{2}}} means
+ once every two years, but with {{{HOURLY}}}, it means
+ once every two hours. The default interval is {{{1}}}.
+
+ wkst::
+ The week start day. Must be one of the {{{MO}}}, {{{TU}}},
+ {{{WE}}} constants, or an integer, specifying the first day
+ of the week. This will affect recurrences based on weekly
+ periods. The default week start is got from
+ {{{calendar.firstweekday()}}}, and may be modified by
+ {{{calendar.setfirstweekday()}}}.
+
+ count::
+ How many occurrences will be generated.
+
+ until::
+ If given, this must be a {{{datetime}}} instance, that will
+ specify the limit of the recurrence. If a recurrence instance
+ happens to be the same as the {{{datetime}}} instance given
+ in the {{{until}}} keyword, this will be the last occurrence.
+
+ bysetpos::
+ If given, it must be either an integer, or a sequence of
+ integers, positive or negative. Each given integer will
+ specify an occurrence number, corresponding to the nth
+ occurrence of the rule inside the frequency period. For
+ example, a {{{bysetpos}}} of {{{-1}}} if combined with a
+ {{{MONTHLY}}} frequency, and a {{{byweekday}}} of
+ {{{(MO, TU, WE, TH, FR)}}}, will result in the last work
+ day of every month.
+
+ bymonth::
+ If given, it must be either an integer, or a sequence of
+ integers, meaning the months to apply the recurrence to.
+
+ bymonthday::
+ If given, it must be either an integer, or a sequence of
+ integers, meaning the month days to apply the recurrence to.
+
+ byyearday::
+ If given, it must be either an integer, or a sequence of
+ integers, meaning the year days to apply the recurrence to.
+
+ byweekno::
+ If given, it must be either an integer, or a sequence of
+ integers, meaning the week numbers to apply the recurrence
+ to. Week numbers have the meaning described in ISO8601,
+ that is, the first week of the year is that containing at
+ least four days of the new year.
+
+ byweekday::
+ If given, it must be either an integer ({{{0 == MO}}}), a
+ sequence of integers, one of the weekday constants
+ ({{{MO}}}, {{{TU}}}, etc), or a sequence of these constants.
+ When given, these variables will define the weekdays where
+ the recurrence will be applied. It's also possible to use
+ an argument {{{n}}} for the weekday instances, which will
+ mean the {{{n}}}''th'' occurrence of this weekday in the
+ period. For example, with {{{MONTHLY}}}, or with
+ {{{YEARLY}}} and {{{BYMONTH}}}, using {{{FR(+1)}}}
+ in {{{byweekday}}} will specify the first friday of the
+ month where the recurrence happens. Notice that in the RFC
+ documentation, this is specified as {{{BYDAY}}}, but was
+ renamed to avoid the ambiguity of that keyword.
+
+ byhour::
+ If given, it must be either an integer, or a sequence of
+ integers, meaning the hours to apply the recurrence to.
+
+ byminute::
+ If given, it must be either an integer, or a sequence of
+ integers, meaning the minutes to apply the recurrence to.
+
+ bysecond::
+ If given, it must be either an integer, or a sequence of
+ integers, meaning the seconds to apply the recurrence to.
+
+ byeaster::
+ If given, it must be either an integer, or a sequence of
+ integers, positive or negative. Each integer will define
+ an offset from the Easter Sunday. Passing the offset
+ {{{0}}} to {{{byeaster}}} will yield the Easter Sunday
+ itself. This is an extension to the RFC specification.
+
+==== rrule methods ====
+The following methods are available in {{{rrule}}} instances:
+
+ rrule.before(dt, inc=False)::
+ Returns the last recurrence before the given {{{datetime}}}
+ instance. The {{{inc}}} keyword defines what happens if
+ {{{dt}}} '''is''' an occurrence. With {{{inc == True}}},
+ if {{{dt}}} itself is an occurrence, it will be returned.
+
+ rrule.after(dt, inc=False)::
+ Returns the first recurrence after the given {{{datetime}}}
+ instance. The {{{inc}}} keyword defines what happens if
+ {{{dt}}} '''is''' an occurrence. With {{{inc == True}}},
+ if {{{dt}}} itself is an occurrence, it will be returned.
+
+ rrule.between(after, before, inc=False)::
+ Returns all the occurrences of the rrule between {{{after}}}
+ and {{{before}}}. The {{{inc}}} keyword defines what happens
+ if {{{after}}} and/or {{{before}}} are themselves occurrences.
+ With {{{inc == True}}}, they will be included in the list,
+ if they are found in the recurrence set.
+
+ rrule.count()::
+ Returns the number of recurrences in this set. It will have
+ go trough the whole recurrence, if this hasn't been done
+ before.
+
+Besides these methods, {{{rrule}}} instances also support
+the {{{__getitem__()}}} and {{{__contains__()}}} special methods,
+meaning that these are valid expressions:
+{{{
+rr = rrule(...)
+if datetime(...) in rr:
+ ...
+print rr[0]
+print rr[-1]
+print rr[1:2]
+print rr[::-2]
+}}}
+
+The getitem/slicing mechanism is smart enough to avoid getting the whole
+recurrence set, if possible.
+
+==== Notes ====
+
+ * The rrule type has no {{{byday}}} keyword. The equivalent keyword
+ has been replaced by the {{{byweekday}}} keyword, to remove the
+ ambiguity present in the original keyword.
+
+ * Unlike documented in the RFC, the starting datetime ({{{dtstart}}})
+ is not the first recurrence instance, unless it does fit in the
+ specified rules. In a python module context, this behavior makes more
+ sense than otherwise. Notice that you can easily get the original
+ behavior by using a rruleset and adding the {{{dtstart}}} as an
+ {{{rdate}}} recurrence.
+
+ * Unlike documented in the RFC, every keyword is valid on every
+ frequency (the RFC documents that {{{byweekno}}} is only valid
+ on yearly frequencies, for example).
+
+ * In addition to the documented keywords, a {{{byeaster}}} keyword
+ was introduced, making it easy to compute recurrent events relative
+ to the Easter Sunday.
+
+==== rrule examples ====
+These examples were converted from the RFC.
+
+Prepare the environment.
+{{{
+>>> from dateutil.rrule import *
+>>> from dateutil.parser import *
+>>> from datetime import *
+
+>>> import pprint
+>>> import sys
+>>> sys.displayhook = pprint.pprint
+}}}
+
+Daily, for 10 occurrences.
+{{{
+>>> list(rrule(DAILY, count=10,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 3, 9, 0),
+ datetime.datetime(1997, 9, 4, 9, 0),
+ datetime.datetime(1997, 9, 5, 9, 0),
+ datetime.datetime(1997, 9, 6, 9, 0),
+ datetime.datetime(1997, 9, 7, 9, 0),
+ datetime.datetime(1997, 9, 8, 9, 0),
+ datetime.datetime(1997, 9, 9, 9, 0),
+ datetime.datetime(1997, 9, 10, 9, 0),
+ datetime.datetime(1997, 9, 11, 9, 0)]
+}}}
+
+Daily until December 24, 1997
+{{{
+>>> list(rrule(DAILY,
+ dtstart=parse("19970902T090000"),
+ until=parse("19971224T000000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 3, 9, 0),
+ datetime.datetime(1997, 9, 4, 9, 0),
+ (...)
+ datetime.datetime(1997, 12, 21, 9, 0),
+ datetime.datetime(1997, 12, 22, 9, 0),
+ datetime.datetime(1997, 12, 23, 9, 0)]
+}}}
+
+Every other day, 5 occurrences.
+{{{
+>>> list(rrule(DAILY, interval=2, count=5,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 4, 9, 0),
+ datetime.datetime(1997, 9, 6, 9, 0),
+ datetime.datetime(1997, 9, 8, 9, 0),
+ datetime.datetime(1997, 9, 10, 9, 0)]
+}}}
+
+Every 10 days, 5 occurrences.
+{{{
+>>> list(rrule(DAILY, interval=10, count=5,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 12, 9, 0),
+ datetime.datetime(1997, 9, 22, 9, 0),
+ datetime.datetime(1997, 10, 2, 9, 0),
+ datetime.datetime(1997, 10, 12, 9, 0)]
+}}}
+
+Everyday in January, for 3 years.
+{{{
+>>> list(rrule(YEARLY, bymonth=1, byweekday=range(7),
+ dtstart=parse("19980101T090000"),
+ until=parse("20000131T090000")))
+[datetime.datetime(1998, 1, 1, 9, 0),
+ datetime.datetime(1998, 1, 2, 9, 0),
+ (...)
+ datetime.datetime(1998, 1, 30, 9, 0),
+ datetime.datetime(1998, 1, 31, 9, 0),
+ datetime.datetime(1999, 1, 1, 9, 0),
+ datetime.datetime(1999, 1, 2, 9, 0),
+ (...)
+ datetime.datetime(1999, 1, 30, 9, 0),
+ datetime.datetime(1999, 1, 31, 9, 0),
+ datetime.datetime(2000, 1, 1, 9, 0),
+ datetime.datetime(2000, 1, 2, 9, 0),
+ (...)
+ datetime.datetime(2000, 1, 29, 9, 0),
+ datetime.datetime(2000, 1, 31, 9, 0)]
+}}}
+
+Same thing, in another way.
+{{{
+>>> list(rrule(DAILY, bymonth=1,
+ dtstart=parse("19980101T090000"),
+ until=parse("20000131T090000")))
+(...)
+}}}
+
+Weekly for 10 occurrences.
+{{{
+>>> list(rrule(WEEKLY, count=10,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 9, 9, 0),
+ datetime.datetime(1997, 9, 16, 9, 0),
+ datetime.datetime(1997, 9, 23, 9, 0),
+ datetime.datetime(1997, 9, 30, 9, 0),
+ datetime.datetime(1997, 10, 7, 9, 0),
+ datetime.datetime(1997, 10, 14, 9, 0),
+ datetime.datetime(1997, 10, 21, 9, 0),
+ datetime.datetime(1997, 10, 28, 9, 0),
+ datetime.datetime(1997, 11, 4, 9, 0)]
+}}}
+
+Every other week, 6 occurrences.
+{{{
+>>> list(rrule(WEEKLY, interval=2, count=6,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 16, 9, 0),
+ datetime.datetime(1997, 9, 30, 9, 0),
+ datetime.datetime(1997, 10, 14, 9, 0),
+ datetime.datetime(1997, 10, 28, 9, 0),
+ datetime.datetime(1997, 11, 11, 9, 0)]
+}}}
+
+Weekly on Tuesday and Thursday for 5 weeks.
+{{{
+>>> list(rrule(WEEKLY, count=10, wkst=SU, byweekday=(TU,TH),
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 4, 9, 0),
+ datetime.datetime(1997, 9, 9, 9, 0),
+ datetime.datetime(1997, 9, 11, 9, 0),
+ datetime.datetime(1997, 9, 16, 9, 0),
+ datetime.datetime(1997, 9, 18, 9, 0),
+ datetime.datetime(1997, 9, 23, 9, 0),
+ datetime.datetime(1997, 9, 25, 9, 0),
+ datetime.datetime(1997, 9, 30, 9, 0),
+ datetime.datetime(1997, 10, 2, 9, 0)]
+}}}
+
+Every other week on Tuesday and Thursday, for 8 occurrences.
+{{{
+>>> list(rrule(WEEKLY, interval=2, count=8,
+ wkst=SU, byweekday=(TU,TH),
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 4, 9, 0),
+ datetime.datetime(1997, 9, 16, 9, 0),
+ datetime.datetime(1997, 9, 18, 9, 0),
+ datetime.datetime(1997, 9, 30, 9, 0),
+ datetime.datetime(1997, 10, 2, 9, 0),
+ datetime.datetime(1997, 10, 14, 9, 0),
+ datetime.datetime(1997, 10, 16, 9, 0)]
+}}}
+
+Monthly on the 1st Friday for ten occurrences.
+{{{
+>>> list(rrule(MONTHLY, count=10, byweekday=FR(1),
+ dtstart=parse("19970905T090000")))
+[datetime.datetime(1997, 9, 5, 9, 0),
+ datetime.datetime(1997, 10, 3, 9, 0),
+ datetime.datetime(1997, 11, 7, 9, 0),
+ datetime.datetime(1997, 12, 5, 9, 0),
+ datetime.datetime(1998, 1, 2, 9, 0),
+ datetime.datetime(1998, 2, 6, 9, 0),
+ datetime.datetime(1998, 3, 6, 9, 0),
+ datetime.datetime(1998, 4, 3, 9, 0),
+ datetime.datetime(1998, 5, 1, 9, 0),
+ datetime.datetime(1998, 6, 5, 9, 0)]
+}}}
+
+Every other month on the 1st and last Sunday of the month for 10 occurrences.
+{{{
+>>> list(rrule(MONTHLY, interval=2, count=10,
+ byweekday=(SU(1), SU(-1)),
+ dtstart=parse("19970907T090000")))
+[datetime.datetime(1997, 9, 7, 9, 0),
+ datetime.datetime(1997, 9, 28, 9, 0),
+ datetime.datetime(1997, 11, 2, 9, 0),
+ datetime.datetime(1997, 11, 30, 9, 0),
+ datetime.datetime(1998, 1, 4, 9, 0),
+ datetime.datetime(1998, 1, 25, 9, 0),
+ datetime.datetime(1998, 3, 1, 9, 0),
+ datetime.datetime(1998, 3, 29, 9, 0),
+ datetime.datetime(1998, 5, 3, 9, 0),
+ datetime.datetime(1998, 5, 31, 9, 0)]
+}}}
+
+Monthly on the second to last Monday of the month for 6 months.
+{{{
+>>> list(rrule(MONTHLY, count=6, byweekday=MO(-2),
+ dtstart=parse("19970922T090000")))
+[datetime.datetime(1997, 9, 22, 9, 0),
+ datetime.datetime(1997, 10, 20, 9, 0),
+ datetime.datetime(1997, 11, 17, 9, 0),
+ datetime.datetime(1997, 12, 22, 9, 0),
+ datetime.datetime(1998, 1, 19, 9, 0),
+ datetime.datetime(1998, 2, 16, 9, 0)]
+}}}
+
+Monthly on the third to the last day of the month, for 6 months.
+{{{
+>>> list(rrule(MONTHLY, count=6, bymonthday=-3,
+ dtstart=parse("19970928T090000")))
+[datetime.datetime(1997, 9, 28, 9, 0),
+ datetime.datetime(1997, 10, 29, 9, 0),
+ datetime.datetime(1997, 11, 28, 9, 0),
+ datetime.datetime(1997, 12, 29, 9, 0),
+ datetime.datetime(1998, 1, 29, 9, 0),
+ datetime.datetime(1998, 2, 26, 9, 0)]
+}}}
+
+Monthly on the 2nd and 15th of the month for 5 occurrences.
+{{{
+>>> list(rrule(MONTHLY, count=5, bymonthday=(2,15),
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 15, 9, 0),
+ datetime.datetime(1997, 10, 2, 9, 0),
+ datetime.datetime(1997, 10, 15, 9, 0),
+ datetime.datetime(1997, 11, 2, 9, 0)]
+}}}
+
+Monthly on the first and last day of the month for 3 occurrences.
+{{{
+>>> list(rrule(MONTHLY, count=5, bymonthday=(-1,1,),
+ dtstart=parse("1997090
+2T090000")))
+[datetime.datetime(1997, 9, 30, 9, 0),
+ datetime.datetime(1997, 10, 1, 9, 0),
+ datetime.datetime(1997, 10, 31, 9, 0),
+ datetime.datetime(1997, 11, 1, 9, 0),
+ datetime.datetime(1997, 11, 30, 9, 0)]
+}}}
+
+Every 18 months on the 10th thru 15th of the month for 10 occurrences.
+{{{
+>>> list(rrule(MONTHLY, interval=18, count=10,
+ bymonthday=range(10,16),
+ dtstart=parse("19970910T090000")))
+[datetime.datetime(1997, 9, 10, 9, 0),
+ datetime.datetime(1997, 9, 11, 9, 0),
+ datetime.datetime(1997, 9, 12, 9, 0),
+ datetime.datetime(1997, 9, 13, 9, 0),
+ datetime.datetime(1997, 9, 14, 9, 0),
+ datetime.datetime(1997, 9, 15, 9, 0),
+ datetime.datetime(1999, 3, 10, 9, 0),
+ datetime.datetime(1999, 3, 11, 9, 0),
+ datetime.datetime(1999, 3, 12, 9, 0),
+ datetime.datetime(1999, 3, 13, 9, 0)]
+}}}
+
+Every Tuesday, every other month, 6 occurences.
+{{{
+>>> list(rrule(MONTHLY, interval=2, count=6, byweekday=TU,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 9, 9, 0),
+ datetime.datetime(1997, 9, 16, 9, 0),
+ datetime.datetime(1997, 9, 23, 9, 0),
+ datetime.datetime(1997, 9, 30, 9, 0),
+ datetime.datetime(1997, 11, 4, 9, 0)]
+}}}
+
+Yearly in June and July for 10 occurrences.
+{{{
+>>> list(rrule(YEARLY, count=4, bymonth=(6,7),
+ dtstart=parse("19970610T0900
+00")))
+[datetime.datetime(1997, 6, 10, 9, 0),
+ datetime.datetime(1997, 7, 10, 9, 0),
+ datetime.datetime(1998, 6, 10, 9, 0),
+ datetime.datetime(1998, 7, 10, 9, 0)]
+}}}
+
+Every 3rd year on the 1st, 100th and 200th day for 4 occurrences.
+{{{
+>>> list(rrule(YEARLY, count=4, interval=3, byyearday=(1,100,200),
+ dtstart=parse("19970101T090000")))
+[datetime.datetime(1997, 1, 1, 9, 0),
+ datetime.datetime(1997, 4, 10, 9, 0),
+ datetime.datetime(1997, 7, 19, 9, 0),
+ datetime.datetime(2000, 1, 1, 9, 0)]
+}}}
+
+Every 20th Monday of the year, 3 occurrences.
+{{{
+>>> list(rrule(YEARLY, count=3, byweekday=MO(20),
+ dtstart=parse("19970519T090000")))
+[datetime.datetime(1997, 5, 19, 9, 0),
+ datetime.datetime(1998, 5, 18, 9, 0),
+ datetime.datetime(1999, 5, 17, 9, 0)]
+}}}
+
+Monday of week number 20 (where the default start of the week is Monday),
+3 occurrences.
+{{{
+>>> list(rrule(YEARLY, count=3, byweekno=20, byweekday=MO,
+ dtstart=parse("19970512T090000")))
+[datetime.datetime(1997, 5, 12, 9, 0),
+ datetime.datetime(1998, 5, 11, 9, 0),
+ datetime.datetime(1999, 5, 17, 9, 0)]
+}}}
+
+The week number 1 may be in the last year.
+{{{
+>>> list(rrule(WEEKLY, count=3, byweekno=1, byweekday=MO,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 12, 29, 9, 0),
+ datetime.datetime(1999, 1, 4, 9, 0),
+ datetime.datetime(2000, 1, 3, 9, 0)]
+}}}
+
+And the week numbers greater than 51 may be in the next year.
+{{{
+>>> list(rrule(WEEKLY, count=3, byweekno=52, byweekday=SU,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 12, 28, 9, 0),
+ datetime.datetime(1998, 12, 27, 9, 0),
+ datetime.datetime(2000, 1, 2, 9, 0)]
+}}}
+
+Only some years have week number 53:
+{{{
+>>> list(rrule(WEEKLY, count=3, byweekno=53, byweekday=MO,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1998, 12, 28, 9, 0),
+ datetime.datetime(2004, 12, 27, 9, 0),
+ datetime.datetime(2009, 12, 28, 9, 0)]
+}}}
+
+Every Friday the 13th, 4 occurrences.
+{{{
+>>> list(rrule(YEARLY, count=4, byweekday=FR, bymonthday=13,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1998, 2, 13, 9, 0),
+ datetime.datetime(1998, 3, 13, 9, 0),
+ datetime.datetime(1998, 11, 13, 9, 0),
+ datetime.datetime(1999, 8, 13, 9, 0)]
+}}}
+
+Every four years, the first Tuesday after a Monday in November,
+3 occurrences (U.S. Presidential Election day):
+{{{
+>>> list(rrule(YEARLY, interval=4, count=3, bymonth=11,
+ byweekday=TU, bymonthday=(2,3,4,5,6,7,8),
+ dtstart=parse("19961105T090000")))
+[datetime.datetime(1996, 11, 5, 9, 0),
+ datetime.datetime(2000, 11, 7, 9, 0),
+ datetime.datetime(2004, 11, 2, 9, 0)]
+}}}
+
+The 3rd instance into the month of one of Tuesday, Wednesday or
+Thursday, for the next 3 months:
+{{{
+>>> list(rrule(MONTHLY, count=3, byweekday=(TU,WE,TH),
+ bysetpos=3, dtstart=parse("19970904T090000")))
+[datetime.datetime(1997, 9, 4, 9, 0),
+ datetime.datetime(1997, 10, 7, 9, 0),
+ datetime.datetime(1997, 11, 6, 9, 0)]
+}}}
+
+The 2nd to last weekday of the month, 3 occurrences.
+{{{
+>>> list(rrule(MONTHLY, count=3, byweekday=(MO,TU,WE,TH,FR),
+ bysetpos=-2, dtstart=parse("19970929T090000")))
+[datetime.datetime(1997, 9, 29, 9, 0),
+ datetime.datetime(1997, 10, 30, 9, 0),
+ datetime.datetime(1997, 11, 27, 9, 0)]
+}}}
+
+Every 3 hours from 9:00 AM to 5:00 PM on a specific day.
+{{{
+>>> list(rrule(HOURLY, interval=3,
+ dtstart=parse("19970902T090000"),
+ until=parse("19970902T170000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 2, 12, 0),
+ datetime.datetime(1997, 9, 2, 15, 0)]
+}}}
+
+Every 15 minutes for 6 occurrences.
+{{{
+>>> list(rrule(MINUTELY, interval=15, count=6,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 2, 9, 15),
+ datetime.datetime(1997, 9, 2, 9, 30),
+ datetime.datetime(1997, 9, 2, 9, 45),
+ datetime.datetime(1997, 9, 2, 10, 0),
+ datetime.datetime(1997, 9, 2, 10, 15)]
+}}}
+
+Every hour and a half for 4 occurrences.
+{{{
+>>> list(rrule(MINUTELY, interval=90, count=4,
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 2, 10, 30),
+ datetime.datetime(1997, 9, 2, 12, 0),
+ datetime.datetime(1997, 9, 2, 13, 30)]
+}}}
+
+Every 20 minutes from 9:00 AM to 4:40 PM for two days.
+{{{
+>>> list(rrule(MINUTELY, interval=20, count=48,
+ byhour=range(9,17), byminute=(0,20,40),
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 2, 9, 20),
+ (...)
+ datetime.datetime(1997, 9, 2, 16, 20),
+ datetime.datetime(1997, 9, 2, 16, 40),
+ datetime.datetime(1997, 9, 3, 9, 0),
+ datetime.datetime(1997, 9, 3, 9, 20),
+ (...)
+ datetime.datetime(1997, 9, 3, 16, 20),
+ datetime.datetime(1997, 9, 3, 16, 40)]
+}}}
+
+An example where the days generated makes a difference because of {{{wkst}}}.
+{{{
+>>> list(rrule(WEEKLY, interval=2, count=4,
+ byweekday=(TU,SU), wkst=MO,
+ dtstart=parse("19970805T090000")))
+[datetime.datetime(1997, 8, 5, 9, 0),
+ datetime.datetime(1997, 8, 10, 9, 0),
+ datetime.datetime(1997, 8, 19, 9, 0),
+ datetime.datetime(1997, 8, 24, 9, 0)]
+
+>>> list(rrule(WEEKLY, interval=2, count=4,
+ byweekday=(TU,SU), wkst=SU,
+ dtstart=parse("19970805T090000")))
+[datetime.datetime(1997, 8, 5, 9, 0),
+ datetime.datetime(1997, 8, 17, 9, 0),
+ datetime.datetime(1997, 8, 19, 9, 0),
+ datetime.datetime(1997, 8, 31, 9, 0)]
+}}}
+
+==== rruleset type ====
+The {{{rruleset}}} type allows more complex recurrence setups, mixing
+multiple rules, dates, exclusion rules, and exclusion dates.
+The type constructor takes the following keyword arguments:
+
+ cache::
+ If True, caching of results will be enabled, improving performance
+ of multiple queries considerably.
+
+==== rruleset methods ====
+The following methods are available:
+
+ rruleset.rrule(rrule)::
+ Include the given {{{rrule}}} instance in the recurrence set
+ generation.
+
+ rruleset.rdate(dt)::
+ Include the given {{{datetime}}} instance in the recurrence
+ set generation.
+
+ rruleset.exrule(rrule)::
+ Include the given {{{rrule}}} instance in the recurrence set
+ exclusion list. Dates which are part of the given recurrence
+ rules will not be generated, even if some inclusive {{{rrule}}}
+ or {{{rdate}}} matches them.
+
+ rruleset.exdate(dt)::
+ Include the given {{{datetime}}} instance in the recurrence set
+ exclusion list. Dates included that way will not be generated,
+ even if some inclusive {{{rrule}}} or {{{rdate}}} matches them.
+
+ rruleset.before(dt, inc=False)::
+ Returns the last recurrence before the given {{{datetime}}}
+ instance. The {{{inc}}} keyword defines what happens if
+ {{{dt}}} '''is''' an occurrence. With {{{inc == True}}},
+ if {{{dt}}} itself is an occurrence, it will be returned.
+
+ rruleset.after(dt, inc=False)::
+ Returns the first recurrence after the given {{{datetime}}}
+ instance. The {{{inc}}} keyword defines what happens if
+ {{{dt}}} '''is''' an occurrence. With {{{inc == True}}},
+ if {{{dt}}} itself is an occurrence, it will be returned.
+
+ rruleset.between(after, before, inc=False)::
+ Returns all the occurrences of the rrule between {{{after}}}
+ and {{{before}}}. The {{{inc}}} keyword defines what happens
+ if {{{after}}} and/or {{{before}}} are themselves occurrences.
+ With {{{inc == True}}}, they will be included in the list,
+ if they are found in the recurrence set.
+
+ rruleset.count()::
+ Returns the number of recurrences in this set. It will have
+ go trough the whole recurrence, if this hasn't been done
+ before.
+
+Besides these methods, {{{rruleset}}} instances also support
+the {{{__getitem__()}}} and {{{__contains__()}}} special methods,
+meaning that these are valid expressions:
+{{{
+set = rruleset(...)
+if datetime(...) in set:
+ ...
+print set[0]
+print set[-1]
+print set[1:2]
+print set[::-2]
+}}}
+
+The getitem/slicing mechanism is smart enough to avoid getting the whole
+recurrence set, if possible.
+
+==== rruleset examples ====
+Daily, for 7 days, jumping Saturday and Sunday occurrences.
+{{{
+>>> set = rruleset()
+>>> set.rrule(rrule(DAILY, count=7,
+ dtstart=parse("19970902T090000")))
+>>> set.exrule(rrule(YEARLY, byweekday=(SA,SU),
+ dtstart=parse("19970902T090000")))
+>>> list(set)
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 3, 9, 0),
+ datetime.datetime(1997, 9, 4, 9, 0),
+ datetime.datetime(1997, 9, 5, 9, 0),
+ datetime.datetime(1997, 9, 8, 9, 0)]
+}}}
+
+Weekly, for 4 weeks, plus one time on day 7, and not on day 16.
+{{{
+>>> set = rruleset()
+>>> set.rrule(rrule(WEEKLY, count=4,
+ dtstart=parse("19970902T090000")))
+>>> set.rdate(datetime.datetime(1997, 9, 7, 9, 0))
+>>> set.exdate(datetime.datetime(1997, 9, 16, 9, 0))
+>>> list(set)
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 7, 9, 0),
+ datetime.datetime(1997, 9, 9, 9, 0),
+ datetime.datetime(1997, 9, 23, 9, 0)]
+}}}
+
+==== rrulestr() function ====
+The {{{rrulestr()}}} function is a parser for ''RFC-like'' syntaxes.
+The function prototype is:
+{{{
+rrulestr(str)
+}}}
+
+The string passed as parameter may be a multiple line string, a
+single line string, or just the {{{RRULE}}} property value.
+
+Additionally, it accepts the following keyword arguments:
+
+ cache::
+ If {{{True}}}, the {{{rruleset}}} or {{{rrule}}} created instance
+ will cache its results. Default is not to cache.
+
+ dtstart::
+ If given, it must be a {{{datetime}}} instance that will be used
+ when no {{{DTSTART}}} property is found in the parsed string. If
+ it is not given, and the property is not found, {{{datetime.now()}}}
+ will be used instead.
+
+ unfold::
+ If set to {{{True}}}, lines will be unfolded following the RFC
+ specification. It defaults to {{{False}}}, meaning that spaces
+ before every line will be stripped.
+
+ forceset::
+ If set to {{{True}}} a {{{rruleset}}} instance will be returned,
+ even if only a single rule is found. The default is to return an
+ {{{rrule}}} if possible, and an {{{rruleset}}} if necessary.
+
+ compatible::
+ If set to {{{True}}}, the parser will operate in RFC-compatible
+ mode. Right now it means that {{{unfold}}} will be turned on,
+ and if a {{{DTSTART}}} is found, it will be considered the first
+ recurrence instance, as documented in the RFC.
+
+ ignoretz::
+ If set to {{{True}}}, the date parser will ignore timezone
+ information available in the {{{DTSTART}}} property, or the
+ {{{UNTIL}}} attribute.
+
+ tzinfos::
+ If set, it will be passed to the datetime string parser to
+ resolve unknown timezone settings. For more information about
+ what could be used here, check the parser documentation.
+
+==== rrulestr() examples ====
+
+Every 10 days, 5 occurrences.
+{{{
+>>> list(rrulestr("""
+... DTSTART:19970902T090000
+... RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
+... """))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 12, 9, 0),
+ datetime.datetime(1997, 9, 22, 9, 0),
+ datetime.datetime(1997, 10, 2, 9, 0),
+ datetime.datetime(1997, 10, 12, 9, 0)]
+}}}
+
+Same thing, but passing only the {{{RRULE}}} value.
+{{{
+>>> list(rrulestr("FREQ=DAILY;INTERVAL=10;COUNT=5",
+ dtstart=parse("19970902T090000")))
+[datetime.datetime(1997, 9, 2, 9, 0),
+ datetime.datetime(1997, 9, 12, 9, 0),
+ datetime.datetime(1997, 9, 22, 9, 0),
+ datetime.datetime(1997, 10, 2, 9, 0),
+ datetime.datetime(1997, 10, 12, 9, 0)]
+}}}
+
+Notice that when using a single rule, it returns an
+{{{rrule}}} instance, unless {{{forceset}}} was used.
+{{{
+>>> rrulestr("FREQ=DAILY;INTERVAL=10;COUNT=5")
+<dateutil.rrule.rrule instance at 0x30269f08>
+
+>>> rrulestr("""
+... DTSTART:19970902T090000
+... RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
+... """)
+<dateutil.rrule.rrule instance at 0x302699e0>
+
+>>> rrulestr("FREQ=DAILY;INTERVAL=10;COUNT=5", forceset=True)
+<dateutil.rrule.rruleset instance at 0x30269f08>
+}}}
+
+But when an {{{rruleset}}} is needed, it is automatically used.
+{{{
+>>> rrulestr("""
+... DTSTART:19970902T090000
+... RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
+... RRULE:FREQ=DAILY;INTERVAL=5;COUNT=3
+... """)
+<dateutil.rrule.rruleset instance at 0x302699e0>
+}}}
+
+=== parser ===
+This module offers a generic date/time string parser which is
+able to parse most known formats to represent a date and/or
+time.
+
+==== parse() function ====
+That's probably the only function you'll need from this module.
+It offers you an interface to access the parser functionality and
+extract a {{{datetime}}} type out of a string.
+
+The prototype of this function is:
+{{{
+parse(timestr)
+}}}
+
+Additionally, the following keyword arguments are available:
+
+ default::
+ If given, this must be a {{{datetime}}} instance. Any fields
+ missing in the parsed date will be copied from this instance.
+ The default value is the current date, at 00:00:00am.
+
+ ignoretz::
+ If this is true, even if a timezone is found in the string,
+ the parser will not use it.
+
+ tzinfos::
+ Using this keyword argument you may provide custom timezones
+ to the parser. If given, it must be either a dictionary with
+ the timezone abbreviation as key, or a function accepting a
+ timezone abbreviation and offset as argument. The dictionary
+ values and the function return must be a timezone offset
+ in seconds, a tzinfo subclass, or a string defining the
+ timezone (in the TZ environment variable format).
+
+ dayfirst::
+ This option allow one to change the precedence in which
+ days are parsed in date strings. The default is given in the
+ parserinfo instance (the default parserinfo has it set to
+ False). If {{{dayfirst}}} is False, the {{{MM-DD-YYYY}}}
+ format will have precedence over {{{DD-MM-YYYY}}} in an
+ ambiguous date.
+
+ yearfirst::
+ This option allow one to change the precedence in which
+ years are parsed in date strings. The default is given in
+ the parserinfo instance (the default parserinfo has it set
+ to False). If {{{yearfirst}}} is false, the {{{MM-DD-YY}}}
+ format will have precedence over {{{YY-MM-DD}}} in an
+ ambiguous date.
+
+ fuzzy::
+ If {{{fuzzy}}} is set to True, unknown tokens in the string
+ will be ignored.
+
+ parserinfo::
+ This parameter allows one to change how the string is parsed,
+ by using a different parserinfo class instance. Using it you
+ may, for example, intenationalize the parser strings, or make
+ it ignore additional words.
+
+==== Format precedence ====
+Whenever an ambiguous date is found, the {{{dayfirst}}} and
+{{{yearfirst}}} parameters will control how the information
+is processed. Here is the precedence in each case:
+
+If {{{dayfirst}}} is {{{False}}} and {{{yearfirst}}} is {{{False}}},
+(default, if no parameter is given):
+
+ * {{{MM-DD-YY}}}
+ * {{{DD-MM-YY}}}
+ * {{{YY-MM-DD}}}
+
+If {{{dayfirst}}} is {{{True}}} and {{{yearfirst}}} is {{{False}}}:
+
+ * {{{DD-MM-YY}}}
+ * {{{MM-DD-YY}}}
+ * {{{YY-MM-DD}}}
+
+If {{{dayfirst}}} is {{{False}}} and {{{yearfirst}}} is {{{True}}}:
+
+ * {{{YY-MM-DD}}}
+ * {{{MM-DD-YY}}}
+ * {{{DD-MM-YY}}}
+
+If {{{dayfirst}}} is {{{True}}} and {{{yearfirst}}} is {{{True}}}:
+
+ * {{{YY-MM-DD}}}
+ * {{{DD-MM-YY}}}
+ * {{{MM-DD-YY}}}
+
+==== Converting two digit years ====
+When a two digit year is found, it is processed considering
+the current year, so that the computed year is never more
+than 49 years after the current year, nor 50 years before the
+current year. In other words, if we are in year 2003, and the
+year 30 is found, it will be considered as 2030, but if the
+year 60 is found, it will be considered 1960.
+
+==== Examples ====
+The following code will prepare the environment:
+{{{
+>>> from dateutil.parser import *
+>>> from dateutil.tz import *
+>>> from datetime import *
+>>> TZOFFSETS = {"BRST": -10800}
+>>> BRSTTZ = tzoffset(-10800, "BRST")
+>>> DEFAULT = datetime(2003, 9, 25)
+}}}
+
+Some simple examples based on the {{{date}}} command, using the
+{{{TZOFFSET}}} dictionary to provide the BRST timezone offset.
+{{{
+>>> parse("Thu Sep 25 10:36:28 BRST 2003", tzinfos=TZOFFSETS)
+datetime.datetime(2003, 9, 25, 10, 36, 28,
+ tzinfo=tzoffset('BRST', -10800))
+
+>>> parse("2003 10:36:28 BRST 25 Sep Thu", tzinfos=TZOFFSETS)
+datetime.datetime(2003, 9, 25, 10, 36, 28,
+ tzinfo=tzoffset('BRST', -10800))
+}}}
+
+Notice that since BRST is my local timezone, parsing it without
+further timezone settings will yield a {{{tzlocal}}} timezone.
+{{{
+>>> parse("Thu Sep 25 10:36:28 BRST 2003")
+datetime.datetime(2003, 9, 25, 10, 36, 28, tzinfo=tzlocal())
+}}}
+
+We can also ask to ignore the timezone explicitly:
+{{{
+>>> parse("Thu Sep 25 10:36:28 BRST 2003", ignoretz=True)
+datetime.datetime(2003, 9, 25, 10, 36, 28)
+}}}
+
+That's the same as processing a string without timezone:
+{{{
+>>> parse("Thu Sep 25 10:36:28 2003")
+datetime.datetime(2003, 9, 25, 10, 36, 28)
+}}}
+
+Without the year, but passing our {{{DEFAULT}}} datetime to return
+the same year, no mattering what year we currently are in:
+{{{
+>>> parse("Thu Sep 25 10:36:28", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 10, 36, 28)
+}}}
+
+Strip it further:
+{{{
+>>> parse("Thu Sep 10:36:28", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 10, 36, 28)
+
+>>> parse("Thu 10:36:28", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 10, 36, 28)
+
+>>> parse("Thu 10:36", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 10, 36)
+
+>>> parse("10:36", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 10, 36)
+>>>
+}}}
+
+Strip in a different way:
+{{{
+>>> parse("Thu Sep 25 2003")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("Sep 25 2003")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("Sep 2003", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("Sep", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("2003", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 0, 0)
+}}}
+
+Another format, based on {{{date -R}}} (RFC822):
+{{{
+>>> parse("Thu, 25 Sep 2003 10:49:41 -0300")
+datetime.datetime(2003, 9, 25, 10, 49, 41,
+ tzinfo=tzoffset(None, -10800))
+}}}
+
+ISO format:
+{{{
+>>> parse("2003-09-25T10:49:41.5-03:00")
+datetime.datetime(2003, 9, 25, 10, 49, 41, 500000,
+ tzinfo=tzoffset(None, -10800))
+}}}
+
+Some variations:
+{{{
+>>> parse("2003-09-25T10:49:41")
+datetime.datetime(2003, 9, 25, 10, 49, 41)
+
+>>> parse("2003-09-25T10:49")
+datetime.datetime(2003, 9, 25, 10, 49)
+
+>>> parse("2003-09-25T10")
+datetime.datetime(2003, 9, 25, 10, 0)
+
+>>> parse("2003-09-25")
+datetime.datetime(2003, 9, 25, 0, 0)
+}}}
+
+ISO format, without separators:
+{{{
+>>> parse("20030925T104941.5-0300")
+datetime.datetime(2003, 9, 25, 10, 49, 41, 500000,
+ tzinfo=tzinfo=tzoffset(None, -10800))
+
+>>> parse("20030925T104941-0300")
+datetime.datetime(2003, 9, 25, 10, 49, 41,
+ tzinfo=tzoffset(None, -10800))
+
+>>> parse("20030925T104941")
+datetime.datetime(2003, 9, 25, 10, 49, 41)
+
+>>> parse("20030925T1049")
+datetime.datetime(2003, 9, 25, 10, 49)
+
+>>> parse("20030925T10")
+datetime.datetime(2003, 9, 25, 10, 0)
+
+>>> parse("20030925")
+datetime.datetime(2003, 9, 25, 0, 0)
+}}}
+
+Everything together.
+{{{
+>>> parse("199709020900")
+datetime.datetime(1997, 9, 2, 9, 0)
+>>> parse("19970902090059")
+datetime.datetime(1997, 9, 2, 9, 0, 59)
+}}}
+
+Different date orderings:
+{{{
+>>> parse("2003-09-25")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("2003-Sep-25")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("25-Sep-2003")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("Sep-25-2003")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("09-25-2003")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("25-09-2003")
+datetime.datetime(2003, 9, 25, 0, 0)
+}}}
+
+Check some ambiguous dates:
+{{{
+>>> parse("10-09-2003")
+datetime.datetime(2003, 10, 9, 0, 0)
+
+>>> parse("10-09-2003", dayfirst=True)
+datetime.datetime(2003, 9, 10, 0, 0)
+
+>>> parse("10-09-03")
+datetime.datetime(2003, 10, 9, 0, 0)
+
+>>> parse("10-09-03", yearfirst=True)
+datetime.datetime(2010, 9, 3, 0, 0)
+}}}
+
+Other date separators are allowed:
+{{{
+>>> parse("2003.Sep.25")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("2003/09/25")
+datetime.datetime(2003, 9, 25, 0, 0)
+}}}
+
+Even with spaces:
+{{{
+>>> parse("2003 Sep 25")
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("2003 09 25")
+datetime.datetime(2003, 9, 25, 0, 0)
+}}}
+
+Hours with letters work:
+{{{
+>>> parse("10h36m28.5s", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 10, 36, 28, 500000)
+
+>>> parse("01s02h03m", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 2, 3, 1)
+
+>>> parse("01h02m03", default=DEFAULT)
+datetime.datetime(2003, 9, 3, 1, 2)
+
+>>> parse("01h02", default=DEFAULT)
+datetime.datetime(2003, 9, 2, 1, 0)
+
+>>> parse("01h02s", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 1, 0, 2)
+}}}
+
+With AM/PM:
+{{{
+>>> parse("10h am", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 10, 0)
+
+>>> parse("10pm", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 22, 0)
+
+>>> parse("12:00am", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 0, 0)
+
+>>> parse("12pm", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 12, 0)
+}}}
+
+Some special treating for ''pertain'' relations:
+{{{
+>>> parse("Sep 03", default=DEFAULT)
+datetime.datetime(2003, 9, 3, 0, 0)
+
+>>> parse("Sep of 03", default=DEFAULT)
+datetime.datetime(2003, 9, 25, 0, 0)
+}}}
+
+Fuzzy parsing:
+{{{
+>>> s = "Today is 25 of September of 2003, exactly " \
+... "at 10:49:41 with timezone -03:00."
+>>> parse(s, fuzzy=True)
+datetime.datetime(2003, 9, 25, 10, 49, 41,
+ tzinfo=tzoffset(None, -10800))
+}}}
+
+Other random formats:
+{{{
+>>> parse("Wed, July 10, '96")
+datetime.datetime(1996, 7, 10, 0, 0)
+
+>>> parse("1996.07.10 AD at 15:08:56 PDT", ignoretz=True)
+datetime.datetime(1996, 7, 10, 15, 8, 56)
+
+>>> parse("Tuesday, April 12, 1952 AD 3:30:42pm PST", ignoretz=True)
+datetime.datetime(1952, 4, 12, 15, 30, 42)
+
+>>> parse("November 5, 1994, 8:15:30 am EST", ignoretz=True)
+datetime.datetime(1994, 11, 5, 8, 15, 30)
+
+>>> parse("3rd of May 2001")
+datetime.datetime(2001, 5, 3, 0, 0)
+
+>>> parse("5:50 A.M. on June 13, 1990")
+datetime.datetime(1990, 6, 13, 5, 50)
+}}}
+
+=== easter ===
+This module offers a generic easter computing method for
+any given year, using Western, Orthodox or Julian algorithms.
+
+==== easter() function ====
+This method was ported from the work done by
+[http://users.chariot.net.au/~gmarts/eastalg.htm GM Arts],
+on top of the algorithm by
+[http://www.tondering.dk/claus/calendar.html 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
+ 1. Original method, with date converted to Gregorian
+ calendar, valid in years 1583 to 4099
+ 1. 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.
+
+=== tz ===
+This module offers timezone implementations subclassing
+the abstract {{{datetime.tzinfo}}} type. There are
+classes to handle [http://www.twinsun.com/tz/tz-link.htm tzfile]
+format files (usually are in /etc/localtime,
+/usr/share/zoneinfo, etc), TZ environment string (in all
+known formats), given ranges (with help from relative
+deltas), local machine timezone, fixed offset timezone,
+and UTC timezone.
+
+==== tzutc type ====
+This type implements a basic UTC timezone. The constructor of this
+type accepts no parameters.
+
+==== tzutc examples ====
+{{{
+>>> from datetime import *
+>>> from dateutil.tz import *
+
+>>> datetime.now()
+datetime.datetime(2003, 9, 27, 9, 40, 1, 521290)
+
+>>> datetime.now(tzutc())
+datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc())
+
+>>> datetime.now(tzutc()).tzname()
+'UTC'
+}}}
+
+==== tzoffset type ====
+This type implements a fixed offset timezone, with no
+support to daylight saving times. Here is the prototype of the
+type constructor:
+{{{
+tzoffset(name, offset)
+}}}
+
+The {{{name}}} parameter may be optionally set to {{{None}}}, and
+{{{offset}}} must be given in seconds.
+
+==== tzoffset examples ====
+{{{
+>>> from datetime import *
+>>> from dateutil.tz import *
+
+>>> datetime.now(tzoffset("BRST", -10800))
+datetime.datetime(2003, 9, 27, 9, 52, 43, 624904,
+ tzinfo=tzinfo=tzoffset('BRST', -10800))
+
+>>> datetime.now(tzoffset("BRST", -10800)).tzname()
+'BRST'
+
+>>> datetime.now(tzoffset("BRST", -10800)).astimezone(tzutc())
+datetime.datetime(2003, 9, 27, 12, 53, 11, 446419,
+ tzinfo=tzutc())
+}}}
+
+==== tzlocal type ====
+This type implements timezone settings as known by the
+operating system. The constructor of this type accepts no
+parameters.
+
+==== tzlocal examples ====
+{{{
+>>> from datetime import *
+>>> from dateutil.tz import *
+
+>>> datetime.now(tzlocal())
+datetime.datetime(2003, 9, 27, 10, 1, 43, 673605,
+ tzinfo=tzlocal())
+
+>>> datetime.now(tzlocal()).tzname()
+'BRST'
+
+>>> datetime.now(tzlocal()).astimezone(tzoffset(None, 0))
+datetime.datetime(2003, 9, 27, 13, 3, 0, 11493,
+ tzinfo=tzoffset(None, 0))
+}}}
+
+==== tzstr type ====
+This type implements timezone settings extracted from a
+string in known TZ environment variable formats. Here is the prototype
+of the constructor:
+{{{
+tzstr(str)
+}}}
+
+==== tzstr examples ====
+Here are examples of the recognized formats:
+
+ * {{{EST5EDT}}}
+ * {{{EST5EDT,4,0,6,7200,10,0,26,7200,3600}}}
+ * {{{EST5EDT,4,1,0,7200,10,-1,0,7200,3600}}}
+ * {{{EST5EDT4,M4.1.0/02:00:00,M10-5-0/02:00}}}
+ * {{{EST5EDT4,95/02:00:00,298/02:00}}}
+ * {{{EST5EDT4,J96/02:00:00,J299/02:00}}}
+
+Notice that if daylight information is not present, but a
+daylight abbreviation was provided, {{{tzstr}}} will follow the
+convention of using the first sunday of April to start daylight
+saving, and the last sunday of October to end it. If start or
+end time is not present, 2AM will be used, and if the daylight
+offset is not present, the standard offset plus one hour will
+be used. This convention is the same as used in the GNU libc.
+
+This also means that some of the above examples are exactly
+equivalent, and all of these examples are equivalent
+in the year of 2003.
+
+Here is the example mentioned in the
+[http://www.python.org/doc/current/lib/module-time.html time module documentation].
+{{{
+>>> os.environ['TZ'] = 'EST+05EDT,M4.1.0,M10.5.0'
+>>> time.tzset()
+>>> time.strftime('%X %x %Z')
+'02:07:36 05/08/03 EDT'
+>>> os.environ['TZ'] = 'AEST-10AEDT-11,M10.5.0,M3.5.0'
+>>> time.tzset()
+>>> time.strftime('%X %x %Z')
+'16:08:12 05/08/03 AEST'
+}}}
+
+And here is an example showing the same information using {{{tzstr}}},
+without touching system settings.
+{{{
+>>> tz1 = tzstr('EST+05EDT,M4.1.0,M10.5.0')
+>>> tz2 = tzstr('AEST-10AEDT-11,M10.5.0,M3.5.0')
+>>> dt = datetime(2003, 5, 8, 2, 7, 36, tzinfo=tz1)
+>>> dt.strftime('%X %x %Z')
+'02:07:36 05/08/03 EDT'
+>>> dt.astimezone(tz2).strftime('%X %x %Z')
+'16:07:36 05/08/03 AEST'
+}}}
+
+Are these really equivalent?
+{{{
+>>> tzstr('EST5EDT') == tzstr('EST5EDT,4,1,0,7200,10,-1,0,7200,3600')
+True
+}}}
+
+Check the daylight limit.
+{{{
+>>> datetime(2003, 4, 6, 1, 59, tzinfo=tz).tzname()
+'EST'
+>>> datetime(2003, 4, 6, 2, 00, tzinfo=tz).tzname()
+'EDT'
+>>> datetime(2003, 10, 26, 0, 59, tzinfo=tz).tzname()
+'EDT'
+>>> datetime(2003, 10, 26, 1, 00, tzinfo=tz).tzname()
+'EST'
+}}}
+
+==== tzrange type ====
+This type offers the same functionality as the {{{tzstr}}} type, but
+instead of timezone strings, information is passed using
+{{{relativedelta}}}s which are applied to a datetime set to the first
+day of the year. Here is the prototype of this type's constructor:
+{{{
+tzrange(stdabbr, stdoffset=None, dstabbr=None, dstoffset=None,
+ start=None, end=None):
+}}}
+
+Offsets must be given in seconds. Information not provided will be
+set to the defaults, as explained in the {{{tzstr}}} section above.
+
+==== tzrange examples ====
+{{{
+>>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT")
+True
+
+>>> from dateutil.relativedelta import *
+>>> range1 = tzrange("EST", -18000, "EDT")
+>>> range2 = tzrange("EST", -18000, "EDT", -14400,
+... relativedelta(hours=+2, month=4, day=1,
+ weekday=SU(+1)),
+... relativedelta(hours=+1, month=10, day=31,
+ weekday=SU(-1)))
+>>> tzstr('EST5EDT') == range1 == range2
+True
+}}}
+
+Notice a minor detail in the last example: while the DST should end
+at 2AM, the delta will catch 1AM. That's because the daylight saving
+time should end at 2AM standard time (the difference between STD and
+DST is 1h in the given example) instead of the DST time. That's how
+the {{{tzinfo}}} subtypes should deal with the extra hour that happens
+when going back to the standard time. Check
+[http://www.python.org/doc/current/lib/datetime-tzinfo.html tzinfo documentation]
+for more information.
+
+==== tzfile type ====
+This type allows one to use tzfile(5) format timezone files to extract
+current and historical zone information. Here is the type constructor
+prototype:
+{{{
+tzfile(fileobj)
+}}}
+
+Where {{{fileobj}}} is either a filename or a file-like object with
+a {{{read()}}} method.
+
+==== tzfile examples ====
+{{{
+>>> tz = tzfile("/etc/localtime")
+>>> datetime.now(tz)
+datetime.datetime(2003, 9, 27, 12, 3, 48, 392138,
+ tzinfo=tzfile('/etc/localtime'))
+
+>>> datetime.now(tz).astimezone(tzutc())
+datetime.datetime(2003, 9, 27, 15, 3, 53, 70863,
+ tzinfo=tzutc())
+
+>>> datetime.now(tz).tzname()
+'BRST'
+>>> datetime(2003, 1, 1, tzinfo=tz).tzname()
+'BRDT'
+}}}
+
+Check the daylight limit.
+{{{
+>>> tz = tzfile('/usr/share/zoneinfo/EST5EDT')
+>>> datetime(2003, 4, 6, 1, 59, tzinfo=tz).tzname()
+'EST'
+>>> datetime(2003, 4, 6, 2, 00, tzinfo=tz).tzname()
+'EDT'
+>>> datetime(2003, 10, 26, 0, 59, tzinfo=tz).tzname()
+'EDT'
+>>> datetime(2003, 10, 26, 1, 00, tzinfo=tz).tzname()
+'EST'
+}}}
+
+==== tzical type ====
+This type is able to parse
+[ftp://ftp.rfc-editor.org/in-notes/rfc2445.txt iCalendar]
+style {{{VTIMEZONE}}} sessions into a Python timezone object.
+The constuctor prototype is:
+{{{
+tzical(fileobj)
+}}}
+
+Where {{{fileobj}}} is either a filename or a file-like object with
+a {{{read()}}} method.
+
+==== tzical methods ====
+
+ tzical.get(tzid=None)::
+ Since a single iCalendar file may contain more than one timezone,
+ you must ask for the timezone you want with this method. If there's
+ more than one timezone in the parsed file, you'll need to pass the
+ {{{tzid}}} parameter. Otherwise, leaving it empty will yield the only
+ available timezone.
+
+==== tzical examples ====
+Here is a sample file extracted from the RFC. This file defines
+the {{{EST5EDT}}} timezone, and will be used in the following example.
+{{{
+BEGIN:VTIMEZONE
+TZID:US-Eastern
+LAST-MODIFIED:19870101T000000Z
+TZURL:http://zones.stds_r_us.net/tz/US-Eastern
+BEGIN:STANDARD
+DTSTART:19671029T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19870405T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+END:DAYLIGHT
+END:VTIMEZONE
+}}}
+
+And here is an example exploring a {{{tzical}}} type:
+{{{
+>>> from dateutil.tz import *; from datetime import *
+
+>>> tz = tzical('EST5EDT.ics')
+>>> tz.keys()
+['US-Eastern']
+
+>>> est = tz.get('US-Eastern')
+>>> est
+<tzicalvtz 'US-Eastern'>
+
+>>> datetime.now(est)
+datetime.datetime(2003, 10, 6, 19, 44, 18, 667987,
+ tzinfo=<tzicalvtz 'US-Eastern'>)
+
+>>> est == tz.get()
+True
+}}}
+
+Let's check the daylight ranges, as usual:
+{{{
+>>> datetime(2003, 4, 6, 1, 59, tzinfo=est).tzname()
+'EST'
+>>> datetime(2003, 4, 6, 2, 00, tzinfo=est).tzname()
+'EDT'
+
+>>> datetime(2003, 10, 26, 0, 59, tzinfo=est).tzname()
+'EDT'
+>>> datetime(2003, 10, 26, 1, 00, tzinfo=est).tzname()
+'EST'
+}}}
+
+==== tzwin type ====
+This type offers access to internal registry-based Windows timezones.
+The constuctor prototype is:
+{{{
+tzwin(name)
+}}}
+
+Where {{{name}}} is the timezone name. There's a static {{{tzwin.list()}}}
+method to check the available names,
+
+==== tzwin methods ====
+
+ tzwin.display()::
+ This method returns the timezone extended name.
+
+ tzwin.list()::
+ This static method lists all available timezone names.
+
+==== tzwin examples ====
+{{{
+>>> tz = tzwin("E. South America Standard Time")
+}}}
+
+==== tzwinlocal type ====
+This type offers access to internal registry-based Windows timezones.
+The constructor accepts no parameters, so the prototype is:
+{{{
+tzwinlocal()
+}}}
+
+==== tzwinlocal methods ====
+
+ tzwinlocal.display()::
+ This method returns the timezone extended name, and returns
+ {{{None}}} if one is not available.
+
+==== tzwinlocal examples ====
+{{{
+>>> tz = tzwinlocal()
+}}}
+
+==== gettz() function ====
+This function is a helper that will try its best to get the right
+timezone for your environment, or for the given string. The prototype
+is as follows:
+{{{
+gettz(name=None)
+}}}
+
+If given, the parameter may be a filename, a path relative to the base
+of the timezone information path (the base could be
+{{{/usr/share/zoneinfo}}}, for example), a string timezone
+specification, or a timezone abbreviation. If {{{name}}} is not given,
+and the {{{TZ}}} environment variable is set, it's used instead. If the
+parameter is not given, and {{{TZ}}} is not set, the default tzfile
+paths will be tried. Then, if no timezone information is found,
+an internal compiled database of timezones is used. When running
+on Windows, the internal registry-based Windows timezones are also
+considered.
+
+Example:
+{{{
+>>> from dateutil.tz import *
+>>> gettz()
+tzfile('/etc/localtime')
+
+>>> gettz("America/Sao Paulo")
+tzfile('/usr/share/zoneinfo/America/Sao_Paulo')
+
+>>> gettz("EST5EDT")
+tzfile('/usr/share/zoneinfo/EST5EDT')
+
+>>> gettz("EST5")
+tzstr('EST5')
+
+>>> gettz('BRST')
+tzlocal()
+
+>>> os.environ["TZ"] = "America/Sao Paulo"
+>>> gettz()
+tzfile('/usr/share/zoneinfo/America/Sao_Paulo')
+
+>>> os.environ["TZ"] = "BRST"
+>>> gettz()
+tzlocal()
+
+>>> gettz("Unavailable")
+>>>
+}}}
+
+=== zoneinfo ===
+This module provides direct access to the internal compiled
+database of timezones. The timezone data and the compiling tools
+are obtained from the following project:
+
+ http://www.twinsun.com/tz/tz-link.htm
+
+==== gettz() function ====
+This function will try to retrieve the given timezone information
+from the internal compiled database, and will cache its results.
+
+Example:
+{{{
+>>> from dateutil import zoneinfo
+>>> zoneinfo.gettz("Brazil/East")
+tzfile('Brazil/East')
+}}}
+
+## vim:ft=moin
View
9 packages/python-dateutil/dateutil/__init__.py
@@ -0,0 +1,9 @@
+"""
+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"
View
92 packages/python-dateutil/dateutil/easter.py
@@ -0,0 +1,92 @@
+"""
+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
+
+__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:
+
+ http://users.chariot.net.au/~gmarts/eastalg.htm
+
+ and
+
+ http://www.tondering.dk/claus/calendar.html
+
+ """
+
+ 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))
+
View
886 packages/python-dateutil/dateutil/parser.py
@@ -0,0 +1,886 @@
+# -*- 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())