|
period.py
This is an early version of a basic time period
checking libary for Python. It is in part inspired by perl's
Time::Period module and the time class mechanism of
Cfengine.
There was a major change at version 0.5 in how the
code works. Previous to that you couldn't do any grouping. In order
to add grouping, I had to completely change how the parser and the
logic of the system works. All tests show that both versions give the
same answers, at least for the test cases.
The purpose of this library is simply to allow you
to specify a time period and check to see if some time is within that
time period. By default, it checks against the current computer time.
You can also check to see if a time is on a holiday.
Time Period Specifications
The time period specifications are reasonably
straightforward, but there are a few tricky bits. It understands the
following time units:
- Hr00 - Hr23
- Min00 - Min59
- Yr1999, Yr2000, etc
- January, February, etc. and ranges February-September
- Monday, Tuesday, etc. and ranges Wednesday-Saturday
- Day01 - Day31
- Week00 - Week52
- Weekday, Weekend
- Always, Never
Now if you had some Python script you didn't ever
want to run on a weekend, you could do this:
from period import in_period
import sys
if in_period('Weekend'):
sys.exit(0) # Only run on weekdays!
You can negate a time unit with a bang
(! ), so you could also say this, a bit
cryptically:
from period import in_period
import sys
if in_period('!Weekday'):
sys.exit(0) # Don't run on weekends!
You can combine time units into more complex period
specifications with a dot (it's confusing to call it a "period" in
this context). In this case, everything in the period specification
must be true for in_period to return true. So,
April.Weekend will be true for every weekend in
April.
In addition to these simple period units, you can
express ranges of times for the ones that use digits. Ranges are
indicated with a '- ' dash. So,
Hr07-Hr21 is true from 7am to 9pm. You can easily
specify your normal working hours, or at something least very close to
them: Weekday.Hr07-Hr16 .
Please note very carefully: time ranges are
inclusive. So, Hr01-Hr03 includes all of
1:00-3:59. If you only want to specify 1:00 to 3:00, then you need
instead to say Hr01-Hr02 which includes 1:00-2:59.
This is inclusive range behavior is true for the
minute, day, year and week ranges as well.
You can also use ranges for days of the week and
months. These will wrap around, so if you do
November-March , you'll get what you expect. Same
for day ranges: Friday-Tuesday will match on
Friday, Saturday, Sunday, Monday and Tuesday.
If you want, you can omit the unit part from the
second part of a range. So, Hr07-Hr16 and
Hr07-16 are effectively the same.
If you want leave your lunch hour out of the period
above you have to use a slightly different notation. If you said
Weekday.Hr07-12.Hr13-16 this will fail, because
one or the other of the Hr periods will fail. So,
to indicate several ranges of these, you use a comma. The correct
period is Weekday.Hr07-11,13-16 . Note that
Hr12 would everything between 12:00 and 12:59, so
we need to get that entire hour out of the time spec.
Hr07-11,12-16 is exactly the same as
Hr07-16 .
In addition to logical "and" you can join periods
with "or" with the | character (that's a pipe):
Weekday|Weekend.Hr10-22 will be true if it's a
weekday or between 10am and 10pm on a weekend. Note that the "and"
operation (the dot) has higher precedence than the "or" (the pipe).
Finally, you may do some grouping with parentheses,
which work as you'd expect: (Monday|Friday).Hr11-14
or more complex expressions with nested sub-expressions:
(Monday|(Friday.January-March)).Yr2002
You can also negate groups by prefixing the bang:
!(Monday|Friday).Hr11-14 .
If you wish to check some time other than the
current computer time, just pass that time as a second argument to
in_period :
from period import in_period
import sys, time
# 86400 == 60 * 60 * 24, a day of seconds
if in_period('Weekend', time.time() + 86400):
sys.exit(0) # Don't run if tomorrow is a weekend.
The time specifications Always
and Never always return true and false
respectively.
Holidays
There is another function in the period library,
is_holiday . This will attempt to consult a SYSV
accounting holidays file. By default, it checks in
/etc/acct/holidays , but you can specify other
locations.
The format of the holidays file is fairly strict.
All comment lines must begin with a star, * . The
first non-comment line must be the year, prime-time start and
prime-time end. All following non-comment lines take the format
"month/day name-of-holiday" where only the single
month/day token is significant. So, here's our
example:
* BCG Holidays
*
* Curr Prime Non-Prime
* Year Start Start
*
2002 0700 1800
*
* only the first column (month/day) is significiant.
*
* month/day Holiday name or comment.
*
1/1 New Years Day
* 1/2 A holiday for testing.
1/15 Martin Luther King, Jr. Day
7/4 Independence Day
11/24 Thanksgiving Day
12/25 Christmas
If you happen to have turned on SYSV accounting on a
system, it will start griping several days before the new year to
remind you to update the holidays file. So, if you aren't using SYSV
accounting, that file may be several years out of date. The
is_holiday function will ignore an out of
date holidays file, and will always report that a day is not a holiday
until the file is fixed.
If you don't use a system that has SYSV accounting,
you can specify the location of a central holidays file in that format
just fine:
from period import is_holiday, in_period
if in_period('Weekend') or is_holiday(holidays="/export/etc/holidays"):
sys.exit(0)
You can also specify some other time to check:
from period import in_period
import sys, time
# 86400 == 60 * 60 * 24, a day of seconds
tomorrow = time.time() + 86400
if in_period('Weekend', tomorrow) or is_holiday(now=tomorrow):
sys.exit(0) # Don't run if tomorrow is a weekend or a holiday.
BUGS
Everything is case sensitive.
Doesn't use distutils to install.
Send questions and problems to
William S. Annis.
|