Finding specific date ranges

K

kyosohma

Hi,

I am working on a timesheet application in which I need to to find the
first pay period in a month that is entirely contained in that month
to calculate vacation time. Below are some example date ranges:


December 31, 2006 January 13, 2007 # doesn't earn
January 14, 2007 January 27, 2007 # does earn
January 28, 2007 February 10, 2007 # doesn't
February 11, 2007 February 24, 2007 # does


So far, the best approach I've come up with is to create a list of
tuples that contain the pay period date ranges for the year and
iterate through the tuples looking for the first occurrence of the
month names matching. Then I'd add that date range to a separate list
and somehow ignore any other matches in that month. This seems like a
hack. Does anyone have a better idea?

Thanks in advance!

Mike
 
T

Tim Golden

Hi,

I am working on a timesheet application in which I need to to find the
first pay period in a month that is entirely contained in that month
to calculate vacation time. Below are some example date ranges:


December 31, 2006 January 13, 2007 # doesn't earn
January 14, 2007 January 27, 2007 # does earn
January 28, 2007 February 10, 2007 # doesn't
February 11, 2007 February 24, 2007 # does


So far, the best approach I've come up with is to create a list of
tuples that contain the pay period date ranges for the year and
iterate through the tuples looking for the first occurrence of the
month names matching. Then I'd add that date range to a separate list
and somehow ignore any other matches in that month. This seems like a
hack. Does anyone have a better idea?


Well, I can come up with a solution which basically reflects the
way I'd do it in SQL (since this kind of thing is my bread-and-butter
there) but I'm not convinced it's really any better than your proposal.
However, for the purposes of illustration:

<code>
import calendar
import datetime

YEAR = 2007

months = [
(datetime.date (YEAR, n, 1), datetime.date (YEAR, n, calendar.monthrange (YEAR, n)[1]))
for n in range (1, 13)
]

periods = [
(datetime.date(2006, 12, 31), datetime.date(2007, 1, 13)),
(datetime.date(2007, 1, 14), datetime.date(2007, 1, 27)),
(datetime.date(2007, 1, 28), datetime.date(2007, 2, 10)),
(datetime.date(2007, 2, 11), datetime.date(2007, 2, 24))
]

for m in months:
candidate_periods = [p for p in periods if m[0] <= p[0] and p[1] <= m[1]]
if candidate_periods:
print m[0], "=>", min (candidate_periods)
else:
print m[0], "=>", "no period matches"

</code>

TJG
 
K

kyosohma

I am working on a timesheet application in which I need to to find the
first pay period in a month that is entirely contained in that month
to calculate vacation time. Below are some example date ranges:
December 31, 2006 January 13, 2007 # doesn't earn
January 14, 2007 January 27, 2007 # does earn
January 28, 2007 February 10, 2007 # doesn't
February 11, 2007 February 24, 2007 # does
So far, the best approach I've come up with is to create a list of
tuples that contain the pay period date ranges for the year and
iterate through the tuples looking for the first occurrence of the
month names matching. Then I'd add that date range to a separate list
and somehow ignore any other matches in that month. This seems like a
hack. Does anyone have a better idea?

Well, I can come up with a solution which basically reflects the
way I'd do it in SQL (since this kind of thing is my bread-and-butter
there) but I'm not convinced it's really any better than your proposal.
However, for the purposes of illustration:

<code>
import calendar
import datetime

YEAR = 2007

months = [
(datetime.date (YEAR, n, 1), datetime.date (YEAR, n, calendar.monthrange (YEAR, n)[1]))
for n in range (1, 13)
]

periods = [
(datetime.date(2006, 12, 31), datetime.date(2007, 1, 13)),
(datetime.date(2007, 1, 14), datetime.date(2007, 1, 27)),
(datetime.date(2007, 1, 28), datetime.date(2007, 2, 10)),
(datetime.date(2007, 2, 11), datetime.date(2007, 2, 24))
]

for m in months:
candidate_periods = [p for p in periods if m[0] <= p[0] and p[1] <= m[1]]
if candidate_periods:
print m[0], "=>", min (candidate_periods)
else:
print m[0], "=>", "no period matches"

</code>

TJG

Thanks! I'll try it both ways and see if there's any appreciable
difference in speed, although since it will be packaged into an
executable, that may not be an issue anyway.

Mike
 
T

Tim Golden

Thanks! I'll try it both ways and see if there's any appreciable
difference in speed, although since it will be packaged into an
executable, that may not be an issue anyway.

Mike

I honestly doubt there's any advantage to my approach, certainly
not in terms of speed. It's really only if it happens to suit
your mind better, or take advantage of structures you might
already have in place, etc.

TJG
 
K

kyosohma

I honestly doubt there's any advantage to my approach, certainly
not in terms of speed. It's really only if it happens to suit
your mind better, or take advantage of structures you might
already have in place, etc.

TJG

For completeness, here's my method for comparison's sake:

<code>

date_list = [('12/31/2006', '01/13/2007'),
('01/14/2007', '01/27/2007'),
('01/28/2007', '02/10/2007'),
('02/11/2007', '02/24/2007'),
('02/25/2007', '03/10/2007'),
('03/11/2007', '03/24/2007'),
('03/25/2007', '04/07/2007'),
('04/08/2007', '04/21/2007'),
('04/22/2007', '05/05/2007'),
('05/06/2007', '05/19/2007'),
('05/20/2007', '06/02/2007'),
('06/03/2007', '06/16/2007'),
('06/17/2007', '06/30/2007')
]
vac_periods = []
found = False
for d in date_list:
begin = d[0][0:2]
end = d[1][0:2]
if begin == end and found == False:
vac_periods.append(d)
found = True
else:
found = False
print vac_periods

</code>

Isn't it kind of late in the day over there, Tim? Anyway, your method
is probably clearer to read whereas mine doesn't require anything to
be imported. I didn't even realize there was a calendar module...or
maybe I forgot about it.

Mike
 
T

Tim Golden

For completeness, here's my method for comparison's sake:
[... snipped ...]
Isn't it kind of late in the day over there, Tim?

(It's about 8.30pm as I write this, so still awake :)
Anyway, your method is probably clearer to read whereas mine
> doesn't require anything to be imported.

I think I've been a SQL & Python programmer for so long that
I tend to do anything -- and sometimes too much -- to avoid
messy loop/exit-when structures. On reflection, my code
would be simplified by ignoring all the datetime.date stuff
which is irrelevant for the purpose, since tuples will
compare perfectly well. Also, I had to put the conditional
in the loop because I didn't have data for every month. If
you *knew* you had at least one period for each month and
weren't fussed about, say, date arithmetic later on, it
would be even simpler, just a min () function around
a generator expression.

Frankly, though, with code as small scale as this, it's
far more important what *you* the programmer and any
future maintainers will find easy to understand and use,
and far less important how clever the code is or how
fast it runs.
> I didn't even realize there was a calendar module...or
maybe I forgot about it.

Even now, after some seven years of using Python, I'm
*still* surprised when I read someone's blog article or
mailing list posting referring to a module or application
I never knew about, or had completely forgotten.

TJG
 
K

kyosohma

I think I've been a SQL & Python programmer for so long that
I tend to do anything -- and sometimes too much -- to avoid
messy loop/exit-when structures. On reflection, my code
would be simplified by ignoring all the datetime.date stuff
which is irrelevant for the purpose, since tuples will
compare perfectly well. Also, I had to put the conditional
in the loop because I didn't have data for every month. If
you *knew* you had at least one period for each month and
weren't fussed about, say, date arithmetic later on, it
would be even simpler, just a min () function around
a generator expression.


I'm getting pretty sick of for loops, flags and/or sentinel values. I
do *know* that there should be one (and only one) period per month
that applies. I just have to grab that period. I currently calculate
the date ranges on the fly based on the last date in the 2-value pair.
Ugh.

I've read an re-read about generators, but they seem to cause my mind
to go blank. It's like the related with statement or decorators. I
just don't understand them well enough to implement them.

Frankly, though, with code as small scale as this, it's
far more important what *you* the programmer and any
future maintainers will find easy to understand and use,
and far less important how clever the code is or how
fast it runs.

I agree. The simpler the code is to read and maintain, the better.
Clever, short code may be cool, but it can also be a pain to
troubleshoot.

Mike
 
Z

Zentrader

December 31, 2006 January 13, 2007 # doesn't earn
January 14, 2007 January 27, 2007 # does earn
January 28, 2007 February 10, 2007 # doesn't
February 11, 2007 February 24, 2007 # does

Am I over simplifying if I say that since it appears to be a two week
pay period, the date has to be greater than the 11th unless the first,
or first and second, are on a weekend, in which case it would be > 12
or > 13? Or a reasonable facsimile thereof, depending on whether or
not the two week period is Saturday through Friday.
 
T

Tim Golden

Zentrader said:
Am I over simplifying if I say that since it appears to be a two week
pay period, the date has to be greater than the 11th unless the first,
or first and second, are on a weekend, in which case it would be > 12
or > 13? Or a reasonable facsimile thereof, depending on whether or
not the two week period is Saturday through Friday.

I think it's one of those things where the neatest answer
could well depend on the sort of heuristic you mention. As
a rule, when I come across this kind of requirement, I tend
to put the most general solution in place, unless a *real*
optimisation is clearly called for. In my experience, this
makes it much easier for the next person who looks at the
code, typically years later, even if that's me!

(This is has just happened to me this week, having to review
a date-related calculation to do the repost frequency of the
adverts my company deals with. I wrote the original code five
years ago, and commented it intelligently, but I *still* had
to work through the code twice when we had a problem with a
particular cycle!)

TJG
 
K

kyosohma

Am I over simplifying if I say that since it appears to be a two week
pay period, the date has to be greater than the 11th unless the first,
or first and second, are on a weekend, in which case it would be > 12
or > 13? Or a reasonable facsimile thereof, depending on whether or
not the two week period is Saturday through Friday.

Yeah it is a 2-week period, but the starting day can change depending
on whether or not you're salaried. I haven't even looked at how we
handle unions. Ugh. But yes, you are correct.

Mike
 
K

kyosohma

I think it's one of those things where the neatest answer
could well depend on the sort of heuristic you mention. As
a rule, when I come across this kind of requirement, I tend
to put the most general solution in place, unless a *real*
optimisation is clearly called for. In my experience, this
makes it much easier for the next person who looks at the
code, typically years later, even if that's me!

(This is has just happened to me this week, having to review
a date-related calculation to do the repost frequency of the
adverts my company deals with. I wrote the original code five
years ago, and commented it intelligently, but I *still* had
to work through the code twice when we had a problem with a
particular cycle!)

TJG

I think it's foolish NOT to comment code unless it's very well self-
documented. Even then, a couple lines of comments can be helpful. I've
had to translate a bunch of Kixtart code into Python and none of it
was commented and it wasn't well formed code either.

And I've had the same thing happen with code that's only a year old.
I'll read it and then go "what the?!" It's just not possible to
remember every line of code you write. I wish it was.

Mike
 
?

=?ISO-8859-1?Q?Ricardo_Ar=E1oz?=

What about (untested) :
import datetime

def itEarns(fromDate, toDate) :
return (fromDate.year == toDate.year and fromDate.month == toDate.month)

periods = [
(datetime.date(2006, 12, 31), datetime.date(2007, 1, 13)),
(datetime.date(2007, 1, 14), datetime.date(2007, 1, 27)),
(datetime.date(2007, 1, 28), datetime.date(2007, 2, 10)),
(datetime.date(2007, 2, 11), datetime.date(2007, 2, 24))
]

candidatePeriods = [(frm, to) for frm, to in periods if itEarns(frm, to)]

??
So far, the best approach I've come up with is to create a list of
tuples that contain the pay period date ranges for the year and
iterate through the tuples looking for the first occurrence of the
month names matching. Then I'd add that date range to a separate list
and somehow ignore any other matches in that month. This seems like a
hack. Does anyone have a better idea?


Well, I can come up with a solution which basically reflects the
way I'd do it in SQL (since this kind of thing is my bread-and-butter
there) but I'm not convinced it's really any better than your proposal.
However, for the purposes of illustration:

<code>
import calendar
import datetime

YEAR = 2007

months = [
(datetime.date (YEAR, n, 1), datetime.date (YEAR, n, calendar.monthrange (YEAR, n)[1]))
for n in range (1, 13)
]

periods = [
(datetime.date(2006, 12, 31), datetime.date(2007, 1, 13)),
(datetime.date(2007, 1, 14), datetime.date(2007, 1, 27)),
(datetime.date(2007, 1, 28), datetime.date(2007, 2, 10)),
(datetime.date(2007, 2, 11), datetime.date(2007, 2, 24))
]

for m in months:
candidate_periods = [p for p in periods if m[0] <= p[0] and p[1] <= m[1]]
if candidate_periods:
print m[0], "=>", min (candidate_periods)
else:
print m[0], "=>", "no period matches"

</code>

TJG
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top