diff options
author | niemeyer <> | 2003-10-09 03:11:46 +0000 |
---|---|---|
committer | niemeyer <> | 2003-10-09 03:11:46 +0000 |
commit | a8102477a48fe0a5ccfebbfbb99c08c7c8157cb3 (patch) | |
tree | a27a3ebf365f457d6a519df8a834c09176f42574 /README | |
parent | cc77d438c8f634fe250f89372f9dccdb6974f125 (diff) | |
download | dateutil-a8102477a48fe0a5ccfebbfbb99c08c7c8157cb3.tar.gz |
- Renamed dateutil.moin to README.
- Other packaging ajustments.
Diffstat (limited to 'README')
-rw-r--r-- | README | 1765 |
1 files changed, 1765 insertions, 0 deletions
@@ -0,0 +1,1765 @@ +## 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, and UTC timezone. + + * Computing of Easter Sunday dates for any given year, + using Western, Orthodox or Julian algorithms; + + * More than 400 test cases. + +== Download == + * attachment:python-dateutil-0.1.tar.bz2 + +== Author == +GustavoNiemeyer <niemeyer@conectiva.com> + +== 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, seconds, microseconds:: + 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 {{{FREQ_YEARLY}}}, {{{FREQ_MONTHLY}}}, +{{{FREQ_WEEKLY}}}, {{{FREQ_DAILY}}}, {{{FREQ_HOURLY}}}, {{{FREQ_MINUTELY}}}, +or {{{FREQ_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 {{{FREQ_YEARLY}}}, an interval of {{{2}}} means + once every two years, but with {{{FREQ_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 + {{{FREQ_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 {{{FREQ_MONTHLY}}}, or with + {{{FREQ_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. + + +==== 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(FREQ_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(FREQ_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(FREQ_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(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)] +}}} + +Everyday in January, for 3 years. +{{{ +>>> list(rrule(FREQ_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(FREQ_DAILY, bymonth=1, dtstart=parse("19980101T090000"), until=parse("20000131T090000"))) +(...) +}}} + +Weekly for 10 occurrences. +{{{ +>>> list(rrule(FREQ_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(FREQ_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(FREQ_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(FREQ_WEEKLY, interval=2, count=8, wkst=SU, byweekday=(TU,TH), dts +tart=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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_MONTHLY, interval=2, count=6, byweekday=TU, dtstart=parse("1 +9970902T090000"))) +[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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_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(FREQ_MONTHLY, count=3, byweekday=(MO,TU,WE,TH,FR), bysetpos=-2, d +tstart=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(FREQ_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(FREQ_MINUTELY, interval=15, count=6, dtstart=parse("19970902T0900 +00"))) +[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(FREQ_MINUTELY, interval=90, count=4, dtstart=parse("19970902T0900 +00"))) +[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(FREQ_MINUTELY, interval=20, count=48, byhour=range(9,17), byminut +e=(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(FREQ_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(FREQ_WEEKLY, interval=2, count=4, byweekday=(TU,SU), wkst=SU, dts +tart=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 examples ==== +Daily, for 7 days, jumping Saturday and Sunday occurrences. +{{{ +>>> set = rruleset() +>>> set.rrule(rrule(FREQ_DAILY, count=7, dtstart=parse("19970902T090000"))) +>>> set.exrule(rrule(FREQ_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(FREQ_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 then 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 +>>> 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' +}}} + +==== 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. + +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") +>>> +}}} + +## vim:ft=moin |