I would like to point out an error with the #820 fix on the function
parse_runtime_limit() in datetutils with respect to reversed time ranges.
I encountered the same error on my side a few days ago and I fixed it on my
cloned repo.
The other day I made a pull from the public repo and saw that the fix was
published (f288e56cfa99f3b9aab580e82158940485fd3b9a)
The thing is that I tested it and it behaves quite strangely.
The problem impacts the function register_emergency() from errorlib since
it calls get_emergency_recipients() that calls parse_runtime_limit()
And here is a quick test to show it :
Define the variable CFG_SITE_EMERGENCY_EMAIL_ADDRESSES.
sudo -u www-data vim /opt/invenio/etc/invenio-local.conf
Add or modify the following line
CFG_SITE_EMERGENCY_EMAIL_ADDRESSES =
{"23:00-01:00":"<username>@localhost"}
Save and quit.
Reload the conf
sudo -u www-data /opt/invenio/bin/inveniocfg --update-all
Launch the following from a shell and watch for your mails
python << EOF
import time
from invenio.errorlib import register_emergency
time.time = lambda: 1329088800.0 # Mock time to 'Mon,13 Feb
2012 00:20:00 +0000'
register_emergency("This mail you should receive but you will not.
Unless you have the #820 fix")
time.time = lambda: 1329088800.0-60*60 # Mock time to 'Sun, 12 Feb
2012 23:20:00 +0000'
register_emergency("This mail you will receive. I bet you missed the
previous one. You will not have it with the #820 fix though, even if you
should")
EOF
Before any fix only the second mail was received.
With the current #820 fix only the first one is received.
With a quick fix of mine both mails are received.
Another easy way to see the problem is to run this python code :
import from invenio.dateutils import parse_runtime_limit
def tidy_parse(runtime):
parsed_runtime=parse_runtime_limit(runtime)
map_func = lambda x:time.strftime("%a, %d %b %Y %H:%M:%S
+0000",time.localtime(x))
return map(map_func,parsed_runtime[0]), map(map_func, parsed_runtime[1])
tidy_parse("23:00-01:00")[0]
With the current fix here is what you get
>>> tidy_parse("Mon 23:00-01:00")[0]
['Sun, 12 Feb 2012 23:00:00 +0000', 'Mon, 13 Feb 2012 01:00:00 +0000']
And for friday at 12:00 for instance
>>> tidy_parse("11:00-05:00")[0]
['Thu, 16 Feb 2012 11:00:00 +0000', 'Fri, 17 Feb 2012 05:00:00 +0000']
And I guess this is not what we want.
Here is my patch with respect to the master
3d86a43c05bb17169265a58531db07154c8feec3.
It makes parse_runtime_limit take into consideration not only the date but
also the current time so that the function docstring is respected.
"The function will return two valid time ranges.
The first could be in the past, containing the present or in the future.
The second is always in the future."
diff --git a/modules/miscutil/lib/dateutils.py
b/modules/miscutil/lib/dateutils.py
index 92c7ac3..8fe66db 100644
--- a/modules/miscutil/lib/dateutils.py
+++ b/modules/miscutil/lib/dateutils.py
@@ -337,31 +337,36 @@ def parse_runtime_limit(value):
if not g:
raise ValueError
pieces = g.groupdict()
+
+ if pieces['begin'] is None:
+ pieces['begin'] = '00:00'
+ if pieces['end'] is None:
+ pieces['end'] = '00:00'
+ beginning_time = extract_time(pieces['begin'])
+ ending_time = extract_time(pieces['end'])
+
today_weekday = today.isoweekday() - 1
if pieces['weekday'] is None:
## No weekday specified. So either today or tomorrow
first_occasion_day = 0
next_occasion_day = 24 * 3600
+
+ if beginning_time >= ending_time:
+ now_time = ( today.hour * 60 + today.minute) *60 +
today.second
+ if now_time <= ending_time:
+ ## Time range is reversed and we are still before the
ending time. The first day is yesterday
+ first_occasion_day = -1*24*3600
+ next_occasion_day = 0
else:
## Weekday specified. So either this week or next
weekday = extract_weekday(pieces['weekday'])
first_occasion_day = -((today_weekday - weekday) % 7) * 24 *
3600
next_occasion_day = first_occasion_day + 7 * 24 * 3600
- if pieces['begin'] is None:
- pieces['begin'] = '00:00'
- if pieces['end'] is None:
- pieces['end'] = '00:00'
-
- beginning_time = extract_time(pieces['begin'])
- ending_time = extract_time(pieces['end'])
-
- if not ending_time:
+ if beginning_time >= ending_time:
+ ## end < begin we add a 24-hours watch tour.
ending_time += 24 * 3600
- elif beginning_time and ending_time:
- if beginning_time > ending_time:
- beginning_time -= 24 * 3600
reference_time = time.mktime(datetime.datetime(today.year,
today.month, today.day).timetuple())
current_range = (
diff --git a/modules/miscutil/lib/dateutils_tests.py
b/modules/miscutil/lib/dateutils_tests.py
index d3d019a..1ad1b64 100644
--- a/modules/miscutil/lib/dateutils_tests.py
+++ b/modules/miscutil/lib/dateutils_tests.py
@@ -187,6 +187,44 @@ class ParseRuntimeLimitTest(unittest.TestCase):
result = dateutils.parse_runtime_limit(limit)
self.assertEqual(expected, result)
+ def test_parse_runtime_limit_reversed_times_only(self):
+ """dateutils - parse runtime using just a reversed time range"""
+ limit = '18:00-06:00'
+ day = datetime.date.today()
+ now = datetime.time()
+ today_4am = time.mktime(datetime.datetime.combine(day,
now.replace(hour=4)).timetuple())
+ today_8am = time.mktime(datetime.datetime.combine(day,
now.replace(hour=8)).timetuple())
+ today_8pm = time.mktime(datetime.datetime.combine(day,
now.replace(hour=20)).timetuple())
+ present_from = datetime.datetime.combine(day, now.replace(hour=18))
+ present_to = datetime.datetime.combine(day, now.replace(hour=6))
+ yesterday_from = present_from + datetime.timedelta(days=-1)
+ future_from = present_from + datetime.timedelta(days=1)
+ future_to = present_to + datetime.timedelta(days=1)
+ the_day_after_to = present_to + datetime.timedelta(days=2)
+ expected_4am = (
+ (time.mktime(yesterday_from.timetuple()),
time.mktime(present_to.timetuple())),
+ (time.mktime(present_from.timetuple()),
time.mktime(future_to.timetuple())),
+ )
+ expected_8pm = expected_8am = (
+ (time.mktime(present_from.timetuple()),
time.mktime(future_to.timetuple())),
+ (time.mktime(future_from.timetuple()),
time.mktime(the_day_after_to.timetuple())),
+ )
+ real_time = time.time # Saves the real time.time
+ time.time = lambda: today_4am # Fakes time to 4am this morning
+ result = dateutils.parse_runtime_limit(limit)
+ time.time=real_time # Restores the real time.time
+ self.assertEqual(expected_4am, result)
+
+ time.time = lambda: today_8am # Fakes time to 8am this morning
+ result = dateutils.parse_runtime_limit(limit)
+ time.time=real_time
+ self.assertEqual(expected_8am, result)
+
+ time.time = lambda: today_8pm # Fakes time to 8pm
+ result = dateutils.parse_runtime_limit(limit)
+ time.time=real_time
+ self.assertEqual(expected_8pm, result)
+
TEST_SUITE = make_test_suite(ConvertFromDateCVSTest,
ConvertIntoDateGUITest,
ParseRuntimeLimitTest,)
diff --git a/modules/miscutil/lib/errorlib_tests.py
b/modules/miscutil/lib/errorlib_tests.py
index 015aa3a..4f330a0 100644
--- a/modules/miscutil/lib/errorlib_tests.py
+++ b/modules/miscutil/lib/errorlib_tests.py
@@ -34,11 +34,17 @@ class TestGetEmergencyRecipients(unittest.TestCase):
now = datetime.datetime.today()
tomorrow = now + datetime.timedelta(days=1)
+ yesterday = now + datetime.timedelta(days=-1)
diff_day = now + datetime.timedelta(days=4)
later = now.replace(hour=(now.hour + 1) % 24)
earlier = now.replace(hour=(now.hour - 1) % 24)
+
+ constraint_now_day = now
+ if earlier >= later :
+ # Handles the case when the test is run at midnight
+ constraint_now_day = yesterday
constraint_now = "%s %s-%s" % (
- now.strftime("%a"),
+ constraint_now_day.strftime("%a"),
earlier.strftime("%H:00"),
later.strftime("%H:00"),
)
Any thought on that ?
Regards.