1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """Date manipulation helper functions."""
19 from __future__ import division
20
21 __docformat__ = "restructuredtext en"
22
23 import math
24 import re
25 import sys
26 from locale import getlocale, LC_TIME
27 from datetime import date, time, datetime, timedelta
28 from time import strptime as time_strptime
29 from calendar import monthrange, timegm
30
31 from six.moves import range
32
33 try:
34 from mx.DateTime import RelativeDateTime, Date, DateTimeType
35 except ImportError:
36 endOfMonth = None
37 DateTimeType = datetime
38 else:
39 endOfMonth = RelativeDateTime(months=1, day=-1)
40
41
42
43
44 FRENCH_FIXED_HOLIDAYS = {
45 'jour_an': '%s-01-01',
46 'fete_travail': '%s-05-01',
47 'armistice1945': '%s-05-08',
48 'fete_nat': '%s-07-14',
49 'assomption': '%s-08-15',
50 'toussaint': '%s-11-01',
51 'armistice1918': '%s-11-11',
52 'noel': '%s-12-25',
53 }
54
55 FRENCH_MOBILE_HOLIDAYS = {
56 'paques2004': '2004-04-12',
57 'ascension2004': '2004-05-20',
58 'pentecote2004': '2004-05-31',
59
60 'paques2005': '2005-03-28',
61 'ascension2005': '2005-05-05',
62 'pentecote2005': '2005-05-16',
63
64 'paques2006': '2006-04-17',
65 'ascension2006': '2006-05-25',
66 'pentecote2006': '2006-06-05',
67
68 'paques2007': '2007-04-09',
69 'ascension2007': '2007-05-17',
70 'pentecote2007': '2007-05-28',
71
72 'paques2008': '2008-03-24',
73 'ascension2008': '2008-05-01',
74 'pentecote2008': '2008-05-12',
75
76 'paques2009': '2009-04-13',
77 'ascension2009': '2009-05-21',
78 'pentecote2009': '2009-06-01',
79
80 'paques2010': '2010-04-05',
81 'ascension2010': '2010-05-13',
82 'pentecote2010': '2010-05-24',
83
84 'paques2011': '2011-04-25',
85 'ascension2011': '2011-06-02',
86 'pentecote2011': '2011-06-13',
87
88 'paques2012': '2012-04-09',
89 'ascension2012': '2012-05-17',
90 'pentecote2012': '2012-05-28',
91 }
92
93
94
96
97 if isinstance(dateobj, date):
98 return ONEDAY * nbdays
99 return nbdays
100
102
103 if isinstance(sampledate, datetime):
104 return datetime(year, month, day)
105 if isinstance(sampledate, date):
106 return date(year, month, day)
107 return Date(year, month, day)
108
110
111 if isinstance(dateobj, date):
112 return dateobj.weekday()
113 return dateobj.day_of_week
114
116
117 year, month, day = [int(chunk) for chunk in datestr.split('-')]
118 return datefactory(year, month, day, sampledate)
119
121 if isinstance(start, date):
122 delta = end - start
123
124 if delta.seconds:
125 return delta.days + 1
126 return delta.days
127 else:
128 return int(math.ceil((end - start).days))
129
131 """return french national days off between begin and end"""
132 begin = datefactory(begin.year, begin.month, begin.day, begin)
133 end = datefactory(end.year, end.month, end.day, end)
134 holidays = [str2date(datestr, begin)
135 for datestr in FRENCH_MOBILE_HOLIDAYS.values()]
136 for year in range(begin.year, end.year+1):
137 for datestr in FRENCH_FIXED_HOLIDAYS.values():
138 date = str2date(datestr % year, begin)
139 if date not in holidays:
140 holidays.append(date)
141 return [day for day in holidays if begin <= day < end]
142
144 """adds date but try to only take days worked into account"""
145 step = get_step(start)
146 weeks, plus = divmod(days, 5)
147 end = start + ((weeks * 7) + plus) * step
148 if weekday(end) >= 5:
149 end += (2 * step)
150 end += len([x for x in get_national_holidays(start, end + step)
151 if weekday(x) < 5]) * step
152 if weekday(end) >= 5:
153 end += (2 * step)
154 return end
155
172
173 -def date_range(begin, end, incday=None, incmonth=None):
174 """yields each date between begin and end
175
176 :param begin: the start date
177 :param end: the end date
178 :param incr: the step to use to iterate over dates. Default is
179 one day.
180 :param include: None (means no exclusion) or a function taking a
181 date as parameter, and returning True if the date
182 should be included.
183
184 When using mx datetime, you should *NOT* use incmonth argument, use instead
185 oneDay, oneHour, oneMinute, oneSecond, oneWeek or endOfMonth (to enumerate
186 months) as `incday` argument
187 """
188 assert not (incday and incmonth)
189 begin = todate(begin)
190 end = todate(end)
191 if incmonth:
192 while begin < end:
193 yield begin
194 begin = next_month(begin, incmonth)
195 else:
196 incr = get_step(begin, incday or 1)
197 while begin < end:
198 yield begin
199 begin += incr
200
201
202
203 ONEDAY = timedelta(days=1)
204 ONEWEEK = timedelta(days=7)
205
206 try:
207 strptime = datetime.strptime
208 except AttributeError:
209 from time import strptime as time_strptime
211 return datetime(*time_strptime(value, format)[:6])
212
214 return time(*time_strptime(value, format)[3:6])
215
217 """return a date from a date (leaving unchanged) or a datetime"""
218 if isinstance(somedate, datetime):
219 return date(somedate.year, somedate.month, somedate.day)
220 assert isinstance(somedate, (date, DateTimeType)), repr(somedate)
221 return somedate
222
224 """return a time from a time (leaving unchanged), date or datetime"""
225
226 if not isinstance(somedate, time):
227 return time(somedate.hour, somedate.minute, somedate.second)
228 assert isinstance(somedate, (time)), repr(somedate)
229 return somedate
230
232 """return a date from a date (leaving unchanged) or a datetime"""
233
234 if isinstance(somedate, datetime):
235 return somedate
236 assert isinstance(somedate, (date, DateTimeType)), repr(somedate)
237 return datetime(somedate.year, somedate.month, somedate.day)
238
240 return timegm(somedate.timetuple()) * 1000 + int(getattr(somedate, 'microsecond', 0) / 1000)
241
243 miliseconds, microseconds = divmod(ticks, 1000)
244 try:
245 return datetime.fromtimestamp(miliseconds)
246 except (ValueError, OverflowError):
247 epoch = datetime.fromtimestamp(0)
248 nb_days, seconds = divmod(int(miliseconds), 86400)
249 delta = timedelta(nb_days, seconds=seconds, microseconds=microseconds)
250 try:
251 return epoch + delta
252 except (ValueError, OverflowError):
253 raise
254
256 return monthrange(somedate.year, somedate.month)[1]
257
259 feb = date(somedate.year, 2, 1)
260 if days_in_month(feb) == 29:
261 return 366
262 else:
263 return 365
264
266 while nbmonth:
267 somedate = first_day(somedate) - ONEDAY
268 nbmonth -= 1
269 return somedate
270
272 while nbmonth:
273 somedate = last_day(somedate) + ONEDAY
274 nbmonth -= 1
275 return somedate
276
278 return date(somedate.year, somedate.month, 1)
279
282
284 """like strftime, but returns a unicode string instead of an encoded
285 string which may be problematic with localized date.
286 """
287 if sys.version_info >= (3, 3):
288
289 return somedate.strftime(fmt)
290 else:
291 try:
292 if sys.version_info < (3, 0):
293 encoding = getlocale(LC_TIME)[1] or 'ascii'
294 return unicode(somedate.strftime(str(fmt)), encoding)
295 else:
296 return somedate.strftime(fmt)
297 except ValueError:
298 if somedate.year >= 1900:
299 raise
300
301
302
303 fields = {'Y': somedate.year,
304 'm': somedate.month,
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
313
315 if dt.tzinfo is None:
316 return dt
317 return (dt.replace(tzinfo=None) - dt.utcoffset())
318
320 if dt.tzinfo is None:
321 return dt
322 return (dt + dt.utcoffset() + dt.dst()).replace(tzinfo=None)
323
325 """return the number of seconds since the begining of the day for that date
326 """
327 return date.second+60*date.minute + 3600*date.hour
328
330 """return the time delta as a number of seconds"""
331 return delta.days + delta.seconds / (3600*24)
332
334 """return the time delta as a fraction of days"""
335 return delta.days*(3600*24) + delta.seconds
336