OLD | NEW |
1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. | 1 # copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr | 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
3 # | 3 # |
4 # This file is part of logilab-common. | 4 # This file is part of logilab-common. |
5 # | 5 # |
6 # logilab-common is free software: you can redistribute it and/or modify it unde
r | 6 # logilab-common is free software: you can redistribute it and/or modify it unde
r |
7 # the terms of the GNU Lesser General Public License as published by the Free | 7 # the terms of the GNU Lesser General Public License as published by the Free |
8 # Software Foundation, either version 2.1 of the License, or (at your option) an
y | 8 # Software Foundation, either version 2.1 of the License, or (at your option) an
y |
9 # later version. | 9 # later version. |
10 # | 10 # |
11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT | 11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT |
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | 13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
14 # details. | 14 # details. |
15 # | 15 # |
16 # You should have received a copy of the GNU Lesser General Public License along | 16 # You should have received a copy of the GNU Lesser General Public License along |
17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>. | 17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>. |
18 """Date manipulation helper functions.""" | 18 """Date manipulation helper functions.""" |
19 from __future__ import division | 19 from __future__ import division |
20 | 20 |
21 __docformat__ = "restructuredtext en" | 21 __docformat__ = "restructuredtext en" |
22 | 22 |
23 import math | 23 import math |
24 import re | 24 import re |
25 from locale import getpreferredencoding | 25 import sys |
| 26 from locale import getlocale, LC_TIME |
26 from datetime import date, time, datetime, timedelta | 27 from datetime import date, time, datetime, timedelta |
27 from time import strptime as time_strptime | 28 from time import strptime as time_strptime |
28 from calendar import monthrange, timegm | 29 from calendar import monthrange, timegm |
29 | 30 |
| 31 from six.moves import range |
| 32 |
30 try: | 33 try: |
31 from mx.DateTime import RelativeDateTime, Date, DateTimeType | 34 from mx.DateTime import RelativeDateTime, Date, DateTimeType |
32 except ImportError: | 35 except ImportError: |
33 endOfMonth = None | 36 endOfMonth = None |
34 DateTimeType = datetime | 37 DateTimeType = datetime |
35 else: | 38 else: |
36 endOfMonth = RelativeDateTime(months=1, day=-1) | 39 endOfMonth = RelativeDateTime(months=1, day=-1) |
37 | 40 |
38 # NOTE: should we implement a compatibility layer between date representations | 41 # NOTE: should we implement a compatibility layer between date representations |
39 # as we have in lgc.db ? | 42 # as we have in lgc.db ? |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 return delta.days | 126 return delta.days |
124 else: | 127 else: |
125 return int(math.ceil((end - start).days)) | 128 return int(math.ceil((end - start).days)) |
126 | 129 |
127 def get_national_holidays(begin, end): | 130 def get_national_holidays(begin, end): |
128 """return french national days off between begin and end""" | 131 """return french national days off between begin and end""" |
129 begin = datefactory(begin.year, begin.month, begin.day, begin) | 132 begin = datefactory(begin.year, begin.month, begin.day, begin) |
130 end = datefactory(end.year, end.month, end.day, end) | 133 end = datefactory(end.year, end.month, end.day, end) |
131 holidays = [str2date(datestr, begin) | 134 holidays = [str2date(datestr, begin) |
132 for datestr in FRENCH_MOBILE_HOLIDAYS.values()] | 135 for datestr in FRENCH_MOBILE_HOLIDAYS.values()] |
133 for year in xrange(begin.year, end.year+1): | 136 for year in range(begin.year, end.year+1): |
134 for datestr in FRENCH_FIXED_HOLIDAYS.values(): | 137 for datestr in FRENCH_FIXED_HOLIDAYS.values(): |
135 date = str2date(datestr % year, begin) | 138 date = str2date(datestr % year, begin) |
136 if date not in holidays: | 139 if date not in holidays: |
137 holidays.append(date) | 140 holidays.append(date) |
138 return [day for day in holidays if begin <= day < end] | 141 return [day for day in holidays if begin <= day < end] |
139 | 142 |
140 def add_days_worked(start, days): | 143 def add_days_worked(start, days): |
141 """adds date but try to only take days worked into account""" | 144 """adds date but try to only take days worked into account""" |
142 step = get_step(start) | 145 step = get_step(start) |
143 weeks, plus = divmod(days, 5) | 146 weeks, plus = divmod(days, 5) |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 | 183 |
181 When using mx datetime, you should *NOT* use incmonth argument, use instead | 184 When using mx datetime, you should *NOT* use incmonth argument, use instead |
182 oneDay, oneHour, oneMinute, oneSecond, oneWeek or endOfMonth (to enumerate | 185 oneDay, oneHour, oneMinute, oneSecond, oneWeek or endOfMonth (to enumerate |
183 months) as `incday` argument | 186 months) as `incday` argument |
184 """ | 187 """ |
185 assert not (incday and incmonth) | 188 assert not (incday and incmonth) |
186 begin = todate(begin) | 189 begin = todate(begin) |
187 end = todate(end) | 190 end = todate(end) |
188 if incmonth: | 191 if incmonth: |
189 while begin < end: | 192 while begin < end: |
| 193 yield begin |
190 begin = next_month(begin, incmonth) | 194 begin = next_month(begin, incmonth) |
191 yield begin | |
192 else: | 195 else: |
193 incr = get_step(begin, incday or 1) | 196 incr = get_step(begin, incday or 1) |
194 while begin < end: | 197 while begin < end: |
195 yield begin | 198 yield begin |
196 begin += incr | 199 begin += incr |
197 | 200 |
198 # makes py datetime usable ##################################################### | 201 # makes py datetime usable ##################################################### |
199 | 202 |
200 ONEDAY = timedelta(days=1) | 203 ONEDAY = timedelta(days=1) |
201 ONEWEEK = timedelta(days=7) | 204 ONEWEEK = timedelta(days=7) |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 return somedate | 275 return somedate |
273 | 276 |
274 def first_day(somedate): | 277 def first_day(somedate): |
275 return date(somedate.year, somedate.month, 1) | 278 return date(somedate.year, somedate.month, 1) |
276 | 279 |
277 def last_day(somedate): | 280 def last_day(somedate): |
278 return date(somedate.year, somedate.month, days_in_month(somedate)) | 281 return date(somedate.year, somedate.month, days_in_month(somedate)) |
279 | 282 |
280 def ustrftime(somedate, fmt='%Y-%m-%d'): | 283 def ustrftime(somedate, fmt='%Y-%m-%d'): |
281 """like strftime, but returns a unicode string instead of an encoded | 284 """like strftime, but returns a unicode string instead of an encoded |
282 string which' may be problematic with localized date. | 285 string which may be problematic with localized date. |
283 | |
284 encoding is guessed by locale.getpreferredencoding() | |
285 """ | 286 """ |
286 encoding = getpreferredencoding(do_setlocale=False) or 'UTF-8' | 287 if sys.version_info >= (3, 3): |
287 try: | 288 # datetime.date.strftime() supports dates since year 1 in Python >=3.3. |
288 return unicode(somedate.strftime(str(fmt)), encoding) | 289 return somedate.strftime(fmt) |
289 except ValueError, exc: | 290 else: |
290 if somedate.year >= 1900: | 291 try: |
291 raise | 292 if sys.version_info < (3, 0): |
292 # datetime is not happy with dates before 1900 | 293 encoding = getlocale(LC_TIME)[1] or 'ascii' |
293 # we try to work around this, assuming a simple | 294 return unicode(somedate.strftime(str(fmt)), encoding) |
294 # format string | 295 else: |
295 fields = {'Y': somedate.year, | 296 return somedate.strftime(fmt) |
296 'm': somedate.month, | 297 except ValueError: |
297 'd': somedate.day, | 298 if somedate.year >= 1900: |
298 } | 299 raise |
299 if isinstance(somedate, datetime): | 300 # datetime is not happy with dates before 1900 |
300 fields.update({'H': somedate.hour, | 301 # we try to work around this, assuming a simple |
301 'M': somedate.minute, | 302 # format string |
302 'S': somedate.second}) | 303 fields = {'Y': somedate.year, |
303 fmt = re.sub('%([YmdHMS])', r'%(\1)02d', fmt) | 304 'm': somedate.month, |
304 return unicode(fmt) % fields | 305 'd': somedate.day, |
| 306 } |
| 307 if isinstance(somedate, datetime): |
| 308 fields.update({'H': somedate.hour, |
| 309 'M': somedate.minute, |
| 310 'S': somedate.second}) |
| 311 fmt = re.sub('%([YmdHMS])', r'%(\1)02d', fmt) |
| 312 return unicode(fmt) % fields |
305 | 313 |
306 def utcdatetime(dt): | 314 def utcdatetime(dt): |
307 if dt.tzinfo is None: | 315 if dt.tzinfo is None: |
308 return dt | 316 return dt |
309 return datetime(*dt.utctimetuple()[:7]) | 317 return datetime(*dt.utctimetuple()[:7]) |
310 | 318 |
311 def utctime(dt): | 319 def utctime(dt): |
312 if dt.tzinfo is None: | 320 if dt.tzinfo is None: |
313 return dt | 321 return dt |
314 return (dt + dt.utcoffset() + dt.dst()).replace(tzinfo=None) | 322 return (dt + dt.utcoffset() + dt.dst()).replace(tzinfo=None) |
315 | 323 |
316 def datetime_to_seconds(date): | 324 def datetime_to_seconds(date): |
317 """return the number of seconds since the begining of the day for that date | 325 """return the number of seconds since the begining of the day for that date |
318 """ | 326 """ |
319 return date.second+60*date.minute + 3600*date.hour | 327 return date.second+60*date.minute + 3600*date.hour |
320 | 328 |
321 def timedelta_to_days(delta): | 329 def timedelta_to_days(delta): |
322 """return the time delta as a number of seconds""" | 330 """return the time delta as a number of seconds""" |
323 return delta.days + delta.seconds / (3600*24) | 331 return delta.days + delta.seconds / (3600*24) |
324 | 332 |
325 def timedelta_to_seconds(delta): | 333 def timedelta_to_seconds(delta): |
326 """return the time delta as a fraction of days""" | 334 """return the time delta as a fraction of days""" |
327 return delta.days*(3600*24) + delta.seconds | 335 return delta.days*(3600*24) + delta.seconds |
OLD | NEW |