1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Support for dates."""
24
25
26
27
28
29
30 from gettext import gettext as _
31
32
33
34
35
36
37 import logging
38 log = logging.getLogger(".Date")
39
40
41
42
43
44
45
46
47
48
49
50
51
52 from gen.lib.calendar import (gregorian_sdn, julian_sdn, hebrew_sdn,
53 french_sdn, persian_sdn, islamic_sdn,
54 gregorian_ymd, julian_ymd, hebrew_ymd,
55 french_ymd, persian_ymd, islamic_ymd)
56 import Config
57
58
59
60
61
62
64 """Error used to report Date errors."""
66 Exception.__init__(self)
67 self.value = value
68
71
72
73
74
75
76
78 """
79 The core date handling class for GRAMPs.
80
81 Supports partial dates, compound dates and alternate calendars.
82 """
83 MOD_NONE = 0
84 MOD_BEFORE = 1
85 MOD_AFTER = 2
86 MOD_ABOUT = 3
87 MOD_RANGE = 4
88 MOD_SPAN = 5
89 MOD_TEXTONLY = 6
90
91 QUAL_NONE = 0
92 QUAL_ESTIMATED = 1
93 QUAL_CALCULATED = 2
94
95 CAL_GREGORIAN = 0
96 CAL_JULIAN = 1
97 CAL_HEBREW = 2
98 CAL_FRENCH = 3
99 CAL_PERSIAN = 4
100 CAL_ISLAMIC = 5
101
102 EMPTY = (0, 0, 0, False)
103
104 _POS_DAY = 0
105 _POS_MON = 1
106 _POS_YR = 2
107 _POS_SL = 3
108 _POS_RDAY = 4
109 _POS_RMON = 5
110 _POS_RYR = 6
111 _POS_RSL = 7
112
113 _calendar_convert = [
114 gregorian_sdn,
115 julian_sdn,
116 hebrew_sdn,
117 french_sdn,
118 persian_sdn,
119 islamic_sdn,
120 ]
121
122 _calendar_change = [
123 gregorian_ymd,
124 julian_ymd,
125 hebrew_ymd,
126 french_ymd,
127 persian_ymd,
128 islamic_ymd,
129 ]
130
131 calendar_names = ["Gregorian",
132 "Julian",
133 "Hebrew",
134 "French Republican",
135 "Persian",
136 "Islamic"]
137
138
139 ui_calendar_names = [_("Gregorian"),
140 _("Julian"),
141 _("Hebrew"),
142 _("French Republican"),
143 _("Persian"),
144 _("Islamic")]
145
147 """
148 Create a new Date instance.
149 """
150 calendar = kwargs.get("calendar", None)
151 modifier = kwargs.get("modifier", None)
152 quality = kwargs.get("quality", None)
153
154 if len(source) == 0:
155 source = None
156 elif len(source) == 1:
157 if type(source[0]) == int:
158 source = (source[0], 0, 0)
159 else:
160 source = source[0]
161 elif len(source) == 2:
162 source = (source[0], source[1], 0)
163 elif len(source) == 3:
164 pass
165 else:
166 raise AttributeError, "invalid args to Date: %s" % source
167
168 if type(source) == tuple:
169 if calendar == None:
170 self.calendar = Date.CAL_GREGORIAN
171 else:
172 self.calendar = self.lookup_calendar(calendar)
173 if modifier == None:
174 self.modifier = Date.MOD_NONE
175 else:
176 self.modifier = self.lookup_modifier(modifier)
177 if quality == None:
178 self.quality = Date.QUAL_NONE
179 else:
180 self.quality = self.lookup_quality(quality)
181 self.dateval = Date.EMPTY
182 self.text = u""
183 self.sortval = 0
184 self.set_yr_mon_day(*source)
185 elif type(source) == str:
186 if (calendar != None or
187 modifier != None or
188 quality != None):
189 raise AttributeError("can't set calendar, modifier, or "
190 "quality with string date")
191 import DateHandler
192 source = DateHandler.parser.parse(source)
193 self.calendar = source.calendar
194 self.modifier = source.modifier
195 self.quality = source.quality
196 self.dateval = source.dateval
197 self.text = source.text
198 self.sortval = source.sortval
199 elif source:
200 self.calendar = source.calendar
201 self.modifier = source.modifier
202 self.quality = source.quality
203 self.dateval = source.dateval
204 self.text = source.text
205 self.sortval = source.sortval
206 else:
207 self.calendar = Date.CAL_GREGORIAN
208 self.modifier = Date.MOD_NONE
209 self.quality = Date.QUAL_NONE
210 self.dateval = Date.EMPTY
211 self.text = u""
212 self.sortval = 0
213
215 """
216 Convert to a series of tuples for data storage.
217 """
218 if no_text_date:
219 text = u''
220 else:
221 text = self.text
222
223 return (self.calendar, self.modifier, self.quality,
224 self.dateval, text, self.sortval)
225
227 """
228 Load from the format created by serialize.
229 """
230 (self.calendar, self.modifier, self.quality,
231 self.dateval, self.text, self.sortval) = data
232 return self
233
234 - def copy(self, source):
235 """
236 Copy all the attributes of the given Date instance to the present
237 instance, without creating a new object.
238 """
239 self.calendar = source.calendar
240 self.modifier = source.modifier
241 self.quality = source.quality
242 self.dateval = source.dateval
243 self.text = source.text
244 self.sortval = source.sortval
245
247 """
248 Compare two dates.
249
250 Comparison function. Allows the usage of equality tests.
251 This allows you do run statements like 'date1 <= date2'
252 """
253 if isinstance(other, Date):
254 return cmp(self.sortval, other.sortval)
255 else:
256 return -1
257
259 """
260 Date arithmetic: Date() + years, or Date() + (years, [months, [days]]).
261 """
262 if type(other) == int:
263 return self.copy_offset_ymd(other)
264 elif type(other) in [tuple, list]:
265 return self.copy_offset_ymd(*other)
266 else:
267 raise AttributeError, "unknown date add type: %s " % type(other)
268
270 """
271 Add a number + Date() or (years, months, days) + Date().
272 """
273 return self + other
274
276 """
277 Date arithmetic: Date() - years, Date - (y,m,d), or Date() - Date().
278 """
279 if type(other) == int:
280 return self.copy_offset_ymd(-other)
281 elif type(other) in [tuple, list]:
282 return self.copy_offset_ymd(*map(lambda x: -x, other))
283 elif type(other) == type(self):
284
285
286 d1 = map(lambda i: i or 1, self.get_ymd())
287 d2 = map(lambda i: i or 1, other.get_ymd())
288 date1 = self
289 date2 = other
290 if d1 < d2:
291 d1, d2 = d2, d1
292 date1, date2 = date2, date1
293
294 if self.calendar != other.calendar:
295 diff = date1.sortval - date2.sortval
296 return (diff/365, (diff % 365)/30, (diff % 365) % 30)
297
298 if d2[2] > d1[2]:
299
300 if d2[1] > d1[1]:
301 d1[0] -= 1
302 d1[1] += 12
303 d1[1] -= 1
304 d1[2] += 31
305
306 if d2[1] > d1[1]:
307 d1[0] -= 1
308 d1[1] += 12
309 days = d1[2] - d2[2]
310 months = d1[1] - d2[1]
311 years = d1[0] - d2[0]
312 if days > 31:
313 months += days / 31
314 days = days % 31
315 if months > 12:
316 years += months / 12
317 months = months % 12
318
319
320 eDate = date1 - (years, months, days)
321 if eDate < date2:
322 diff = 0
323 while eDate < date2 and diff < 60:
324 diff += 1
325 eDate = eDate + (0, 0, diff)
326 if diff == 60:
327 return None
328 return (years, months, days - diff)
329 elif eDate > date2:
330 diff = 0
331 while eDate > date2 and diff > -60:
332 diff -= 1
333 eDate = eDate - (0, 0, abs(diff))
334 if diff == -60:
335 return None
336 return (years, months, days + diff)
337 else:
338 return (years, months, days)
339 else:
340 raise AttributeError, "unknown date sub type: %s " % type(other)
341
342
343
344
345
347 return self.sortval < other.sortval
348
350 return self.sortval > other.sortval
351
353 """
354 Return 1 if the given Date instance is the same as the present
355 instance IN ALL REGARDS.
356
357 Needed, because the __cmp__ only looks at the sorting value, and
358 ignores the modifiers/comments.
359 """
360 if self.modifier == other.modifier \
361 and self.modifier == Date.MOD_TEXTONLY:
362 value = self.text == other.text
363 else:
364 value = (self.calendar == other.calendar and
365 self.modifier == other.modifier and
366 self.quality == other.quality and
367 self.dateval == other.dateval)
368 return value
369
371 """
372 Return the minimal start_date, and a maximal stop_date corresponding
373 to this date, given in Gregorian calendar.
374
375 Useful in doing range overlap comparisons between different dates.
376
377 Note that we stay in (YR,MON,DAY)
378 """
379
380 def yr_mon_day(dateval):
381 """
382 Local function to swap order for easy comparisons, and correct
383 year of slash date.
384
385 Slash date is given as year1/year2, where year1 is Julian year,
386 and year2=year1+1 the Gregorian year.
387
388 Slash date is already taken care of.
389 """
390 return (dateval[Date._POS_YR], dateval[Date._POS_MON],
391 dateval[Date._POS_DAY])
392 def date_offset(dateval, offset):
393 """
394 Local function to do date arithmetic: add the offset, return
395 (year,month,day) in the Gregorian calendar.
396 """
397 new_date = Date()
398 new_date.set_yr_mon_day(dateval[0], dateval[1], dateval[2])
399 return new_date.offset(offset)
400
401 datecopy = Date(self)
402
403 datecopy.convert_calendar(Date.CAL_GREGORIAN)
404 start = yr_mon_day(datecopy.get_start_date())
405 stop = yr_mon_day(datecopy.get_stop_date())
406
407 if stop == (0, 0, 0):
408 stop = start
409
410 stopmax = list(stop)
411 if stopmax[0] == 0:
412 stopmax[0] = start[Date._POS_YR]
413 if stopmax[1] == 0:
414 stopmax[1] = 12
415 if stopmax[2] == 0:
416 stopmax[2] = 31
417 startmin = list(start)
418 if startmin[1] == 0:
419 startmin[1] = 1
420 if startmin[2] == 0:
421 startmin[2] = 1
422
423 if self.modifier == Date.MOD_BEFORE:
424 stopmax = date_offset(startmin, -1)
425 fdiff = Config.get(Config.DATE_BEFORE_RANGE)
426 startmin = (stopmax[0] - fdiff, stopmax[1], stopmax[2])
427 elif self.modifier == Date.MOD_AFTER:
428 startmin = date_offset(stopmax, 1)
429 fdiff = Config.get(Config.DATE_AFTER_RANGE)
430 stopmax = (startmin[0] + fdiff, startmin[1], startmin[2])
431 elif (self.modifier == Date.MOD_ABOUT or
432 self.quality == Date.QUAL_ESTIMATED):
433 fdiff = Config.get(Config.DATE_ABOUT_RANGE)
434 startmin = (startmin[0] - fdiff, startmin[1], startmin[2])
435 stopmax = (stopmax[0] + fdiff, stopmax[1], stopmax[2])
436
437 return (tuple(startmin), tuple(stopmax))
438
439 - def match(self, other_date, comparison="="):
440 """
441 Compare two dates using sophisticated techniques looking for any match
442 between two possible dates, date spans and qualities.
443
444 The other comparisons for Date (is_equal() and __cmp() don't actually
445 look for anything other than a straight match, or a simple comparison
446 of the sortval.
447
448 comparison =,== :
449 Returns True if any part of other_date matches any part of self
450 comparison < :
451 Returns True if any part of other_date < any part of self
452 comparison << :
453 Returns True if all parts of other_date < all parts of self
454 comparison > :
455 Returns True if any part of other_date > any part of self
456 comparison >> :
457 Returns True if all parts of other_date > all parts of self
458 """
459 if (other_date.modifier == Date.MOD_TEXTONLY or
460 self.modifier == Date.MOD_TEXTONLY):
461 if comparison in ["=", "=="]:
462 return (self.text.upper().find(other_date.text.upper()) != -1)
463 else:
464 return False
465
466
467 other_start, other_stop = other_date.get_start_stop_range()
468 self_start, self_stop = self.get_start_stop_range()
469
470 if comparison in ["=", "=="]:
471
472 return ((self_start <= other_start <= self_stop) or
473 (self_start <= other_stop <= self_stop) or
474 (other_start <= self_start <= other_stop) or
475 (other_start <= self_stop <= other_stop))
476 elif comparison == "<":
477
478 return self_start < other_stop
479 elif comparison == "<<":
480
481 return self_stop < other_start
482 elif comparison == ">":
483
484 return self_stop > other_start
485 elif comparison == ">>":
486
487 return self_start > other_stop
488 else:
489 raise AttributeError, ("invalid match comparison operator: '%s'" %
490 comparison)
491
493 """
494 Produce a string representation of the Date object.
495
496 If the date is not valid, the text representation is displayed. If
497 the date is a range or a span, a string in the form of
498 'YYYY-MM-DD - YYYY-MM-DD' is returned. Otherwise, a string in
499 the form of 'YYYY-MM-DD' is returned.
500 """
501 if self.quality == Date.QUAL_ESTIMATED:
502 qual = "est "
503 elif self.quality == Date.QUAL_CALCULATED:
504 qual = "calc "
505 else:
506 qual = ""
507
508 if self.modifier == Date.MOD_BEFORE:
509 pref = "bef "
510 elif self.modifier == Date.MOD_AFTER:
511 pref = "aft "
512 elif self.modifier == Date.MOD_ABOUT:
513 pref = "abt "
514 else:
515 pref = ""
516
517 if self.calendar != Date.CAL_GREGORIAN:
518 cal = " (%s)" % Date.calendar_names[self.calendar]
519 else:
520 cal = ""
521
522 if self.modifier == Date.MOD_TEXTONLY:
523 val = self.text
524 elif self.modifier == Date.MOD_RANGE or self.modifier == Date.MOD_SPAN:
525 val = "%04d-%02d-%02d - %04d-%02d-%02d" % (
526 self.dateval[Date._POS_YR], self.dateval[Date._POS_MON],
527 self.dateval[Date._POS_DAY], self.dateval[Date._POS_RYR],
528 self.dateval[Date._POS_RMON], self.dateval[Date._POS_RDAY])
529 else:
530 val = "%04d-%02d-%02d" % (
531 self.dateval[Date._POS_YR], self.dateval[Date._POS_MON],
532 self.dateval[Date._POS_DAY])
533 return "%s%s%s%s" % (qual, pref, val, cal)
534
536 """
537 Return the sort value of Date object.
538
539 If the value is a text string, 0 is returned. Otherwise, the
540 calculated sort date is returned. The sort date is rebuilt on every
541 assignment.
542
543 The sort value is an integer representing the value. The sortval is
544 the integer number of days that have elapsed since Monday, January 1,
545 4713 BC in the proleptic Julian calendar.
546 See http://en.wikipedia.org/wiki/Julian_day
547 """
548 return self.sortval
549
551 """
552 Return an integer indicating the calendar selected.
553
554 The valid values are::
555
556 MOD_NONE = no modifier (default)
557 MOD_BEFORE = before
558 MOD_AFTER = after
559 MOD_ABOUT = about
560 MOD_RANGE = date range
561 MOD_SPAN = date span
562 MOD_TEXTONLY = text only
563 """
564 return self.modifier
565
575
577 """
578 Return an integer indicating the calendar selected.
579
580 The valid values are::
581
582 QUAL_NONE = normal (default)
583 QUAL_ESTIMATED = estimated
584 QUAL_CALCULATED = calculated
585 """
586 return self.quality
587
596
598 """
599 Return an integer indicating the calendar selected.
600
601 The valid values are::
602
603 CAL_GREGORIAN - Gregorian calendar
604 CAL_JULIAN - Julian calendar
605 CAL_HEBREW - Hebrew (Jewish) calendar
606 CAL_FRENCH - French Republican calendar
607 CAL_PERSIAN - Persian calendar
608 CAL_ISLAMIC - Islamic calendar
609 """
610 return self.calendar
611
620
622 """
623 Return a tuple representing the start date.
624
625 If the date is a compound date (range or a span), it is the first part
626 of the compound date. If the date is a text string, a tuple of
627 (0, 0, 0, False) is returned. Otherwise, a date of (DD, MM, YY, slash)
628 is returned. If slash is True, then the date is in the form of 1530/1.
629 """
630 if self.modifier == Date.MOD_TEXTONLY:
631 val = Date.EMPTY
632 else:
633 val = self.dateval[0:4]
634 return val
635
637 """
638 Return a tuple representing the second half of a compound date.
639
640 If the date is not a compound date, (including text strings) a tuple
641 of (0, 0, 0, False) is returned. Otherwise, a date of (DD, MM, YY, slash)
642 is returned. If slash is True, then the date is in the form of 1530/1.
643 """
644 if self.modifier == Date.MOD_RANGE or self.modifier == Date.MOD_SPAN:
645 val = self.dateval[4:8]
646 else:
647 val = Date.EMPTY
648 return val
649
651 """
652 Return the item specified.
653 """
654 if self.modifier == Date.MOD_TEXTONLY:
655 val = 0
656 else:
657 val = self.dateval[index]
658 return val
659
661 """
662 Determine if the item specified is valid.
663 """
664 if self.modifier == Date.MOD_TEXTONLY:
665 val = False
666 else:
667 val = self.dateval[index] != 0
668 return val
669
671 """
672 Return the item specified.
673 """
674 if self.modifier == Date.MOD_SPAN or self.modifier == Date.MOD_RANGE:
675 val = self.dateval[index]
676 else:
677 val = 0
678 return val
679
681 """
682 Return the year associated with the date.
683
684 If the year is not defined, a zero is returned. If the date is a
685 compound date, the lower date year is returned.
686 """
687 return self._get_low_item(Date._POS_YR)
688
690 """
691 Set the year, month, and day values.
692 """
693 dv = list(self.dateval)
694 dv[Date._POS_YR] = year
695 dv[Date._POS_MON] = month
696 dv[Date._POS_DAY] = day
697 self.dateval = tuple(dv)
698 self._calc_sort_value()
699
731
733 """
734 Return a Date copy based on year, month, and day offset.
735 """
736 orig_cal = self.calendar
737 if self.calendar != 0:
738 new_date = self.to_calendar("gregorian")
739 else:
740 new_date = self
741 retval = Date(new_date)
742 retval.set_yr_mon_day_offset(year, month, day)
743 if orig_cal == 0:
744 return retval
745 else:
746 retval.convert_calendar(orig_cal)
747 return retval
748
749 - def copy_ymd(self, year=0, month=0, day=0):
750 """
751 Return a Date copy with year, month, and day set.
752 """
753 retval = Date(self)
754 retval.set_yr_mon_day(year, month, day)
755 return retval
756
758 """
759 Set the year value.
760 """
761 self.dateval = self.dateval[0:2] + (year, ) + self.dateval[3:]
762 self._calc_sort_value()
763
765 """
766 Return true if the year is valid.
767 """
768 return self._get_low_item_valid(Date._POS_YR)
769
771 """
772 Return the month associated with the date.
773
774 If the month is not defined, a zero is returned. If the date is a
775 compound date, the lower date month is returned.
776 """
777 return self._get_low_item(Date._POS_MON)
778
780 """
781 Return true if the month is valid
782 """
783 return self._get_low_item_valid(Date._POS_MON)
784
786 """
787 Return the day of the month associated with the date.
788
789 If the day is not defined, a zero is returned. If the date is a
790 compound date, the lower date day is returned.
791 """
792 return self._get_low_item(Date._POS_DAY)
793
795 """
796 Return true if the day is valid.
797 """
798 return self._get_low_item_valid(Date._POS_DAY)
799
801 """
802 Return true if any part of the date is valid.
803 """
804 return self.modifier != Date.MOD_TEXTONLY
805
807 """
808 Return the day of the year associated with the second part of a
809 compound date.
810
811 If the year is not defined, a zero is returned.
812 """
813 return self._get_high_item(Date._POS_RYR)
814
816 """
817 Return the month of the month associated with the second part of a
818 compound date.
819
820 If the month is not defined, a zero is returned.
821 """
822 return self._get_high_item(Date._POS_RMON)
823
825 """
826 Return the day of the month associated with the second part of a
827 compound date.
828
829 If the day is not defined, a zero is returned.
830 """
831 return self._get_high_item(Date._POS_RDAY)
832
834 """
835 Return the high year estimate.
836
837 For compound dates with non-zero stop year, the stop year is returned.
838 Otherwise, the start year is returned.
839 """
840 if self.is_compound():
841 ret = self.get_stop_year()
842 if ret:
843 return ret
844 else:
845 return self.get_year()
846
847 - def get_text(self):
848 """
849 Return the text value associated with an invalid date.
850 """
851 return self.text
852
853 - def set(self, quality, modifier, calendar, value, text=None):
854 """
855 Set the date to the specified value.
856
857 Parameters are::
858
859 quality - The date quality for the date (see get_quality
860 for more information)
861 modified - The date modifier for the date (see get_modifier
862 for more information)
863 calendar - The calendar associated with the date (see
864 get_calendar for more information).
865 value - A tuple representing the date information. For a
866 non-compound date, the format is (DD, MM, YY, slash)
867 and for a compound date the tuple stores data as
868 (DD, MM, YY, slash1, DD, MM, YY, slash2)
869 text - A text string holding either the verbatim user input
870 or a comment relating to the date.
871
872 The sort value is recalculated.
873 """
874
875 if modifier in (Date.MOD_NONE, Date.MOD_BEFORE,
876 Date.MOD_AFTER, Date.MOD_ABOUT) and len(value) < 4:
877 raise DateError("Invalid value. Should be: (DD, MM, YY, slash)")
878 if modifier in (Date.MOD_RANGE, Date.MOD_SPAN) and len(value) < 8:
879 raise DateError("Invalid value. Should be: (DD, MM, "
880 "YY, slash1, DD, MM, YY, slash2)")
881 if modifier not in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER,
882 Date.MOD_ABOUT, Date.MOD_RANGE, Date.MOD_SPAN,
883 Date.MOD_TEXTONLY):
884 raise DateError("Invalid modifier")
885 if quality not in (Date.QUAL_NONE, Date.QUAL_ESTIMATED,
886 Date.QUAL_CALCULATED):
887 raise DateError("Invalid quality")
888 if calendar not in (Date.CAL_GREGORIAN, Date.CAL_JULIAN,
889 Date.CAL_HEBREW, Date.CAL_FRENCH,
890 Date.CAL_PERSIAN, Date.CAL_ISLAMIC):
891 raise DateError("Invalid calendar")
892
893 self.quality = quality
894 self.modifier = modifier
895 self.calendar = calendar
896 self.dateval = value
897 year = max(value[Date._POS_YR], 1)
898 month = max(value[Date._POS_MON], 1)
899 day = max(value[Date._POS_DAY], 1)
900 if year == 0 and month == 0 and day == 0:
901 self.sortval = 0
902 else:
903 func = Date._calendar_convert[calendar]
904 self.sortval = func(year, month, day)
905 if text:
906 self.text = text
907
909 """
910 Calculate the numerical sort value associated with the date.
911 """
912 year = max(self.dateval[Date._POS_YR], 1)
913 month = max(self.dateval[Date._POS_MON], 1)
914 day = max(self.dateval[Date._POS_DAY], 1)
915 if year == 0 and month == 0 and day == 0:
916 self.sortval = 0
917 else:
918 func = Date._calendar_convert[self.calendar]
919 self.sortval = func(year, month, day)
920
922 """
923 Convert the date from the current calendar to the specified calendar.
924 """
925 if calendar == self.calendar:
926 return
927 (year, month, day) = Date._calendar_change[calendar](self.sortval)
928 if self.is_compound():
929 ryear = max(self.dateval[Date._POS_RYR], 1)
930 rmonth = max(self.dateval[Date._POS_RMON], 1)
931 rday = max(self.dateval[Date._POS_RDAY], 1)
932 sdn = Date._calendar_convert[self.calendar](ryear, rmonth, rday)
933 (nyear, nmonth, nday) = Date._calendar_change[calendar](sdn)
934 self.dateval = (day, month, year, self.dateval[Date._POS_SL],
935 nday, nmonth, nyear, self.dateval[Date._POS_RSL])
936 else:
937 self.dateval = (day, month, year, self.dateval[Date._POS_SL])
938 self.calendar = calendar
939
940 - def set_as_text(self, text):
941 """
942 Set the day to a text string, and assign the sort value to zero.
943 """
944 self.modifier = Date.MOD_TEXTONLY
945 self.text = text
946 self.sortval = 0
947
948 - def set_text_value(self, text):
949 """
950 Set the text string to a given text.
951 """
952 self.text = text
953
961
963 """
964 Return True if the date is a date range or a date span.
965 """
966 return self.modifier == Date.MOD_RANGE \
967 or self.modifier == Date.MOD_SPAN
968
970 """
971 Return True if the date is a regular date.
972
973 The regular date is a single exact date, i.e. not text-only, not
974 a range or a span, not estimated/calculated, not about/before/after
975 date, and having year, month, and day all non-zero.
976 """
977 return self.modifier == Date.MOD_NONE \
978 and self.quality == Date.QUAL_NONE \
979 and self.get_year_valid() and self.get_month_valid() \
980 and self.get_day_valid()
981
987
993
999
1001 """
1002 Lookup calendar name in the list of known calendars, even if translated.
1003 """
1004 calendar_lower = [n.lower() for n in Date.calendar_names]
1005 ui_lower = [n.lower() for n in Date.ui_calendar_names]
1006 if calendar.lower() in calendar_lower:
1007 return calendar_lower.index(calendar.lower())
1008 elif calendar.lower() in ui_lower:
1009 return ui_lower.index(calendar.lower())
1010 else:
1011 raise AttributeError("invalid calendar: '%s'" % calendar)
1012
1014 """
1015 Lookup date quality keyword, even if translated.
1016 """
1017 qualities = ["none", "estimated", "calculated"]
1018 ui_qualities = [_("none"), _("estimated"), _("calculated")]
1019 if quality.lower() in qualities:
1020 return qualities.index(quality.lower())
1021 elif quality.lower() in ui_qualities:
1022 return ui_qualities.index(quality.lower())
1023 else:
1024 raise AttributeError("invalid quality: '%s'" % quality)
1025
1027 """
1028 Lookup date modifier keyword, even if translated.
1029 """
1030 mods = ["none", "before", "after", "about",
1031 "range", "span", "textonly"]
1032 ui_mods = [_("none"), _("before"), _("after"), _("about"),
1033 _("range"), _("span"), _("textonly")]
1034 if modifier.lower() in mods:
1035 return mods.index(modifier.lower())
1036 elif modifier.lower() in ui_mods:
1037 return ui_mods.index(modifier.lower())
1038 else:
1039 raise AttributeError("invalid modifier: '%s'" % modifier)
1040
1042 """
1043 Return a new Date object in the calendar calendar_name.
1044
1045 >>> Date("Jan 1 1591").to_calendar("julian")
1046 1590-12-22 (Julian)
1047 """
1048 cal = self.lookup_calendar(calendar_name)
1049 retval = Date(self)
1050 retval.convert_calendar(cal)
1051 return retval
1052
1054 """
1055 Return true if the date is a slash-date.
1056 """
1057 return self._get_low_item_valid(Date._POS_SL)
1058