| 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 |