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