1
2
3
4
5
6 import datetime as py_datetime
7 from jalali import GregorianToJalali, JalaliToGregorian, j_days_in_month
8 import re as _re
9 MINYEAR=1
10 MAXYEAR=9377
11
12 timedelta = py_datetime.timedelta
13 tzinfo = py_datetime.tzinfo
14 -class time(py_datetime.time):
15 """time(hour, minute, second) --> time object"""
18
19
20 -class date(object):
21 """date(year, month, day) --> date object"""
22 j_months = ['Farvardin', 'Ordibehesht', 'Khordad', 'Tir', 'Mordad', 'Shahrivar',
23 'Mehr', 'Aban', 'Azar', 'Dey', 'Bahman', 'Esfand']
24 j_months_short = ['Far', 'Ord', 'Kho', 'Tir', 'Mor', 'Sha',
25 'Meh', 'Aba', 'Aza', 'Dey', 'Bah', 'Esf']
26
27 j_weekdays = ['Shanbeh', 'Yekshanbeh','Doshanbeh',
28 'SehShanbeh', 'Chaharshanbeh', 'Panjshanbeh','Jomeh']
29 j_weekdays_short = ['Sha', 'Yek','Dos',
30 'Seh', 'Cha', 'Pan','Jom']
31
32
33 @property
36
37 @property
40
41 @property
44
45 __year = 0
46 __month = 0
47 __day = 0
48
50 if type(value) is int or type(value) is long :
51 return True
52 return False
53
54
75
76
77 """The smallest possible difference between non-equal date objects, timedelta(days=1)."""
78 resolution = timedelta(1)
79
80 """The earliest representable date, date(MINYEAR, 1, 1)"""
81
82
83 """The latest representable date, date(MAXYEAR, 12, 31)."""
84
85
87 """check if year is leap year
88 algortim is based on http://en.wikipedia.org/wiki/Leap_year"""
89 return self.year % 33 in (1, 5, 9, 13, 17, 22, 26, 30)
90
95
96 @staticmethod
98 """Convert gregorian to jalali and return jdatetime.date
99 jdatetime.date.fromgregorian(day=X,month=X,year=X)
100 jdatetime.date.fromgregorian(date=datetime.date)
101 """
102 if 'date' in kw and type(kw['date']) == py_datetime.date :
103 d = kw['date']
104 (y, m, d) = GregorianToJalali(d.year, d.month, d.day).getJalaliList()
105 return date(y, m, d)
106 if 'day' in kw and 'month' in kw and 'year' in kw :
107 (year, month, day) = (kw['year'], kw['month'], kw['day'])
108 (y, m, d) = GregorianToJalali(year, month, day).getJalaliList()
109 return date(y, m, d)
110
111 raise ValueError, "fromgregorian have to called fromgregorian(day=X,month=X,year=X) or fromgregorian(date=datetime.date)"
112
113 @staticmethod
119
120 @staticmethod
125
127 """Return proleptic jalali ordinal. Farvardin 1 of year 1 which is equal to 622-3-21 of Gregorian."""
128 d = self.togregorian()
129 return d.toordinal() - 226894
130
131
132 @staticmethod
134 """int -> date corresponding to a proleptic Jalali ordinal.
135 it starts from Farvardin 1 of year 1, which is equal to 622-3-21 of Gregorian"""
136 if ordinal < 1 :
137 raise ValueError, "ordinal must be >= 1"
138 d = py_datetime.date.fromordinal(226894 +ordinal)
139 (y, m, d) = GregorianToJalali(d.year, d.month, d.day).getJalaliList()
140 return date(y, m, d)
141
143 return "jdatetime.date(%s, %s, %s)"%(self.year, self.month, self.day)
144
147
149 """x.__add__(y) <==> x+y"""
150 if type(timedelta) != py_datetime.timedelta :
151 raise TypeError, ("unsupported operand type(s) for +: '%s' and '%s'"%(type(self), type(timedelta)))
152 gd = self.togregorian() + timedelta
153 return date.fromgregorian(date=gd)
154
156 """x.__sub__(y) <==> x-y"""
157 if type(timedelta) != py_datetime.timedelta :
158 raise TypeError, ("unsupported operand type(s) for +: '%s' and '%s'"%(type(self), type(timedelta)))
159 gd = self.togregorian() - timedelta
160 return date.fromgregorian(date=gd)
161
162 - def __eq__(self, other_date):
163 """x.__eq__(y) <==> x==y"""
164 if other_date == None :
165 return False
166 if type(other_date) != date :
167 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
168 if self.year == other_date.year and self.month == other_date.month and self.day == other_date.day :
169 return True
170 return False
171
172 - def __ge__(self, other_date):
173 """x.__ge__(y) <==> x>=y"""
174 if type(other_date) != date :
175 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
176
177 if self.year > other_date.year :
178 return True
179 elif self.year == other_date.year :
180 if self.month > other_date.month :
181 return True
182 elif self.month == other_date.month and self.day >= other_date.day :
183 return True
184 return False
185
186 - def __gt__(self, other_date):
187 """x.__gt__(y) <==> x>y"""
188 if type(other_date) != date :
189 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
190
191 if self.year > other_date.year :
192 return True
193 elif self.year == other_date.year :
194 if self.month > other_date.month :
195 return True
196 elif self.month >= other_date.month and self.day > other_date.day :
197 return True
198 return False
199
200 - def __le__(self, other_date):
201 """x.__le__(y) <==> x<=y"""
202 if type(other_date) != date :
203 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
204
205 return not self.__ge__(other_date)
206
207 - def __lt__(self, other_date):
208 """x.__lt__(y) <==> x<y"""
209 if type(other_date) != date :
210 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
211
212 return not self.__gt__(other_date)
213
214 - def __ne__(self, other_date):
215 """x.__ne__(y) <==> x!=y"""
216 if other_date == None:
217 return True
218 if type(other_date) != date :
219 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
220
221 return not self.__eq__(other_date)
222
224 """x.__radd__(y) <==> y+x"""
225 if type(timedelta) != py_datetime.timedelta :
226 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
227
228 return self.__add__(timedelta)
229
231 """x.__rsub__(y) <==> y-x"""
232 if type(timedelta) != py_datetime.timedelta :
233 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
234
235 return self.__sub__(timedelta)
236
238 """x.__hash__() <==> hash(x)"""
239 gd = self.togregorian()
240 return gd.__hash__()
241
243 """Return ctime() style string."""
244 return self.strftime("%c")
245
246 - def replace(self, year=0, month=0, day=0):
247 """Return date with new specified fields."""
248 new_year = self.year
249 new_month = self.month
250 new_day = self.day
251
252 if year != 0 :
253 new_year = year
254 if month != 0 :
255 new_month = month
256 if day != 0 :
257 new_day = day
258
259 return date(new_year, new_month, new_day)
260
268
270 """Return the day of the week represented by the date.
271 Shanbeh == 0 ... Jomeh == 6"""
272 gd = self.togregorian()
273 if gd.weekday() == 5 :
274 return 0
275 if gd.weekday() == 6 :
276 return 1
277 if gd.weekday() == 0 :
278 return 2
279 if gd.weekday() == 1 :
280 return 3
281 if gd.weekday() == 2 :
282 return 4
283 if gd.weekday() == 3 :
284 return 5
285 if gd.weekday() == 4 :
286 return 6
287
289 """Return the day of the week as an integer, where Shanbeh is 1 and Jomeh is 7"""
290 return self.weekday() + 1
291
292
294 """Return week number """
295 return self.yday() / 7
296
298 """Return a 3-tuple, (ISO year, ISO week number, ISO weekday)."""
299 return (self.year, self.weeknumber(), self.isoweekday())
300
304
305
306
307
309 """format -> strftime() style string."""
310
311
312
313
314
315 format = format.replace("%a", self.j_weekdays_short[self.weekday()])
316
317 format = format.replace("%A", self.j_weekdays[self.weekday()])
318
319
320 format = format.replace("%b", self.j_months_short[self.month - 1 ])
321
322 format = format.replace("%B", self.j_months[self.month -1 ])
323
324 if '%c' in format :
325 format = format.replace("%c", self.strftime("%a %b %d %H:%M:%S %Y"))
326
327 format = format.replace("%d", '%02.d'%(self.day) )
328
329 try :
330 format = format.replace("%f", '%06.d'%(self.microsecond))
331 except :
332 format = format.replace("%f", "000000" )
333
334 try :
335 format = format.replace("%H", '%02.d'%(self.hour) )
336 except :
337 format = format.replace("%H", '00' )
338
339 try :
340 if self.hour > 12 :
341 format = format.replace("%I", '%2.d'%(self.hour - 12))
342 else:
343 format = format.replace("%I", '%2.d'%(self.hour))
344 except :
345 format = format.replace("%I", '00' )
346
347 format = format.replace("%j", '%03.d'%( self.yday()) )
348
349 format = format.replace("%m", '%02.d'%( self.month) )
350
351 try :
352 format = format.replace("%M", '%02.d'%(self.minute))
353 except :
354 format = format.replace("%M", '00')
355
356 try :
357 if self.hour > 12 :
358 format = format.replace("%p", 'PM')
359 else :
360 format = format.replace("%p", 'AM')
361 except :
362 format = format.replace("%p", 'AM')
363
364 try :
365 format = format.replace("%S", '%02.d'%(self.second))
366 except :
367 format = format.replace("%S", '00')
368
369 format = format.replace("%w", str(self.weekday()))
370
371 format = format.replace("%W", str(self.weeknumber()))
372
373 if '%x' in format :
374 format = format.replace("%x", self.strftime("%m/%d/%y"))
375
376 if '%X' in format :
377 format = format.replace("%X", self.strftime('%H:%I:%S'))
378
379 format = format.replace("%Y", str(self.year) )
380
381 format = format.replace("%y", str(self.year)[2:] )
382
383 format = format.replace("%Y", str(self.year) )
384
385 try :
386 sign = "+"
387 diff = self.tzinfo.utcoffset(self.tzinfo)
388 diff_sec = diff.seconds
389 if diff.days > 0 or diff.days < -1 :
390 raise ValueError, "tzinfo.utcoffset() returned big time delta! ; must be in -1439 .. 1439"
391 if diff.days != 0:
392 sign = "-"
393 diff_sec = (1 * 24 * 60 * 60) - diff_sec
394 tmp_min = diff_sec / 60
395 diff_hour = tmp_min / 60
396 diff_min = tmp_min % 60
397 format = format.replace("%z", '%s%02.d%02.d'%(sign, diff_hour, diff_min))
398 except AttributeError:
399 format = format.replace("%z", '')
400
401 try:
402 format = format.replace("%Z", self.tzinfo.tzname(self.tzinfo))
403 except AttributeError:
404 format = format.replace("%Z", '')
405
406 return format
407
409 """datetime(year, month, day, [hour, [minute, [seconds, [microsecond, [tzinfo]]]]]) --> datetime object"""
410 __time = None
411
415
417 """Return date object with same year, month and day."""
418 return date(self.year, self.month, self.day)
419
420 - def __init__(self,year, month, day, hour=None, minute=None, second=None, microsecond=None, tzinfo=None):
439
441 if self.__time.tzinfo != None :
442 return "jdatetime.datetime(%s, %s, %s, %s, %s, %s, %s, tzinfo=%s)"%(self.year, self.month, self.day,self.hour, self.minute, self.second, self.microsecond ,self.tzinfo)
443
444 if self.__time.microsecond != 0 :
445 return "jdatetime.datetime(%s, %s, %s, %s, %s, %s, %s)"%(self.year, self.month, self.day,self.hour, self.minute, self.second, self.microsecond)
446
447 if self.__time.second != 0 :
448 return "jdatetime.datetime(%s, %s, %s, %s, %s, %s)"%(self.year, self.month, self.day, self.hour, self.minute, self.second)
449
450 return "jdatetime.datetime(%s, %s, %s, %s, %s)"%(self.year, self.month, self.day,self.hour, self.minute)
451
452 @staticmethod
454 """Current date or datetime"""
455 return datetime.now()
456
457 @staticmethod
463
464 @staticmethod
470
471 @staticmethod
477
478 @staticmethod
484
485 @staticmethod
486 - def combine(d=None, t=None, **kw):
487 """date, time -> datetime with same date and time fields"""
488
489 c_date = None
490 if d != None :
491 c_date = d
492 elif 'date' in kw :
493 c_date = kw['date']
494
495 c_time = None
496 if t != None:
497 c_time = t
498 elif 'time' in kw :
499 c_time = kw['time']
500
501 if c_date == None :
502 raise TypeError , "Required argument 'date' (pos 1) not found"
503 if c_time == None :
504 raise TypeError , "Required argument 'date' (pos 2) not found"
505
506 if type(c_date) != date :
507 raise TypeError, "combine() argument 1 must be jdatetime.date, not %s"%(type(c_date))
508 if type(c_time) != time :
509 raise TypeError, "combine() argument 2 must be jdatetime.time, not %s"%(type(c_time))
510
511 return datetime(c_date.year, c_date.month, c_date.day, c_time.hour, c_time.minute, c_time.second, c_time.microsecond, c_time.tzinfo)
512
513 @staticmethod
515 """int -> date corresponding to a proleptic Jalali ordinal.
516 it starts from Farvardin 1 of year 1, which is equal to 622-3-21 of Gregorian"""
517 if ordinal < 1 :
518 raise ValueError, "ordinal must be >= 1"
519 d = py_datetime.date.fromordinal(226894 +ordinal)
520 j_date = date.fromgregorian(date=d)
521 return datetime(j_date.year, j_date.month, j_date.day, 0, 0)
522
523 @property
526
527 @property
530
531 @property
534
535 @property
538
539 @property
542
543
544
545 @staticmethod
547 """string, format -> new datetime parsed from a string (like time.strptime())"""
548 if '*' in format :
549 format = format.replace("*", "\*")
550 if '+' in format :
551 format = format.replace("+", "\+")
552 if '(' in format or ')' in format :
553 format = format.replace("(", "\(")
554 format = format.replace(")", "\)")
555 if '[' in format or ']' in format :
556 format = format.replace("[", "\[")
557 format = format.replace("]", "\]")
558 result_date = {'day': 1, 'month': 1, 'year': 1279, 'microsecond': 0, 'second': 0, 'minute' : 0, 'hour': 0}
559 apply_order = []
560 format_map = {
561 '%d': ['[0-9]{1,2}' , 'day'],
562 '%f': ['[0-9]{1,6}' , 'microsecond'],
563 '%H': ['[0-9]{1,2}' , 'hour'],
564 '%m': ['[0-9]{1,2}' , 'month'],
565 '%M': ['[0-9]{1,2}' , 'minute'],
566 '%S': ['[0-9]{1,2}' , 'second'],
567 '%Y': ['[0-9]{4,5}' , 'year'],
568 }
569 regex = format
570 find = _re.compile("([%a-zA-Z]{2})")
571
572 for form in find.findall(format):
573 if form in format_map :
574 regex = regex.replace(form, "(" + format_map[form][0] + ")")
575 apply_order.append( format_map[form][1])
576 try :
577 p = _re.compile(regex)
578 if not p.match(date_string):
579 raise ValueError
580 for i,el in enumerate(p.match(date_string).groups()):
581 result_date[apply_order[i]] = int(el)
582 return datetime(result_date['year'], result_date['month'], result_date['day'], result_date['hour'], result_date['minute'], result_date['second'])
583 except :
584 raise ValueError , "time data '%s' does not match format '%s'"%(date_string, format)
585
586 - def replace(self, year = None, month = None, day = None, hour = None, minute = None, second = None, microsecond = None, tzinfo = None):
587 """Return datetime with new specified fields."""
588 t_year = self.year
589 if year != None :
590 t_year = year
591
592 t_month = self.month
593 if month != None :
594 t_month = month
595
596 t_day = self.day
597 if day != None :
598 t_day = day
599
600 t_hour = self.hour
601 if hour != None :
602 t_hour = hour
603
604 t_min = self.minute
605 if minute != None :
606 t_min = minute
607
608 t_sec = self.second
609 if second != None :
610 t_sec = second
611
612 t_mic = self.microsecond
613 if microsecond != None :
614 t_mic = microsecond
615
616 t_tz = self.tzinfo
617 if tzinfo != None :
618 t_tz = t_tz
619 return datetime(t_year, t_month, t_day, t_hour, t_min, t_sec, t_mic, t_tz)
620
622 """x.__add__(y) <==> x+y"""
623 if type(timedelta) != py_datetime.timedelta :
624 raise TypeError, ("unsupported operand type(s) for +: '%s' and '%s'"%(type(self), type(timedelta)))
625 gdatetime = self.togregorian()
626 new_gdatetime = gdatetime + timedelta
627
628 return datetime.fromgregorian(datetime = new_gdatetime)
629
631 """x.__sub__(y) <==> x-y"""
632 if type(timedelta) != py_datetime.timedelta :
633 raise TypeError, ("unsupported operand type(s) for +: '%s' and '%s'"%(type(self), type(timedelta)))
634 gdatetime = self.togregorian()
635 new_gdatetime = gdatetime - timedelta
636
637 return datetime.fromgregorian(datetime = new_gdatetime)
638
639
640 - def __eq__(self, other_datetime):
641 """x.__eq__(y) <==> x==y"""
642 if other_datetime == None :
643 return False
644 if type(other_datetime) != datetime :
645 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_datetime)))
646 if self.year == other_datetime.year and self.month == other_datetime.month and self.day == other_datetime.day :
647 return True
648 if self.hour == other_datetime.hour and self.minute == other_datetime.minute and self.second == other_datetime.second and self.microsecond == other_datetime.microsecond and self.tzinfo == self.other_datetime.tzinfo :
649 return True
650 return False
651
652 - def __ge__(self, other_datetime):
653 """x.__ge__(y) <==> x>=y"""
654 if type(other_datetime) != datetime :
655 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_datetime)))
656
657 if self.year > other_datetime.year :
658 return True
659 elif self.year == other_datetime.year :
660 if self.month > other_datetime.month :
661 return True
662 elif self.month == other_datetime.month and self.day >= other_datetime.day :
663 return True
664 if self.hour >= other_datetime.hour :
665 return True
666 if self.minute >= other_datetime.minute:
667 return True
668 if self.second >= other_datetime.second :
669 return True
670 if self.microsecond >= other_datetime.microsecond :
671 return True
672
673 return False
674
675 - def __gt__(self, other_datetime):
676 """x.__gt__(y) <==> x>y"""
677 if type(other_datetime) != datetime :
678 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_datetime)))
679
680 if self.year > other_datetime.year :
681 return True
682 elif self.year == other_datetime.year :
683 if self.month > other_datetime.month :
684 return True
685 elif self.month >= other_datetime.month and self.day > other_datetime.day :
686 return True
687 if self.hour > other_datetime.hour :
688 return True
689 if self.minute > other_datetime.minute :
690 return True
691 if self.second > other_datetime.second:
692 return True
693 if self.microsecond > other_datetime.microsecond :
694 return True
695
696 return False
697
699 """x.__hash__() <==> hash(x)"""
700 gdt = self.togregorian()
701 return gdt.__hash__()
702
703
704 - def __le__(self, other_datetime):
705 """x.__le__(y) <==> x<=y"""
706 if type(other_datetime) != datetime :
707 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_datetime)))
708
709 return not self.__ge__(other_datetime)
710
711 - def __lt__(self, other_datetime):
712 """x.__lt__(y) <==> x<y"""
713 if type(other_datetime) != datetime :
714 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_datetime)))
715 return not self.__gt__(other_datetime)
716
717 - def __ne__(self, other_datetime):
718 """x.__ne__(y) <==> x!=y"""
719 if other_date == None:
720 return True
721 if type(other_date) != datetime :
722 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_datetime)))
723
724 return not self.__eq__(other_datetime)
725
727 """x.__radd__(y) <==> y+x"""
728 if type(timedelta) != py_datetime.timedelta :
729 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
730
731 return self.__add__(timedelta)
732
734 """x.__rsub__(y) <==> y-x"""
735 if type(timedelta) != py_datetime.timedelta :
736 raise TypeError, ("unsupported operand type for ==: '%s'"%(type(other_date)))
737
738 return self.__sub__(timedelta)
739
740
741 @staticmethod
743 """Convert gregorian to jalali and return jdatetime.datetime
744 jdatetime.date.fromgregorian(day=X,month=X,year=X,[hour=X, [minute=X, [second=X, [tzinfo=X]]]])
745 jdatetime.date.fromgregorian(date=datetime.date)
746 jdatetime.date.fromgregorian(datetime=datetime.datetime)
747 """
748 if 'date' in kw and type(kw['date']) == py_datetime.date :
749 d = kw['date']
750 (y, m, d) = GregorianToJalali(d.year, d.month, d.day).getJalaliList()
751 return datetime(y, m, d)
752 if 'datetime' in kw and type(kw['datetime']) == py_datetime.datetime :
753 dt = kw['datetime']
754 (y, m, d) = GregorianToJalali(dt.year, dt.month, dt.day).getJalaliList()
755 return datetime(y, m, d, dt.hour, dt.minute, dt.second, dt.microsecond, dt.tzinfo)
756 if 'day' in kw and 'month' in kw and 'year' in kw :
757 (year, month, day) = (kw['year'], kw['month'], kw['day'])
758 (y, m, d) = GregorianToJalali(year, month, day).getJalaliList()
759 hour = None
760 minute = None
761 second = None
762 microsecond = None
763 tzinfo = None
764 if 'hour' in kw :
765 hour = kw['hour']
766 if 'minute' in kw :
767 minute = kw['minute']
768 if 'second' in kw :
769 second = kw['second']
770 if 'microsecond' in kw:
771 microsecond = kw['microsecond']
772 if 'tzinfo' in kw :
773 tzinfo = kw['tzinfo']
774 return datetime(y, m, d, hour, minute, second, microsecond, tzinfo)
775
776 raise ValueError, "fromgregorian have to called fromgregorian(day=X,month=X,year=X, [hour=X, [minute=X, [second=X, [tzinfo=X]]]]) or fromgregorian(date=datetime.date) or fromgregorian(datetime=datetime.datetime)"
777
782
783
789
791 """Return ctime() style string."""
792 return self.strftime("%c")
793
794
796 """Return self.tzinfo.dst(self)"""
797 if self.tzinfo :
798 return self.tzinfo.dst(self)
799 return None
800
810
812 """Return time tuple, compatible with time.localtime().
813 It returns Gregorian object!
814 """
815 dt= self.togregorian()
816 return dt.timetuple()
817
819 """Return time object with same time and tzinfo."""
820
822 """Return self.tzinfo.tzname(self)"""
823 if self.tzinfo :
824 return self.tzinfo.tzname(self)
825 return None
826
828 """Return self.tzinfo.utcoffset(self)."""
829 if self.tzinfo :
830 return self.tzinfo.utcoffset(self)
831
833 """Return UTC time tuple, compatible with time.localtime().
834 It returns Gregorian object !
835 """
836 dt = self.togregorian()
837 return dt.utctimetuple()
838
840 mil = self.strftime("%f")
841 if int(mil) == 0 :
842 mil = ""
843 else :
844 mil = "." + mil
845 tz = self.strftime("%z")
846 return self.strftime("%Y-%m-%d%H:%M:%S") + "%s%s"%(mil,tz)
847