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