1 """
2 @version: 0.7
3 @date: 2008-08-28
4 @status: beta
5 @author: Stephen Ferg
6 @contact: http://www.ferg.org/contact_info
7 @license: Creative Commons Attribution License 2.0 http://creativecommons.org/licenses/by/2.0/
8 @see: http://www.ferg.org/pyfdate
9 @note:
10
11 ABOUT PYFDATE
12
13 The purpose of pyfdate is to provide a variety of user-friendly features
14 for working with datetimes, doing date arithmetic, and formatting dates,
15 times, and periods of time.
16
17 Pyfdate doesn't attempt to be all things to all people: rather, its goal
18 is to make it easy to do 95% if the things that people want to do with
19 dates and times.
20
21 Pyfdate provides two classes:
22
23 ------------------------------------------------------------------------
24
25 Time:
26 - A specific point in time, identified by
27 year, month, day, hour, minute, second.
28 (Python's datetime module calls this a "datetime".)
29
30 Period:
31 - An amount of time, measured in days, hours, minutes, and seconds.
32
33 ------------------------------------------------------------------------
34
35 In order to keep pyfdate simple, there are a number of things that it does not attempt to do.
36 - Pyfdate doesn't know anything about timezones.
37 - Pyfdate doesn't know anything about daylight savings time.
38 - Pyfdate doesn't provide any facilities for parsing strings into date/times.
39 (It does, however, provide a useful numsplit() function which performs a kind of parsing.)
40 - The smallest unit of time that Pyfdate can handle is a second.
41 It cannot be used to work with hundredths or thousandths of seconds.
42
43 INTERNATIONAL LANGUAGE SUPPORT
44
45 By default, pyfdate's language for displaying dates and times
46 (e.g. weekday names and month names) is American English.
47
48 If pyfdate can successfully import pyfdate_local,
49 the language for displaying dates and times
50 is taken from the pyfdate_local.py file.
51
52 Localization files are available for a number of languages.
53 For example: pyfdate_local_francais.py for French.
54
55 To use the file for language foobar,
56 simply copy pyfdate_local_foobar.py to pyfdate_local.py
57
58 And of course it is possible to customize pyfdate_local.py for any particular
59 language of your choice. If you create (or correct) a localization file
60 for pyfdate, please share it with others by submitting it for inclusion
61 in the pyfdate distribution.
62
63
64 BACKGROUND
65
66 Many ideas for pyfdate functionality grew out of Steve Ferg's experiences with
67 an earlier (non-Python) program called "fdate", which provided datetime
68 arithmetic functionality for MS-DOS batch files. The name "pyfdate" is derived
69 from "Python" and "fdate".
70 """
71 from pprint import pprint
72 import time, copy, sys, os, types
73
74 try:
75 import datetime
76 except ImportError, e:
77 raise AssertionError("Error importing datetime.\n"
78 +"Note that pyfdate requires Python version 2.3+.\n"
79 +"You are now running Python version " + sys.version)
80 from pprint import pprint
81
82 """
83 Language-specific interface for pyfdate, for language: American English
84 """
85 LANG = "American English"
86 TimeExpressedIn24Hours = False
87 CivilTimeSeparator = ":"
88 CivilDateFormat = "m d, y"
89
90 HOUR = "hour"
91 HOURS = "hours"
92 MINUTE = "minute"
93 MINUTES = "minutes"
94 SECOND = "second"
95 SECONDS = "seconds"
96
97 DAY = "day"
98 DAYS = "days"
99 HOUR = "hour"
100 HOURS = "hours"
101 MINUTE = "minute"
102 MINUTES = "minutes"
103 SECOND = "second"
104 SECONDS = "seconds"
105
106 WEEK = "week"
107 WEEKS = "weeks"
108 MONTH = "month"
109 MONTHS = "months"
110 YEAR = "year"
111 YEARS = "years"
112
113 MONTH_NAMES = \
114 { 1:"January"
115 , 2:"February"
116 , 3:"March"
117 , 4:"April"
118 , 5:"May"
119 , 6:"June"
120 , 7:"July"
121 , 8:"August"
122 , 9:"September"
123 ,10:"October"
124 ,11:"November"
125 ,12:"December"
126 }
127
128 WEEKDAY_NAMES = \
129 {1:"Monday"
130 ,2:"Tuesday"
131 ,3:"Wednesday"
132 ,4:"Thursday"
133 ,5:"Friday"
134 ,6:"Saturday"
135 ,7:"Sunday"
136 }
137
138
139
140
141
142 for __monthNumber, __monthName in MONTH_NAMES.items():
143 __monthName = __monthName.upper().replace("-","_")
144 exec(__monthName + " = " + str(__monthNumber))
145
146 for __weekdayNumber, __weekdayName in WEEKDAY_NAMES.items():
147 __weekdayName = __weekdayName.upper().replace("-","_")
148 exec(__weekdayName + " = " + str(__weekdayNumber))
149
150
151
152
153
154
155 try: from pyfdate_local import *
156 except ImportError: pass
157
158
159
160
161 NEXT = "NEXT"
162 NEAREST = "NEAREST"
163 PREVIOUS = "PREVIOUS"
164
165 SECONDS_IN_MINUTE = 60
166 MINUTES_IN_HOUR = 60
167 HOURS_IN_DAY = 24
168 SECONDS_IN_HOUR = SECONDS_IN_MINUTE * MINUTES_IN_HOUR
169 SECONDS_IN_DAY = SECONDS_IN_HOUR * HOURS_IN_DAY
170 MINUTES_IN_DAY = MINUTES_IN_HOUR * HOURS_IN_DAY
171
172 NORMAL_DAYS_IN_MONTH = \
173 { 1:31
174 , 2:28
175 , 3:31
176 , 4:30
177 , 5:31
178 , 6:30
179 , 7:31
180 , 8:31
181 , 9:30
182 , 10:31
183 , 11:30
184 , 12:31
185 }
186
187
188
189
190
191
192
193
195 """
196 A pyfdate.Period is an amount of time.
197
198 pyfdate.Period performs a function similar to the standard library datetime.timedelta.
199 pyfdate.Period, however, is implemented differently than the datetime.timedelta class.
200 pyfdate.Period stores only a single value: self.period_seconds.
201 This may be a positive or negative value.
202
203 pyfdate.Period objects (like datetime.timedelta objects) are used to do date
204 arithmetic. But since pyfdate.Time provides more sophisticated date
205 arithmetic features than datetime.datetime, pyfdate. Periods are probably
206 more widely used for their display capabilities than for their date
207 arithmetic capabilities.
208 """
209
210
211
212 - def __init__(self, arg1=0,hours=0,minutes=0,seconds=0):
213 """
214 Thhe constructor of a Period object.
215
216 Constructor expects arguments of:
217 - a series of positional arguments: [days [, hours[,minutes[,seconds]]]], or
218 - a datetime.timedelta object, or
219 - a pyfdate.Period object
220 @rtype: Period
221 """
222 if isinstance(arg1, datetime.timedelta):
223
224 timedelta = arg1
225 self.period_seconds = timedelta.seconds + (timedelta.days * SECONDS_IN_DAY)
226 return
227
228 if isinstance(arg1, Period):
229
230 self.period_seconds = copy.deepcopy(arg1.period_seconds)
231 return
232
233
234 days = arg1
235 self.period_seconds = (
236 (days * SECONDS_IN_DAY )
237 + (hours * SECONDS_IN_HOUR )
238 + (minutes * SECONDS_IN_MINUTE)
239 + seconds )
240
241
242
243
244
246 """
247 Returns a clone of myself, with my absolute value.
248 @return: a clone of myself, with my absolute value
249 @rtype: Period
250 """
251 return Period(0,0,0,abs(self.period_seconds))
252
253
254
255
257 """
258 Add one period to another.
259 @return: a new Period object
260 @rtype: Period
261 """
262 if isinstance(arg, Period): pass
263 else:
264 raise AssertionError("Cannot add a "
265 + arg.__class__.__name__ + " object to a Period object.")
266
267 return Period(0,0,0, self.period_seconds + arg.period_seconds)
268
269
270
271
273 """
274 Compare two Period objects for equality.
275 @rtype: boolean
276 """
277 if isinstance(arg, Period): pass
278 else:
279 raise AssertionError("Cannot compare Period object with "
280 + arg.__class__.__name__ + " object.")
281
282 if self.period_seconds == arg.period_seconds: return True
283 return False
284
285
286
287
289 """
290 Compares two Period objects to determine if one is greater than,
291 or equal to, the other.
292 @rtype: boolean
293 """
294 if self.__gt__(arg): return True
295 if self.__eq__(arg): return True
296 return False
297
298
299
300
302 """
303 Compares two Period objects to determine if one is greater than the other.
304 @rtype: boolean
305 """
306 if isinstance(arg, Period): pass
307 else:
308 raise AssertionError("Cannot compare Period object with "
309 + arg.__class__.__name__ + " object.")
310 if self.period_seconds > arg.period_seconds: return True
311 return False
312
313
314
315
317 """
318 Compares two Period objects to determine if one is less than,
319 or equal to, the other.
320 @rtype: boolean
321 """
322 if self.__gt__(arg): return False
323 return True
324
325
326
327
329 """
330 Compares two Period objects to determine if one is less than the other.
331 @rtype: boolean
332 """
333 if self.__gt__(arg): return False
334 if self.__eq__(arg): return False
335 return True
336
337
338
339
341 """
342 Compare two Period objects for inequality.
343 @rtype: boolean
344 """
345 return not self.__eq__(arg)
346
347
348
349
351 """
352 Returns a string representation of a Period.
353 @rtype: string
354 @return: a string representation of a Period.
355 e.g.::
356 "4 days 3 hours 20 minutes 45 seconds"
357 """
358 return self.get_p()
359
360
361
362
363
365 """
366 Subtract one period from another.
367 @return: a new Period object
368 @rtype: Period
369 """
370 if isinstance(arg, Period): pass
371 else:
372 raise AssertionError("Cannot subtract a "
373 + arg.__class__.__name__ + " object from a Period object.")
374
375 return Period(0,0,0, self.period_seconds - arg.period_seconds)
376
377
378
379
380
382 """
383 A general method for subtracting time from a Period object.
384 @rtype: Period
385
386 @note: Example:
387 ::
388 p1 = Period()
389 p2 = p1.plus(weeks=2,days=3,hours=4,minutes=99, seconds=1)
390 """
391 for key,value in kwargs.items():
392 kwargs[key] = value * -1
393
394 return self.add(**kwargs)
395
396
397 minus = subtract
398
399
400
401
402
403 - def add(self, **kwargs):
404 """
405 A general method for adding time to a Period object. To
406 subtract time, add time in negative increments or use the subtract() method.
407 @rtype: Period
408 @note:
409 Example::
410 p1 = Period()
411 p2 = p1.plus(weeks=2,days=3,hours=4,minutes=99, seconds=1)
412 """
413 argNum = 0
414 p = self.clone()
415 for key, value in kwargs.items():
416 argNum += 1
417 if False: pass
418 elif key in ("day" ,"days" , DAY , DAYS ): p = Period(0,0,0,p.period_seconds + (value * SECONDS_IN_DAY ))
419 elif key in ("hour" ,"hours" , HOUR , HOURS ): p = Period(0,0,0,p.period_seconds + (value * SECONDS_IN_HOUR ))
420 elif key in ("minute" ,"minutes", MINUTE, MINUTES): p = Period(0,0,0,p.period_seconds + (value * SECONDS_IN_MINUTE))
421 elif key in ("second" ,"seconds", SECOND, SECONDS): p = Period(0,0,0,p.period_seconds + value )
422 elif key in ("week" ,"weeks" , WEEK , WEEKS ): p = Period(0,0,0,p.period_seconds + (7*(value * SECONDS_IN_DAY)))
423 else :
424 raise AssertionError(
425 self.__class__.__name__ +".plus()"
426 + " received invalid keyword argument: " + str(key)
427 + " in argument " + str(argNum) + ".\n"
428 + kwargsToString(**kwargs)
429 )
430 return p
431
432
433 plus = add
434
435
436
437
439 """
440 Return a clone of myself.
441 @return: a clone of myself
442 @rtype: Period
443 """
444 return Period(0,0,0,self.period_seconds)
445
446
447
448
450 """
451 Return the days portion of the period, as an int.
452 @rtype: int
453 @return: days, e.g.::
454 3
455 """
456 days, hours, minutes, seconds = self.get_tuple()
457 return days
458 days = property(get_days)
459
460
461
462
464 """
465 Return the hours portion of the Period, as an int.
466 @rtype: int
467 @return: hours, e.g.::
468 15
469 """
470 days, hours, minutes, seconds = self.get_tuple()
471 return hours
472 hours = property(get_hours)
473
474
475
476
478 """
479 Return the minutes portion of the Period, as an int.
480 @rtype: int
481 @return: minutes, e.g.::
482 45
483 """
484 days, hours, minutes, seconds = self.get_tuple()
485 return minutes
486 minutes = property(get_minutes)
487
488
489
490
492 """
493 Return the seconds portion of the Period, as an int.
494 @rtype: int
495 @return: seconds, e.g.::
496 45
497 """
498 days, hours, minutes, seconds = self.get_tuple()
499 return seconds
500 seconds = property(get_seconds)
501
502
503
504
505 - def get_p(self, **kwargs):
506 """
507 Return myself in a nicely-formatted string.
508 @rtype: string
509 @return: myself in a nicely-formatted string, e.g.::
510 "2 days 3 hours 0 minutes 45 seconds"
511
512 @note:
513 ::
514 if keyword arg showZeroComponents == True:
515 all components (days, hours, minutes, seconds) will be shown
516 else:
517 only non-zero components (except seconds) will be shown
518
519 if the period is empty (has a duration of zero):
520 if keyword arg showZeroPeriod == True:
521 return "0 seconds"
522 else:
523 return "" # the empty string
524
525 """
526 showZeroComponents = kwargs.get("showZeroComponents",True)
527 showZeroPeriod = kwargs.get("showZeroPeriod",True)
528
529 if self.period_seconds == 0:
530 if showZeroPeriod: return "0 " + SECONDS
531 else: return ""
532
533 days, hours, minutes, seconds = self.get_tuple()
534
535 if days == 0 :
536 if showZeroComponents : daysString = "0 " + DAY
537 else : daystring = ""
538 elif days == 1 : daysString = "1 " + DAY
539 elif days == -1 : daysString = "-1 " + DAY
540 else : daysString = str(days) + " " + DAYS
541
542 if hours == 0 :
543 if showZeroComponents : hoursString = "0 " + HOURS
544 else : hoursString = ""
545 elif hours == 1 : hoursString = "1 " + HOUR
546 elif hours == -1 : hoursString = "-1 " + HOUR
547 else : hoursString = str(hours) + " " + HOURS
548
549 if minutes == 0:
550 if showZeroComponents : minutesString = "0 " + MINUTES
551 else : minutesString = ""
552 elif minutes == 1: minutesString = "1 " + MINUTE
553 else : minutesString = str(minutes) + " " + MINUTES
554
555 if seconds == 0:
556 if showZeroComponents : secondsString = "0 " + SECONDS
557 else : secondsString = ""
558 elif seconds == 1: secondsString = "1 " + SECOND
559 elif seconds ==-1: secondsString = "-1 " + SECOND
560 else : secondsString = str(seconds) + " " + SECONDS
561
562 results = ""
563 if showZeroComponents:
564 results += " " + daysString
565 results += " " + hoursString
566 results += " " + minutesString
567 results += " " + secondsString
568 else:
569 if days : results += " " + daysString
570 if hours : results += " " + hoursString
571 if minutes: results += " " + minutesString
572 if seconds: results += " " + secondsString
573
574 return results.strip()
575
576 p = property(get_p)
577
578
579
580
581
583 """
584 Return myself as a nicely formatted string: suppress components whose value is zero.
585 @rtype: string
586 @return: myself nicely formatted: suppress components whose value is zero.
587 If my duration is zero, return "0 seconds". e.g.::
588 "2 days 3 hours"
589 @note: This method will never return an empty string.
590 If my duration is zero, then it returns the string "0 seconds"
591 """
592 return self.get_p(showZeroComponents=False)
593 short = property(get_short)
594
595
596
597
599 """
600 Return myself as a nicely formatted string: suppress components whose value is zero.
601 @rtype: string
602 @return: myself nicely formatted: suppress components whose value is zero.
603 e.g.::
604 "2 days 3 hours"
605 @note: If my duration is 0, this method will return an empty string.
606 """
607 return self.get_p(showZeroComponents=False,showZeroPeriod=False)
608 shortest = property(get_shortest)
609
610
611
612
614 """
615 Returns a Period expressed as a datetime.timedelta object
616 @return: a Period expressed as a datetime.timedelta object
617 @rtype: timedelta
618 """
619 days, seconds = divmod(self.period_seconds, SECONDS_IN_DAY)
620 return datetime.timedelta(days, seconds)
621 timedelta = property(get_timedelta)
622
623
624
625
626
628 """
629 Returns myself formatted as a 4-tuple of ints (days, hours, minutes, seconds).
630 @return: myself formatted as a 4-tuple of ints (days, hours, minutes, seconds)
631 @rtype: tuple
632 """
633 period_seconds = abs(self.period_seconds)
634
635 days , remainder = divmod(period_seconds, SECONDS_IN_DAY)
636 hours , remainder = divmod(remainder , SECONDS_IN_HOUR)
637 minutes, seconds = divmod(remainder , SECONDS_IN_MINUTE)
638
639 if self.period_seconds >= 0:
640 return days, hours, minutes, seconds
641 else:
642 return -days, -hours, -minutes, -seconds
643
644 tuple = property(get_tuple)
645
646
647
648
649
651 """
652 Returns this Period, expressed in units of days.
653 @return: myself, expressed in units of days
654 @rtype: int
655 """
656 days, seconds = divmod(abs(self.period_seconds), SECONDS_IN_DAY)
657 return days
658 asDays = property(get_asDays)
659
660
661
662
664 """
665 Returns this Period, expressed in units of hours.
666 @return: myself, expressed in units of hours
667 @rtype: int
668 """
669 hours, seconds = divmod(abs(self.period_seconds), SECONDS_IN_HOUR)
670 return hours
671 asHours = property(get_asHours)
672
673
674
675
677 """
678 Returns this Period, expressed in units of minutes.
679 @return: myself, expressed in units of minutes
680 @rtype: int
681 """
682 minutes, seconds = divmod(abs(self.period_seconds), SECONDS_IN_MINUTE)
683 return minutes
684 asMinutes = property(get_asMinutes)
685
686
687
688
690 """
691 Returns this Period, expressed in units of seconds.
692 @return: myself, expressed in units of seconds
693 @rtype: int
694 """
695 return abs(self.period_seconds)
696 asSeconds = property(get_asSeconds)
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
713 """
714 A specific point in time, identified by
715 year, month, day, hour, minute, second.
716
717 Python's datetime module calls this a "datetime".
718 """
719
720
721
722
723 - def __init__(self,arg1=None,month=1,day=1,hour=0,minute=0,second=0):
724 """
725 The constructor for a Time object.
726
727 Constructor expects arguments of:
728 - None (this will construct a Time object from current date/time) or
729 - a datetime.datetime object, or
730 - a pyfdate.Time object, or
731 - a series of positional arguments: year [,month[,day[,hour[,minute[,second]]]]]
732 @rtype: Time
733 """
734 if arg1==None:
735
736 self.datetime = datetime.datetime.now()
737 return
738
739 if isinstance(arg1, datetime.datetime):
740
741
742 self.datetime = copy.deepcopy(arg1)
743 return
744
745 if isinstance(arg1,Time):
746
747
748 self.datetime = copy.deepcopy(arg1.datetime)
749 return
750
751
752 year = arg1
753 try:
754 self.datetime = datetime.datetime(year,month,day,hour,minute,second)
755 except ValueError, e:
756 raise ValueError(str(e) + "\nArgs to Time.__init__ were:\n"
757 + str(arg1) + "\n"
758 + str(month) + "\n"
759 + str(day) + "\n"
760 + str(hour) + "\n"
761 + str(minute) + "\n"
762 + str(second) + "\n"
763 )
764
765
766
767
769 """
770 Add a Period to this Time to produce a new Time.
771 @rtype: Time
772 """
773 if isinstance(arg, Period):
774 return Time(self.datetime + arg.timedelta)
775
776 raise AssertionError("Cannot add a "
777 + arg.__class__.__name__ + " object to a Time object.")
778
779
780
781
783 """
784 Compare two Time objects for equality.
785 @rtype: boolean
786 """
787 if isinstance(arg, Time): pass
788 else: raise AssertionError("Cannot compare a "
789 + arg.__class__.__name__ + " object to a Time object.")
790
791 if self.datetime == arg.datetime: return True
792 return False
793
794
795
796
798 """
799 Compare two Time objects for relative position in time.
800 @rtype: boolean
801 """
802 if self.__gt__(arg): return True
803 if self.__eq__(arg): return True
804 return False
805
806
807
808
810 """
811 Compare two Time objects to determine if one is later (greater than) the other.
812 @rtype: boolean
813 """
814 if isinstance(arg, Time): pass
815 else: raise AssertionError("Cannot compare a "
816 + arg.__class__.__name__ + " object to a Time object.")
817
818 if self.datetime > arg.datetime: return True
819 return False
820
821
822
823
825 """
826 Compare two Time objects for relative position in time.
827 @rtype: boolean
828 """
829 if self.__gt__(arg): return False
830 return True
831
832
833
834
836 """
837 Compare two Time objects for relative position in time.
838 @rtype: boolean
839 """
840 if self.__gt__(arg): return False
841 if self.__eq__(arg): return False
842 return True
843
844
845
846
848 """
849 Compare two Time objects for inequality.
850 @rtype: boolean
851 """
852 return not self.__eq__(arg)
853
854
855
856
857
859 """
860 Return a string representation of self as an isodate + space + isotime
861 @rtype: string
862 @return: self as an isodate + space + isotime, e.g.::
863 "2007-03-02 09:30:45"
864 """
865 return self.get_isodatetime(" ")
866
867
868
869
870
872 """
873 Subtract a Period or a Time from this Time object.
874
875 @rtype: Time or Period
876 @return: a Time (if <arg> is a Period) or a Period (if <arg> is a Time)
877
878 @arg arg: a Period object or a Time object
879 @type arg: Period or Time
880
881 @note:
882 ::
883 If <arg> is a Period object:
884 Subtract a Period from this Time to produce a new Time,
885 else, if <arg> is a Time object:
886 Subtract one Time from another Time to produce a Period.
887
888 @note:
889 If a Period is returned, it is signed. That is::
890 If an earlier time is subtracted from a later time:
891 The Period will be positive
892 else:
893 the Period will be negative.
894 """
895 if isinstance(arg, Time):
896 timedelta = self.datetime - arg.datetime
897 return Period(timedelta)
898
899 if isinstance(arg, Period):
900 return Time(self.datetime - arg.timedelta)
901
902 raise AssertionError("Cannot subtract a "
903 + arg.__class__.__name__ + " object from a Time object.")
904
905
906
907
908
909 - def add(self, **kwargs):
910 """
911 A method to add time to a Time object.
912 This is the basic method for doing date/time arithmetic.
913
914 The units and amounts of time are specified as keyword arguments.
915
916 @rtype: Time
917 @return: a new Time object with the specified amounts of time added to it.
918
919 @note: To subtract amounts, you can add negative amounts (see the example)
920 or use the 'subtract' method.
921
922 @note: Example: To get the time that is a month and a week from the current time::
923 t = Time().add(months=1,weeks=1)
924 @note: Example: To get the time that is a month and a week from the current time::
925 t = Time().add(months=1).plus(weeks=1)
926
927 @note: Example: To subtract 6 weeks from the current time::
928 t = Time().add(weeks=-6) # note the minus sign: -6.
929 """
930 days, hours, minutes, seconds = 0, 0.0, 0.0, 0.0
931
932 t = self.clone()
933 p = Period()
934 for key, value in kwargs.items():
935 if False: pass
936 elif key in ("day" ,"days" , DAY , DAYS ): p = p.add(days=value)
937 elif key in ("hour" ,"hours" , HOUR , HOURS ): p = p.add(hours=value)
938 elif key in ("minute" ,"minutes", MINUTE, MINUTES): p = p.add(minutes=value)
939 elif key in ("second" ,"seconds", SECOND, SECONDS): p = p.add(seconds=value)
940 elif key in ("week" ,"weeks" , WEEK , WEEKS ): p = p.add(weeks=value)
941 elif key in ("month" ,"months" , MONTH , MONTHS ): t = t.addmonths(value)
942 elif key in ("year" ,"years" , YEAR , YEARS ): t = t.addmonths(value * 12)
943 else:
944 raise AssertionError("In Time.plus() method, "
945 + "I do not recognize keyword "
946 + key +" in argment: "
947 + key + "=" + str(value)
948 )
949 return t + p
950
951
952
953
954
955 plus = add
956
957
958
959
960
962 """
963 A method for adding months to a time object.
964
965 >>> t1 = Time(2004,1,31)
966 >>> t1.d
967 'January 31, 2004'
968 >>> t2 = t1.addmonths(1)
969 >>> t2.d
970 'February 29, 2004'
971 >>> t2 = t1.add(months=8)
972 >>> t2.d
973 'September 30, 2004'
974
975 @rtype: Time
976 @return: a new Time object with the specified amounts of time added to it.
977
978 @note:
979 You can use this method directly, e.g.::
980 t = Time().addmonths(6)
981
982 Or you can use the standard add() method::
983 t = Time().add(months=6)
984 """
985 numericMonth = CalendarMonthToNumericMonth(self.year, self.month)
986 numericMonth += months
987 newYear, newMonth = NumericMonthToCalendarMonth(numericMonth)
988
989 maxDaysInNewMonth = NORMAL_DAYS_IN_MONTH[newMonth]
990 if newMonth == 2 and isLeapYear(newYear): maxDaysInNewMonth += 1
991
992 newDay = self.day
993 if self.day > maxDaysInNewMonth:
994
995
996
997 newDay = maxDaysInNewMonth
998
999 return Time(newYear,newMonth,newDay,self.hour,self.minute,self.second)
1000
1001
1002
1003
1004
1006 """
1007 The most recent anniversary of myself.
1008 @return: The most recent anniversary of some event, previous to today.
1009 @rtype: Time
1010 @arg eventTime: the time of the event whose anniversary we want
1011 @type eventTime: Time
1012 @note: In a non-leapyear, the anniversary of an event that occurred
1013 on February 29 of a leapyear will be moved to February 28.
1014 """
1015
1016 eventAnniversaryThisYear = eventTime.goto(year=self.year)
1017
1018
1019
1020 if eventAnniversaryThisYear <= self:
1021 return eventAnniversaryThisYear
1022 else:
1023 return eventTime.goto(year=self.year-1)
1024
1025
1026
1027
1028
1029 - def clone(self, **kwargs):
1030 """
1031 Return a clone of myself.
1032 @return: a clone of myself
1033 @rtype: Time
1034
1035 @note:
1036 It is possible to change certain parts of the cloned Time
1037 by using keyword arguments for the component(s) to
1038 be replaced in the clone. To make a clone of the current
1039 time, but one set in the year 1935, for example::
1040 t1 = Time()
1041 t2 = t1.clone(year=1935)
1042
1043 Note that it is possible to trigger a ValueError, if
1044 for example:
1045 - The current date is February 29, 2004 (a leap year) and the
1046 year is reset to 2007 (not a leap year).
1047 - The current date is January 31 and the month is reset to
1048 September (which has only 30 days).
1049
1050 If you *want* to raise a ValueError when such problems occur,
1051 use the clone() method. Otherwise, use the goto() method,
1052 which attempts to recover from such errors::
1053 t1 = Time()
1054 t2 = t1.goto(year=1935)
1055 """
1056 year,month,day,hour,minute,second = self.get_tuple()
1057
1058 for key, value in kwargs.items():
1059 if value == int(value): pass
1060 else:
1061 raise AssertionError("In Time.clone() method, keyword argment: "
1062 + key + "=" + str(value)
1063 + "\nthe argument value is not an int."
1064 )
1065
1066 if False: pass
1067 elif key in ("year" , YEAR ): year = value
1068 elif key in ("month" , MONTH ): month = value
1069 elif key in ("day" , DAY ): day = value
1070 elif key in ("hour" , HOUR ): hour = value
1071 elif key in ("minute" , MINUTE): minute = value
1072 elif key in ("second" , SECOND): second = value
1073 else:
1074 raise AssertionError("In Time.clone() method, "
1075 + "I do not recognize keyword "
1076 + key +" in argment: "
1077 + key + "=" + str(value)
1078 )
1079 return Time(year,month,day,hour,minute,second)
1080
1081
1082
1083
1084
1085 - def diff(self, eventTime):
1086 """
1087 Determine the difference (a Period) between two Times.
1088 @rtype: Period
1089 @note:
1090 Unlike the __sub__ method, the diff (i.e. difference) method
1091 always returns a Period object with a positive value.
1092 """
1093 if isinstance(eventTime, Time): pass
1094 else:
1095 raise AssertionError("Expecting a Time object as argument. Found a "
1096 + eventTime.__class__.__name__ + " object.")
1097
1098 timedelta = self.datetime - eventTime.datetime
1099 return Period( abs(timedelta) )
1100
1101
1102
1103
1104
1105
1107 """
1108 Determine the difference (a Period) between myself and the time of some event.
1109 @rtype: tuple
1110 @note:
1111 returns the difference between two Time objects as a tuple of
1112 (years, Period)
1113 """
1114 if isinstance(eventTime, Time): pass
1115 else:
1116 raise AssertionError("Expecting a Time object as argument. Found a "
1117 + eventTime.__class__.__name__ + " object.")
1118
1119 laterTime = self
1120 earlierTime = eventTime
1121
1122 if earlierTime > laterTime:
1123 laterTime, earlierTime = earlierTime, laterTime
1124
1125 anniversaryTime = laterTime.anniversaryOf(earlierTime)
1126
1127 elapsedYears = anniversaryTime.year - earlierTime.year
1128 periodSinceAnniversary = laterTime - anniversaryTime
1129
1130 return elapsedYears, periodSinceAnniversary
1131
1132 diffy = diffyears
1133
1134
1135
1136
1137
1139 """
1140 Determine the difference (a Period) between myself and the time of some event.
1141 @rtype: tuple
1142 @note:
1143 returns the difference between two Time objects as a tuple of
1144 (years,months, Period)
1145 """
1146 if isinstance(eventTime, Time): pass
1147 else:
1148 raise AssertionError("Expecting a Time object as argument. Found a "
1149 + eventTime.__class__.__name__ + " object.")
1150
1151 years, days = self.diffmonths(eventTime)
1152 years, months = divmod(years,12)
1153 return years, months, days
1154 diffym = diffyearsmonths
1155
1156
1157
1158
1160 """
1161 Determine the difference (a Period) between myself and the time of some event.
1162 @rtype: tuple
1163 @examle:
1164 Returns the difference between two Time objects as a tuple of
1165 (months, Period), so the Period part can be formatted as desired::
1166 personDateOfBirth = Time(1946,5,8,6,45)
1167 months, days = Time().diffm(personDateOfBirth)
1168 print ("He is " + str(months) + " " + str(days) + " old.")
1169
1170 """
1171 if isinstance(eventTime, Time): pass
1172 else:
1173 raise AssertionError("Expecting a Time object as argument. Found a "
1174 + eventTime.__class__.__name__ + " object.")
1175
1176 laterTime = self
1177 earlierTime = eventTime
1178
1179 if earlierTime > laterTime:
1180 laterTime, earlierTime = earlierTime, laterTime
1181
1182 anniversaryTime = earlierTime.goto(year=laterTime.year,month=laterTime.month)
1183
1184
1185 if anniversaryTime <= laterTime: pass
1186 else:
1187 lastMonth = laterTime.minus(months=1)
1188 anniversaryTime = earlierTime.goto(year=lastMonth.year,month=lastMonth.month)
1189
1190
1191 earlierNumericMonth = CalendarMonthToNumericMonth(earlierTime.year, earlierTime.month)
1192 anniversaryNumericMonth = CalendarMonthToNumericMonth(anniversaryTime.year, anniversaryTime.month)
1193 elapsedMonths = anniversaryNumericMonth - earlierNumericMonth
1194
1195 periodSinceAnniversary = laterTime - anniversaryTime
1196
1197 return elapsedMonths, periodSinceAnniversary
1198
1199 diffm = diffmonths
1200
1201
1202
1203
1204
1206 """
1207 return a new Time object which has been moved so it does not fall
1208 on a Saturday or Sunday.
1209
1210 @rtype: Time
1211 @type direction: string
1212
1213 @arg direction: the direction in which to exit the weekend::
1214 @note:
1215 If the weekday of the current object occurs on the weekend, the
1216 weekday of the new object is moved out of the weekend::
1217 if <direction> is NEXT , the day is moved to the following Monday.
1218 if <direction> is PREVIOUS, the day is moved to the previous Friday.
1219 if <direction> is None , then
1220 - a Saturday is moved to the previous Friday
1221 - a Sunday is moved to the following Monday
1222 """
1223 if self.weekday == 6:
1224 if direction == NEXT: return self.add(days=2)
1225 return self.add(days=-1)
1226
1227 elif self.weekday == 7:
1228 if direction == PREVIOUS: return self.add(days=-2)
1229 return self.add(days=1)
1230
1231 else:
1232 return self.clone()
1233
1234
1235
1236
1237
1238
1239 - def flex(self, baseTime):
1240 """
1241 A method for adding days to a date, based on a base date. If the
1242 day-of-month of my time is less than the day-of-month of the baseTime,
1243 then calculate the number of days difference and add them to my time.
1244
1245 This method is designed to be used when doing monthly arithmetic.
1246 In the following example, we add 1 month to January 31. Because
1247 February contains only 28 days, we get February 28.
1248
1249 If we want to get the counterpart of January 31 in February
1250 (February 31, which resolves to March 3) we can "flex" the
1251 date based on the base date of January 31.
1252
1253 >>> startDate = Time(2003,1,31)
1254 >>> startDate.d
1255 'January 31, 2003'
1256 >>> startDate.add(months=1).d
1257 'February 28, 2003'
1258 >>> startDate.add(months=1).flex(startDate).d
1259 'March 3, 2003'
1260
1261 @rtype: Time
1262 @return: a new Time object that has been "flexed" based on the baseTime
1263 """
1264 if baseTime.day > self.day:
1265 number_of_lost_days = baseTime.day - self.day
1266 return self.add(days=number_of_lost_days)
1267 else:
1268 return self
1269
1270
1271
1272
1273
1275 """
1276 @return: a new Time object with the datetime of the last modification
1277 date of the file with <filename>.
1278 @rtype: Time
1279 @arg filename: a filename
1280 @type filename: string
1281 """
1282 timestamp = os.path.getmtime(filename)
1283 return self.fromTimestamp(timestamp)
1284
1285
1286
1287
1288
1290 """
1291 @return: a new Time object with the datetime from <timestamp>.
1292 @rtype: Time
1293 @arg timestamp: a timestamp
1294 @type timestamp: timestamp
1295 @see: the datetime module for documentation
1296 """
1297 return Time(datetime.datetime.fromtimestamp(timestamp))
1298
1299
1300
1301
1303 """
1304 Return a string containing the civildate.
1305 @rtype: string
1306 @return: the civildate, e.g.::
1307 "April 30, 2007"
1308 """
1309 return self.get_d()
1310 civildate = property(get_civildate)
1311
1312
1313
1314
1316 """
1317 A utility method for other civiltime methods.
1318 @rtype: tuple
1319 @return: a tuple containing the components of civiltime::
1320 (hour, minute, second, am_pm_flag)
1321 """
1322 if TimeExpressedIn24Hours:
1323 return self.hour,self.minute,self.second,""
1324
1325
1326
1327 am = kwargs.get("am", "am")
1328 pm = kwargs.get("pm", "pm")
1329
1330 hour = self.hour
1331 if self.hour == 0: hour = 12
1332 elif self.hour > 12: hour = self.hour -12
1333
1334 if self.hour < 12: return hour,self.minute,self.second,am
1335 else: return hour,self.minute,self.second,pm
1336
1337 civiltimebase = property(__getCiviltimeBase)
1338
1339
1340
1341
1342
1344 """
1345 Return a string containing the civil time.
1346
1347 If keyword arg showseconds=True, time will include seconds.
1348
1349 If keyword arg showHundredthsOfSeconds=True, time will include hundredths of seconds.
1350
1351 Note that hundredths of seconds are faked to "00".
1352 This is primarily for MS-DOS timestamps which show hundredths of seconds,
1353 even though the clocks of PCs are not accurate to hundredths of seconds.
1354
1355 @rtype: string
1356 @return: a string containing the civil time, e.g.::
1357 "6:30pm"
1358
1359 @note:
1360 Civil time as used in United States of America::
1361 midnight = 12:00am
1362 time between midnight and noon = am
1363 noon = 12:00pm
1364 time between noon and midnight = am
1365
1366 @type showseconds: boolean
1367 @keyword showseconds: defaults to False.
1368
1369 @type showHundredthsOfSeconds: boolean
1370 @keyword showHundredthsOfSeconds: defaults to False.
1371 """
1372 showseconds = kwargs.get("showseconds", False)
1373 showHundredthsOfSeconds = kwargs.get("showHundredthsOfSeconds", False)
1374
1375 if showHundredthsOfSeconds: showseconds = True
1376
1377 hour, minute,second,ampm = self.__getCiviltimeBase(**kwargs)
1378
1379 result = str(hour) + CivilTimeSeparator + str(minute).zfill(2)
1380 if showseconds: result += CivilTimeSeparator + str(second).zfill(2)
1381 if showHundredthsOfSeconds: result = result + CivilTimeSeparator + "00"
1382
1383 return result + ampm
1384
1385
1386 civiltime = property(get_civiltime)
1387 t = property(get_civiltime)
1388
1389
1390
1391
1393 """
1394 Return a string containing the civil time (including seconds.)
1395 @rtype: string
1396 @return: a string containing the civil time including seconds,
1397 e.g.::
1398 "6:30:45pm"
1399 """
1400 return self.get_civiltime(showseconds=True)
1401 civiltime2 = property(get_civiltime2)
1402 t2 = property(get_civiltime2)
1403
1404
1405
1406
1408 """
1409 Return a string containing the civildate.
1410 @rtype: string
1411 @return: the civildate, e.g.::
1412 "April 30, 2007"
1413 """
1414 result = []
1415 for character in CivilDateFormat:
1416 if character == "m": result.append(self.monthname)
1417 elif character == "d": result.append(str(self.day))
1418 elif character == "y": result.append(str(self.year))
1419 else : result.append(character)
1420 return "".join(result)
1421
1422 d = property(get_d)
1423
1424
1425
1426
1427
1429 """
1430 Return the datetime in the format used by Microsoft's MS-DOS.
1431 @rtype: string
1432 @return: the datetime in the format used by Microsoft's MS-DOS
1433 """
1434 return self.get_civiltime(am="a",pm="p",showHundredthsOfSeconds=True)
1435 dostime = property(get_dostime)
1436
1437
1438
1439
1440
1442 """
1443 Return the day part of the datetime.
1444 @return: day
1445 @rtype: int
1446 """
1447 return self.datetime.day
1448 day = property(get_day)
1449
1450
1451
1452
1453
1455 """
1456 Return a string containing the civildate and the time, e.g. "April 30, 2007 6:30pm".
1457 @rtype: string
1458 @return: the civildate and the time, e.g.::
1459 "April 30, 2007 6:30pm"
1460 """
1461 return self.d + " " + self.civiltime
1462 dt = property(get_dt)
1463
1464
1465
1466
1468 """
1469 Return a string containing the civildate and the time (including seconds)
1470 e.g. "April 30, 2007 6:30:45pm".
1471
1472 @rtype: string
1473 @return: the civildate and the time, e.g.::
1474 "April 30, 2007 6:30:45pm"
1475 """
1476 return self.d + " " + self.civiltime2
1477 dt2 = property(get_dt2)
1478
1479
1480
1481
1483 """
1484 Return the hour portion of the Time, as an int.
1485 @return: hour
1486 @rtype: int
1487 """
1488 return self.datetime.hour
1489 hour = property(get_hour)
1490
1491
1492
1493
1494
1496 """
1497 Return a string containing an ISO date in format yyyy-mm-dd, e.g. "2007-10-09"
1498 @rtype: string
1499 @return: an ISO date in format yyyy-mm-dd, e.g.::
1500 "2007-10-09" # Oct 9, 2007
1501
1502 @type sep: string
1503 @arg sep: separator string to use. Defaults to the ISO standard: a dash.
1504 """
1505 return (
1506 str(self.year).zfill(4)
1507 + sep
1508 + str(self.month).zfill(2)
1509 + sep
1510 + str(self.day).zfill(2)
1511 )
1512 isodate = property(get_isodate)
1513
1514
1515
1516
1518 """
1519 Return a string containing ISO time in format hh:mm:ss, e.g. "11:15:00".
1520 @rtype: string
1521 @return: an ISO time in format hh:mm:ss, e.g.::
1522 "11:15:00" # 11:15 in the morning
1523
1524 @type sep: string
1525 @arg sep: separator string to use. Defaults to the ISO standard: a colon.
1526 """
1527 return (
1528 str(self.hour).zfill(2)
1529 + sep
1530 + str(self.minute).zfill(2)
1531 + sep
1532 + str(self.second).zfill(2)
1533 )
1534 isotime = property(get_isotime)
1535
1536
1537
1538
1540 """
1541 Return a string containing an ISO datetime in format yyyy-mm-ddThh:mm:ss.
1542 @rtype: string
1543 @return: an ISO datetime in format yyyy-mm-ddThh:mm:ss, e.g.::
1544 "2007-10-09T11:15:00" # Oct 9, 2007 at 11:15 in the morning
1545
1546 @type sep: string
1547 @arg sep: separator string to use between date and time.
1548 Default is the ISO standard, a capital letter "T".
1549 @arg datesep: separator string to use within date.
1550 Default is the ISO standard, a dash "-".
1551 @arg timesep: separator string to use within time.
1552 Default is the ISO standard, a colon ":".
1553 @arg seps: separator string to use within and between date and time.
1554 If specified, seps over-rides the other separators.
1555 Note that the value for seps may be a zero-length string.
1556 "seps" is useful for constructing datetime strings for things like
1557 datestamps and filenames.
1558 """
1559 if seps != None:
1560 sep = datesep = timesep = seps
1561 return self.get_isodate(sep=datesep) + sep + self.get_isotime(sep=timesep)
1562 isodatetime = property(get_isodatetime)
1563
1564
1565
1566
1568 """
1569 Return a string containing the ISO datetime in a format suitable for making a filename.
1570 @rtype: string
1571 @return: the ISO datetime in a format suitable for making a filename. E.g.::
1572 "2007-10-09-11-15-00" # Oct 9, 2007 at 11:15 in the morning
1573
1574 @note: All parts of the datetime will be separated by dashes.
1575 @note: A collection of these strings, sorted using a standard string sort,
1576 will sort in temporal order.
1577
1578 @type sep: string
1579 @arg sep: separator string to use between datetime parts. Default = "-"
1580 """
1581 return self.get_isodate(sep) + sep + self.get_isotime(sep)
1582 isofilename = property(get_isofilename)
1583
1584
1585
1586
1587
1589 """
1590 Return the ISO weekday number as an int, where Monday=1 .. Sunday=7
1591 @rtype: int
1592 @return: the ISO weekday number, e.g.::
1593 1 # first day of the week, Monday
1594 """
1595 return self.datetime.isoweekday()
1596 isoweekday = property(get_isoweekday)
1597 weekday = property(get_isoweekday)
1598
1599
1600
1601
1602
1604 """
1605 Return the ISO week number, as an int.
1606 @rtype: int
1607 @return: the ISO week number, e.g.::
1608 1 # first week of the year
1609 """
1610 year, weeknumber, weekday = self.datetime.isocalendar()
1611 return weeknumber
1612 weeknumber = property(get_isoweeknumber)
1613 isoweeknumber = property(get_isoweeknumber)
1614
1615
1616
1617
1619 """
1620 Return the minute portion of a Time, as an int.
1621 @rtype: int
1622 @return: minute, e.g.::
1623 15 # 15 minutes past the beginning of the hour
1624 """
1625 return self.datetime.minute
1626 minute = property(get_minute)
1627
1628
1629
1630
1631
1633 """
1634 Return the month portion of a Time, as an int.
1635 @rtype: int
1636 @return: month, e.g.::
1637 12 # for the 12th month, December
1638 """
1639 return self.datetime.month
1640 month = property(get_month)
1641
1642
1643
1644
1645
1647 """
1648 Return a string containing the natural language name of the month.
1649 @rtype: string
1650 @return: monthname, e.g.::
1651 "December"
1652 """
1653 return MONTH_NAMES[self.month]
1654 monthname = property(get_monthname)
1655 m = property(get_monthname)
1656
1657
1658
1659
1660
1662 """
1663 Return the second portion of a Time, as an int.
1664 @return: second
1665 @rtype: int
1666 """
1667 return self.datetime.second
1668 second = property(get_second)
1669
1670
1671
1672
1673
1675 """
1676 Return a string containing the time and the civil date.
1677 @rtype: string
1678 @return: the time and the civildate, e.g.::
1679 "6:30pm April 30, 2007"
1680 """
1681 return self.civiltime + " " + self.d
1682 td = property(get_td)
1683
1684
1685
1686
1687
1688
1690 """
1691 Return a string containing the time and the civil date
1692 (including seconds).
1693 @rtype: string
1694 @return: the time and the civildate, e.g.::
1695 "6:30:45pm April 30, 2007"
1696 """
1697 return self.civiltime2 + " " + self.d
1698 t2d = property(get_t2d)
1699
1700
1701
1702
1704 """
1705 Return a tuple containing the parts of the datetime.
1706 @rtype: tuple
1707 @return: myself formatted as a 6-tuple of ints (year, month, day, hour, minute, second).
1708 E.g.::
1709 (2007,10,9,11,15,0) # Oct 9, 2007 at 11:15 in the morning
1710 """
1711 return self.year,self.month,self.day,self.hour,self.minute,self.second
1712
1713
1714
1715
1716
1718 """
1719 Return a string containing the time, the weekday name, and the civildate.
1720 @rtype: string
1721 @return: the time, the weekday name, and the civildate, e.g.::
1722 "6:30pm Monday April 30, 2007"
1723 """
1724 return self.civiltime + " " + self.weekdayname + " " + self.d
1725 twd = property(get_twd)
1726
1727
1728
1729
1730
1732 """
1733 Return a string containing the time (including seconds),
1734 the weekday name, and the civildate.
1735 @rtype: string
1736 @return: the time (including seconds), the weekday name, and the civildate, e.g.::
1737 "6:30:45pm Monday April 30, 2007"
1738 """
1739 return self.civiltime2 + " " + self.weekdayname + " " + self.d
1740 t2wd = property(get_t2wd)
1741
1742
1743
1744
1746 """
1747 Return a string containing a Unix date, e.g. "07-DEC-2006".
1748 @rtype: string
1749 @return: a string containing a Unix date, e.g.::
1750 "07-DEC-2006"
1751
1752 @type sep: string
1753 @arg sep: separator string to use. Defaults to a dash.
1754 """
1755 return (
1756 str(self.day).zfill(2)
1757 + sep
1758 + self.monthname.upper()[:3]
1759 + sep
1760 + str(self.year).zfill(4)
1761 )
1762 unixdate = property(get_unixdate)
1763
1764
1765
1766
1768 """
1769 Returns the natural language name of the day of the week.
1770 @rtype: string
1771 @return: the natural language name of the day of the week, e.g.::
1772 "Monday"
1773
1774 @type weekday: int
1775 @arg weekday: the ISO weekday number. If not specified, defaults
1776 to the weekday of self (this Time object).
1777 """
1778 if weekday: return WEEKDAY_NAMES[weekday]
1779 return WEEKDAY_NAMES[self.weekday]
1780
1781 weekdayname = property(get_weekdayname)
1782 w = property(get_weekdayname)
1783
1784
1785
1786
1787
1789 """
1790 Returns a string containing the weekday name and the civildate.
1791 @rtype: string
1792 @return: the weekday name and the civildate.
1793 e.g.::
1794 "Monday April 30, 2007"
1795 """
1796 return self.weekdayname + " " + self.d
1797 wd = property(get_wd)
1798
1799
1800
1801
1802
1804 """
1805 Returns a string containing the weekday name, the civildate, and the time.
1806 @rtype: string
1807 @return: the weekday name, the civildate, and the time, e.g.::
1808 "Monday April 30, 2007 6:30pm"
1809 """
1810 return self.weekdayname + " " + self.d + " " + self.civiltime
1811 wdt = property(get_wdt)
1812
1813
1814
1815
1817 """
1818 Returns a string containing the weekday name, the civildate,
1819 and the time (including seconds).
1820 @rtype: string
1821 @return: the weekday name, the civildate, and the time, e.g.::
1822 "Monday April 30, 2007 6:30:45pm"
1823 """
1824 return self.weekdayname + " " + self.d + " " + self.civiltime2
1825 wdt2 = property(get_wdt2)
1826
1827
1828
1829
1831 """
1832 Return the year as an int, e.g. 2007
1833 @rtype: int
1834 @return: year, e.g.::
1835 2007
1836 """
1837 return self.datetime.year
1838 year = property(get_year)
1839
1840
1841
1842
1843
1845 """
1846 Return the year as a string, e.g. "2007"
1847 @rtype: string
1848 @return: year,
1849 e.g.::
1850 "2007"
1851 """
1852 return str(self.datetime.year)
1853 y = property(get_yearstring)
1854
1855
1856
1857
1858
1859
1860 - def goto(self,**kwargs):
1861 """
1862 Returns a clone of self but with some component(s)
1863 (year, month, day, hour, minute, second) reset to a new value.
1864
1865 @rtype: Time
1866 @return: a new Time object in which the time has been moved
1867 according to the keyword args
1868
1869 @note:
1870 A "goto" can fail in a number of ways.
1871 - The current date is February 29, 2004 (a leap year) and the
1872 year is reset to 2007 (not a leap year).
1873 - The current date is January 31 and the month is reset to
1874 September (which has only 30 days).
1875 @note: This method attempts to recover from such failures by decrementing
1876 the "day" value to 28 before failing.
1877
1878 @note: Example:
1879 To obtain a Time object whose date is May 15, 2003::
1880 t = Time(2007,5,15)
1881 t2 = t.goto(year=2003)
1882
1883 @note: Example:
1884 To obtain a time object whose date is February 28, 2007::
1885 t = Time(2004,2,29) # a leap year
1886 t2 = t.goto(year=2007) # not a leap year
1887 """
1888 year = kwargs.get("year",self.year)
1889 month = kwargs.get("month",self.month)
1890 day = kwargs.get("day",self.day)
1891
1892
1893 if day > NORMAL_DAYS_IN_MONTH[month]:
1894 day = NORMAL_DAYS_IN_MONTH[month]
1895
1896
1897 possible_day = day
1898 while month == 2 and (possible_day == 28 or possible_day == 29):
1899 try:
1900 return Time(year,month, possible_day
1901 , kwargs.get("hour",self.hour)
1902 , kwargs.get("minute",self.minute)
1903 , kwargs.get("second",self.second)
1904 )
1905 except ValueError: pass
1906 possible_day = possible_day -1
1907
1908
1909 return Time(year,month,day
1910 , kwargs.get("hour",self.hour)
1911 , kwargs.get("minute",self.minute)
1912 , kwargs.get("second",self.second)
1913 )
1914
1915
1916
1917
1918
1920 """
1921 Returns a new Time object in which the month has been moved to the specified
1922 argMonth.
1923
1924 @rtype: Time
1925 @return: a new Time object in which the month has been moved to the specified
1926 month number.
1927
1928 @type argMonth: int
1929 @arg argMonth:
1930 The argMonth number should be specified by means
1931 of one of pyfdate's month number constants (JANUARY, FEBRUARY, etc.)
1932
1933 @type direction: string
1934 @arg direction: The direction in time::
1935 If direction == NEXT , we will move forward in time to the following month
1936 If direction == PREVIOUS, we will move backward in time to the preceding month
1937 If direction == NEAREST , we will move to the nearest month
1938
1939 @type useToday: boolean
1940 @keyword useToday:
1941 If the current month is the same as argMonth, the value of the
1942 useToday flag (True or False) will determine whether or not we use
1943 the current month, or ignore it, in our search for the NEXT, PREVIOUS, or NEAREST month.
1944
1945 @note: Example:
1946 If this month is April, to move the date to the following November::
1947 t = Time().gotoMonth(NOVEMBER)
1948 or
1949 t = Time().gotoMonth(NOVEMBER,NEXT)
1950
1951 @note: Example:
1952 If this month is April, to move the date to the previous November::
1953 t = Time().gotoMonth(NOVEMBER, PREVIOUS)
1954
1955
1956 @note: Example:
1957 If this month is April, to move the date to the nearest November::
1958 t = Time().gotoMonth(NOVEMBER, NEAREST)
1959
1960 @note:
1961 Question::
1962 If today is in November and we want to go to the NEXT (or PREVIOUS) November,
1963 is today considered to be in the next (or previous) November?
1964 Answer::
1965 If useToday == True: (which is the default)
1966 today is considered to be in the nearest November.
1967 else: (useToday=False has been specified as a keyword arg)
1968 today is ignored in determining the NEXT (or PREVIOUS) November.
1969
1970 Question::
1971 If today is November and we want to go to the NEAREST November
1972 and we have specified useToday=False,
1973 which is the nearest November: last November or next November?
1974 Answer::
1975 NEXT (i.e. the future) November is considered nearest.
1976 """
1977 useToday = kwargs.get("useToday", True)
1978
1979 if direction == NEAREST : return self.__gotoNearestMonth(argMonth,useToday)
1980 elif direction == NEXT : increment = +1
1981 elif direction == PREVIOUS: increment = -1
1982 else:
1983 raise AssertionError("Invalid argument for direction: " + str(direction))
1984
1985 m = CalendarMonthToNumericMonth(self.year, self.month)
1986 newYear, newMonth = NumericMonthToCalendarMonth(m)
1987
1988 if self.month == argMonth:
1989 if useToday:
1990 return self.clone()
1991 else:
1992
1993 m += increment
1994 newYear, newMonth = NumericMonthToCalendarMonth(m)
1995
1996 while newMonth != argMonth:
1997 m += increment
1998 newYear, newMonth = NumericMonthToCalendarMonth(m)
1999
2000 newTime = Time(newYear, newMonth, self.day, self.hour, self.minute, self.second)
2001 return newTime
2002
2003
2004
2005
2007 """
2008 Return a new Time object in which the time has been moved to the beginning of the month.
2009 @rtype: Time
2010 @return: a new Time object in which the time has been moved to the beginning of the month.
2011 @note:
2012 Example:
2013 If today is May 15, 2007 then to obtain a time object whose date is May 1::
2014 t = Time().gotoMonthBegin()
2015 """
2016 return Time(self.year, self.month, 1, self.hour, self.minute, self.second)
2017
2018
2019
2020
2022 """
2023 Return a new Time object in which the time has been moved to the end of the month.
2024 @rtype: Time
2025 @return: a new Time object in which the time has been moved to the end of the month.
2026
2027 @note: Example:
2028 If today is May 15, 2007 then to obtain a time object whose date is May 31::
2029 t = Time().gotoMonthEnd()
2030 """
2031
2032
2033
2034
2035 return self.gotoMonthBegin().plus(months=1,days=-1)
2036
2037
2038
2039
2041 """
2042 Return a new Time object in which the time has been moved to the beginning of year.
2043 @rtype: Time
2044 @return: a new Time object in which the time has been moved to the beginning of year.
2045 @note: Example:
2046 If today is May 15, 2007 then to obtain a time object whose date is January 1, 2007::
2047 t = Time().gotoYearBegin()
2048 """
2049 return Time(self.year, 1,1, self.hour,self.minute,self.second)
2050
2051
2052
2053
2055 """
2056 Return a new Time object in which the time has been moved to the end of the year.
2057 @rtype: Time
2058 @return: a new Time object in which the time has been moved to the end of the year.
2059 @note: Example:
2060 If today is May 15, 2007 then to obtain a time object whose date is December 31, 2007::
2061 t = Time().gotoYearEnd()
2062 """
2063
2064 return Time(self.year+1, 1,1, self.hour,self.minute,self.second).plus(days=-1)
2065
2066
2067
2068
2070 """
2071 Return a new Time object in which the month has been moved
2072 (forward or backward) to the closest month with month number <month>.
2073
2074 @rtype: Time
2075 @return:
2076 a new Time object in which the month has been moved
2077 (forward or backward) to the closest month with month number <month>.
2078
2079 @type month: int
2080 @arg month: the monthnumber of the the desired month.
2081 The monthnumber should be specified by means
2082 of one of pyfdate's month number constants.
2083
2084 @type useTodayFlag: boolean
2085 @arg useTodayFlag: specifies what to do it the current monthnumber is
2086 the same as the desired monthnumber::
2087 If useTodayFlag == True:
2088 return the current month
2089 else:
2090 return the month one year in the future.
2091
2092 @note:
2093 If the NEXT month with monthnumber <month>
2094 and the PREVIOUS month with monthnumber <month>
2095 are both exactly six months from the current month,
2096 then return the NEXT month with monthnumber <month>.
2097
2098 @note: Example:
2099 t = Time().__gotoNearestMonth(JULY)
2100 """
2101 if self.month == month:
2102 if useTodayFlag:
2103 return self.clone()
2104 else:
2105 return self.add(years=1)
2106
2107 else:
2108 timeNext = self.gotoMonth(month, NEXT , useToday=False)
2109 timePrev = self.gotoMonth(month, PREVIOUS, useToday=False)
2110
2111 monthnumSelf = CalendarMonthToNumericMonth(self.year, self.month)
2112 monthnumNext = CalendarMonthToNumericMonth(timeNext.year, timeNext.month)
2113 monthnumPrev = CalendarMonthToNumericMonth(timePrev.year, timePrev.month)
2114
2115 futureDifference = abs(monthnumSelf - monthnumNext)
2116 pastDifference = abs(monthnumSelf - monthnumPrev)
2117
2118 if futureDifference <= pastDifference: return timeNext
2119 else: return timePrev
2120
2121
2122
2123
2124
2126 """
2127 Return a new Time object in which the weekday has been moved
2128 (forward or backward) to the closest weekday with weekday number <weekday>.
2129
2130 @note:
2131 If the current weekday is already <weekday>::
2132 if useToday == False
2133 return the NEXT <weekday>
2134 else
2135 return a clone of the current Time object.
2136
2137 @rtype: Time
2138 @note:
2139 <weekday> should be specified by means
2140 of one of pyfdate's weekday number constants.
2141
2142 @note: Example::
2143 t = Time().movetoNearestWeekday(THURSDAY)
2144 """
2145 if self.weekday == weekday:
2146 if useTodayFlag:
2147 return self.clone()
2148 else:
2149 return self.add(weeks=1)
2150
2151 else:
2152 futureWeekday = self.gotoWeekday(weekday, NEXT)
2153 pastWeekday = self.gotoWeekday(weekday, PREVIOUS)
2154
2155 futureDifference = futureWeekday - self
2156 pastDifference = self - pastWeekday
2157
2158 if futureDifference <= pastDifference: return futureWeekday
2159 else: return pastWeekday
2160
2161
2162
2163
2165 """
2166 Return a new Time object in which
2167 the date has been moved to the specified argWeekday.
2168
2169 @rtype: Time
2170 @return: a new Time object in which the time has been moved to the specified weekday.
2171
2172 @type argWeekday: int
2173 @arg argWeekday:
2174 The argWeekday number should be specified by means
2175 of one of pyfdate's weekday number constants (MONDAY, TUESDAY, etc.)
2176
2177 @type direction: string
2178 @arg direction: The direction in time::
2179 If direction == NEXT , we will move forward in time to the following weekday
2180 If direction == PREVIOUS, we will move backward in time to the preceding weekday
2181 If direction == NEAREST , we will move to the nearest weekday
2182
2183 @type useToday: boolean
2184 @keyword useToday:
2185 If the current weekday is the same as argWeekday, the value of the
2186 useToday flag (True or False) will determine whether we use the current date,
2187 or ignore it, in our search for the NEXT, PREVIOUS, or NEAREST weekday.
2188 """
2189 """
2190
2191 @note:
2192 Example:
2193 If today is Tuesday::
2194 t = Time().gotoWeekday(THURSDAY)
2195 or
2196 t = Time().gotoWeekday(THURSDAY,NEXT)
2197 will move the date to the following Thursday.
2198
2199 @note:
2200 Example:
2201 If today is Tuesday::
2202 t = Time().gotoWeekday(THURSDAY, PREVIOUS)
2203 will move the date to the previous Thursday.
2204
2205 @note:
2206 Example:
2207 t = Time().gotoWeekday(THURSDAY,NEAREST)
2208 will move the date to the nearest Thursday.
2209
2210 @note:
2211 Question: If today is Thursday and we want to go to the NEXT (or PREVIOUS) Thursday:
2212 Is today considered to be the next (or previous) Thursday?
2213
2214 Answer::
2215 If useToday == True: (which is the default)
2216 today is considered the nearest Thursday.
2217 else: (useToday=False has been specified as a keyword arg)
2218 today is ignored in determining the NEXT (or PREVIOUS) Thursday.
2219
2220 Question: if today is Thursday and we want to go to the NEAREST Thursday
2221 and we have specified useToday=False:
2222 Which is the nearest Thursday: last Thursday or next Thursday?
2223 Answer:
2224 NEXT (i.e. the future) Thursday is considered nearest.
2225 """
2226 useToday = kwargs.get("useToday", True)
2227
2228 if direction == NEAREST : return self.__gotoNearestWeekday(argWeekday, useToday)
2229 elif direction == NEXT : increment = +1
2230 elif direction == PREVIOUS: increment = -1
2231 else:
2232 raise AssertionError("Invalid argument for direction: " + str(direction))
2233
2234 if self.weekday == argWeekday:
2235 if useToday:
2236 return self.clone()
2237 else:
2238 return self.plus(weeks=increment)
2239 else:
2240 newTime = self.clone()
2241 while newTime.weekday != argWeekday:
2242 newTime = newTime.plus(days=increment)
2243 return newTime
2244
2245
2246
2247
2248
2250 """
2251 Return a boolean indicating whether the year is or is not a leap year.
2252
2253 @return: True if a <year> is a leap year; otherwise return False.
2254 @rtype: boolean
2255 """
2256 return isLeapYear(self.year)
2257
2258
2259
2260
2261
2263 """
2264 Subtract some amounts of time from the current time. Syntactic sugar
2265 for cases in which you don't want to "add" negative amounts of time.
2266
2267 @rtype: Time
2268 @return: a new Time object in which the time has been moved by the specified amounts.
2269
2270 Coding Example::
2271 t1 = Time()
2272 t2 = t1.subtract(weeks=2,days=3,hours=4,minutes=99, seconds=1)
2273 """
2274 for key,value in kwargs.items():
2275 kwargs[key] = value * -1
2276
2277 return self.add(**kwargs)
2278
2279
2280 minus = subtract
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2298 """
2299 A utility routine for showing arguments in error messages.
2300 Useful for debugging.
2301
2302 >>> from pyfdate import *
2303 >>> s = argsToString("test",2,Time())
2304 >>> print s
2305 arg 1: "test" <type 'str'>
2306 arg 2: 2 <type 'int'>
2307 arg 3: 2008-01-01 14:40:18 <type 'instance'>
2308
2309 @rtype: string
2310 """
2311 s = ""
2312 for index, arg in enumerate(args):
2313 argnum = index + 1
2314 if type(arg) == type("aaa"): arg = '"%s"' % arg
2315
2316 s += ("arg " + str(argnum).rjust(5)
2317 + ': '
2318 + str(arg)
2319 + ' '
2320 + str(type(arg))
2321 + '\n'
2322 )
2323 return s
2324
2325
2326
2327
2329 """
2330 A utility routine for showing keyword arguments in error messages.
2331 Useful for debugging.
2332
2333 >>> from pyfdate import *
2334 >>> s = kwargsToString(first="test", second=2, third=Time())
2335 >>> print s
2336 first : "test" <type 'str'>
2337 second : 2 <type 'int'>
2338 third : 2008-01-01 14:36:38 <type 'instance'>
2339
2340 @rtype: string
2341 """
2342 s = ""
2343 keys = kwargs.keys()
2344 keys.sort()
2345
2346 for key in keys:
2347 value = kwargs[key]
2348 if type(value) == type("aaa"): value = '"%s"' % value
2349 s += (key.ljust(10)
2350 + ': '
2351 + str(value)
2352 + ' '
2353 + str(type(value))
2354 + '\n')
2355 return s
2356
2357
2358
2359
2360
2362 """
2363 Convert a calendar month (year,month)
2364 to a numeric representation:
2365
2366 >>> CalendarMonthToNumericMonth(2007,4)
2367 24075
2368
2369 @rtype: int
2370 """
2371 elapsedYears = year -1
2372 elapsedMonths = month -1
2373 return (elapsedYears * 12) + elapsedMonths
2374
2375
2376
2377
2379 """
2380 Convert a numeric representation of a month
2381 to a calendar month (year, month).
2382
2383 >>> NumericMonthToCalendarMonth(24075)
2384 (2007, 4)
2385
2386 @rtype: tuple
2387 """
2388 elapsedYears, elapsedMonths = divmod(months, 12)
2389 year = elapsedYears + 1
2390 month = elapsedMonths + 1
2391 return year, month
2392
2393
2394
2395
2396
2398 """
2399 Return True if year is a leapyear; otherwise return False.
2400
2401 >>> isLeapYear(2004)
2402 True
2403 >>> isLeapYear(2000)
2404 True
2405 >>> isLeapYear(2005)
2406 False
2407
2408 @rtype: boolean
2409 @return: True if year is a leapyear; otherwise return False.
2410 """
2411 try:
2412
2413 d = datetime.datetime(year,2,29)
2414 return True
2415 except ValueError:
2416 return False
2417
2418
2419
2420
2421
2423 """
2424 Convert an hour expressed as am/pm into one expressed in 24 hour time.
2425
2426 >>> to24hour(12,"am")
2427 0
2428 >>> to24hour(1,"am")
2429 1
2430 >>> to24hour(12,"PM")
2431 12
2432 >>> to24hour(1,"PM")
2433 13
2434
2435 @rtype: int
2436 @return: the number of of an hour in 24-hour (aka "military") time.
2437
2438 @type hour: int
2439 @arg hour: must be an integer (or a string that can be converted to an integer)
2440 in the range of 1 to 12
2441
2442 @type ampm: string
2443 @arg ampm: must be a string containing either "am" or "pm" (in upper or lower case)
2444 """
2445 try:
2446 hour = int(hour)
2447 except:
2448 raise AssertionError('to24hour() function receives an '
2449 + 'invalid value for hour argument: "' + str(hour) + '"')
2450
2451 ampm = ampm.lower()
2452
2453 assert hour < 13
2454 assert hour > 0
2455 assert ampm in ("am","pm")
2456
2457 if hour == 12 and ampm == 'am': return 0
2458 if hour == 12 and ampm == 'pm': return 12
2459 if ampm == 'pm': return hour + 12
2460 return hour
2461
2463 """
2464 split a string into its numeric parts and return
2465 a list containing the numeric parts converted to ints.
2466
2467 This function can be used to parse a string containing
2468 an ISO datetime.
2469
2470 >>> from pyfdate import *
2471 >>> numsplit("2007_10_09")
2472 [2007, 10, 9]
2473 >>> numsplit("2007-10-09T23:45:59")
2474 [2007, 10, 9, 23, 45, 59]
2475 >>> numsplit("2007/10/09 23.45.59")
2476 [2007, 10, 9, 23, 45, 59]
2477 """
2478 s = list(s)
2479 for i in range(len(s)):
2480 if not s[i] in "0123456789": s[i] = " "
2481 return [int(x) for x in "".join(s).split()]
2482
2483
2484
2485
2486
2487 if __name__ == "__main__":
2488 print("pyfdate language is " + LANG)
2489 t = Time()
2490 print t.t
2491 print t.td
2492 print t.twd
2493
2494 print t.dt
2495 print t.dt2
2496
2497 print t.wdt
2498 print t.wdt2
2499
2500 print t.dt + " " + t.w
2501 print t.dt2 + " " + t.w
2502 print t.m + " " + t.y
2503
2504 for s in (
2505 "2007_10_09"
2506 , "2007-10-09T23:45:59"
2507 , "2007/10/09 23.45.59"
2508 , "23:45:59"
2509 ):
2510 print(s.ljust(30)+ " --> " + str(numsplit(s)))
2511
2512 print "It is now:", t
2513