]> git.proxmox.com Git - mirror_edk2.git/blob - AppPkg/Applications/Python/Python-2.7.10/Lib/calendar.py
AppPkg/Applications/Python/Python-2.7.10: Initial Checkin part 4/5.
[mirror_edk2.git] / AppPkg / Applications / Python / Python-2.7.10 / Lib / calendar.py
1 """Calendar printing functions
2
3 Note when comparing these calendars to the ones printed by cal(1): By
4 default, these calendars have Monday as the first day of the week, and
5 Sunday as the last (the European convention). Use setfirstweekday() to
6 set the first day of the week (0=Monday, 6=Sunday)."""
7
8 import sys
9 import datetime
10 import locale as _locale
11
12 __all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday",
13 "firstweekday", "isleap", "leapdays", "weekday", "monthrange",
14 "monthcalendar", "prmonth", "month", "prcal", "calendar",
15 "timegm", "month_name", "month_abbr", "day_name", "day_abbr"]
16
17 # Exception raised for bad input (with string parameter for details)
18 error = ValueError
19
20 # Exceptions raised for bad input
21 class IllegalMonthError(ValueError):
22 def __init__(self, month):
23 self.month = month
24 def __str__(self):
25 return "bad month number %r; must be 1-12" % self.month
26
27
28 class IllegalWeekdayError(ValueError):
29 def __init__(self, weekday):
30 self.weekday = weekday
31 def __str__(self):
32 return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday
33
34
35 # Constants for months referenced later
36 January = 1
37 February = 2
38
39 # Number of days per month (except for February in leap years)
40 mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
41
42 # This module used to have hard-coded lists of day and month names, as
43 # English strings. The classes following emulate a read-only version of
44 # that, but supply localized names. Note that the values are computed
45 # fresh on each call, in case the user changes locale between calls.
46
47 class _localized_month:
48
49 _months = [datetime.date(2001, i+1, 1).strftime for i in range(12)]
50 _months.insert(0, lambda x: "")
51
52 def __init__(self, format):
53 self.format = format
54
55 def __getitem__(self, i):
56 funcs = self._months[i]
57 if isinstance(i, slice):
58 return [f(self.format) for f in funcs]
59 else:
60 return funcs(self.format)
61
62 def __len__(self):
63 return 13
64
65
66 class _localized_day:
67
68 # January 1, 2001, was a Monday.
69 _days = [datetime.date(2001, 1, i+1).strftime for i in range(7)]
70
71 def __init__(self, format):
72 self.format = format
73
74 def __getitem__(self, i):
75 funcs = self._days[i]
76 if isinstance(i, slice):
77 return [f(self.format) for f in funcs]
78 else:
79 return funcs(self.format)
80
81 def __len__(self):
82 return 7
83
84
85 # Full and abbreviated names of weekdays
86 day_name = _localized_day('%A')
87 day_abbr = _localized_day('%a')
88
89 # Full and abbreviated names of months (1-based arrays!!!)
90 month_name = _localized_month('%B')
91 month_abbr = _localized_month('%b')
92
93 # Constants for weekdays
94 (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
95
96
97 def isleap(year):
98 """Return True for leap years, False for non-leap years."""
99 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
100
101
102 def leapdays(y1, y2):
103 """Return number of leap years in range [y1, y2).
104 Assume y1 <= y2."""
105 y1 -= 1
106 y2 -= 1
107 return (y2//4 - y1//4) - (y2//100 - y1//100) + (y2//400 - y1//400)
108
109
110 def weekday(year, month, day):
111 """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12),
112 day (1-31)."""
113 return datetime.date(year, month, day).weekday()
114
115
116 def monthrange(year, month):
117 """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for
118 year, month."""
119 if not 1 <= month <= 12:
120 raise IllegalMonthError(month)
121 day1 = weekday(year, month, 1)
122 ndays = mdays[month] + (month == February and isleap(year))
123 return day1, ndays
124
125
126 class Calendar(object):
127 """
128 Base calendar class. This class doesn't do any formatting. It simply
129 provides data to subclasses.
130 """
131
132 def __init__(self, firstweekday=0):
133 self.firstweekday = firstweekday # 0 = Monday, 6 = Sunday
134
135 def getfirstweekday(self):
136 return self._firstweekday % 7
137
138 def setfirstweekday(self, firstweekday):
139 self._firstweekday = firstweekday
140
141 firstweekday = property(getfirstweekday, setfirstweekday)
142
143 def iterweekdays(self):
144 """
145 Return a iterator for one week of weekday numbers starting with the
146 configured first one.
147 """
148 for i in range(self.firstweekday, self.firstweekday + 7):
149 yield i%7
150
151 def itermonthdates(self, year, month):
152 """
153 Return an iterator for one month. The iterator will yield datetime.date
154 values and will always iterate through complete weeks, so it will yield
155 dates outside the specified month.
156 """
157 date = datetime.date(year, month, 1)
158 # Go back to the beginning of the week
159 days = (date.weekday() - self.firstweekday) % 7
160 date -= datetime.timedelta(days=days)
161 oneday = datetime.timedelta(days=1)
162 while True:
163 yield date
164 try:
165 date += oneday
166 except OverflowError:
167 # Adding one day could fail after datetime.MAXYEAR
168 break
169 if date.month != month and date.weekday() == self.firstweekday:
170 break
171
172 def itermonthdays2(self, year, month):
173 """
174 Like itermonthdates(), but will yield (day number, weekday number)
175 tuples. For days outside the specified month the day number is 0.
176 """
177 for date in self.itermonthdates(year, month):
178 if date.month != month:
179 yield (0, date.weekday())
180 else:
181 yield (date.day, date.weekday())
182
183 def itermonthdays(self, year, month):
184 """
185 Like itermonthdates(), but will yield day numbers. For days outside
186 the specified month the day number is 0.
187 """
188 for date in self.itermonthdates(year, month):
189 if date.month != month:
190 yield 0
191 else:
192 yield date.day
193
194 def monthdatescalendar(self, year, month):
195 """
196 Return a matrix (list of lists) representing a month's calendar.
197 Each row represents a week; week entries are datetime.date values.
198 """
199 dates = list(self.itermonthdates(year, month))
200 return [ dates[i:i+7] for i in range(0, len(dates), 7) ]
201
202 def monthdays2calendar(self, year, month):
203 """
204 Return a matrix representing a month's calendar.
205 Each row represents a week; week entries are
206 (day number, weekday number) tuples. Day numbers outside this month
207 are zero.
208 """
209 days = list(self.itermonthdays2(year, month))
210 return [ days[i:i+7] for i in range(0, len(days), 7) ]
211
212 def monthdayscalendar(self, year, month):
213 """
214 Return a matrix representing a month's calendar.
215 Each row represents a week; days outside this month are zero.
216 """
217 days = list(self.itermonthdays(year, month))
218 return [ days[i:i+7] for i in range(0, len(days), 7) ]
219
220 def yeardatescalendar(self, year, width=3):
221 """
222 Return the data for the specified year ready for formatting. The return
223 value is a list of month rows. Each month row contains up to width months.
224 Each month contains between 4 and 6 weeks and each week contains 1-7
225 days. Days are datetime.date objects.
226 """
227 months = [
228 self.monthdatescalendar(year, i)
229 for i in range(January, January+12)
230 ]
231 return [months[i:i+width] for i in range(0, len(months), width) ]
232
233 def yeardays2calendar(self, year, width=3):
234 """
235 Return the data for the specified year ready for formatting (similar to
236 yeardatescalendar()). Entries in the week lists are
237 (day number, weekday number) tuples. Day numbers outside this month are
238 zero.
239 """
240 months = [
241 self.monthdays2calendar(year, i)
242 for i in range(January, January+12)
243 ]
244 return [months[i:i+width] for i in range(0, len(months), width) ]
245
246 def yeardayscalendar(self, year, width=3):
247 """
248 Return the data for the specified year ready for formatting (similar to
249 yeardatescalendar()). Entries in the week lists are day numbers.
250 Day numbers outside this month are zero.
251 """
252 months = [
253 self.monthdayscalendar(year, i)
254 for i in range(January, January+12)
255 ]
256 return [months[i:i+width] for i in range(0, len(months), width) ]
257
258
259 class TextCalendar(Calendar):
260 """
261 Subclass of Calendar that outputs a calendar as a simple plain text
262 similar to the UNIX program cal.
263 """
264
265 def prweek(self, theweek, width):
266 """
267 Print a single week (no newline).
268 """
269 print self.formatweek(theweek, width),
270
271 def formatday(self, day, weekday, width):
272 """
273 Returns a formatted day.
274 """
275 if day == 0:
276 s = ''
277 else:
278 s = '%2i' % day # right-align single-digit days
279 return s.center(width)
280
281 def formatweek(self, theweek, width):
282 """
283 Returns a single week in a string (no newline).
284 """
285 return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek)
286
287 def formatweekday(self, day, width):
288 """
289 Returns a formatted week day name.
290 """
291 if width >= 9:
292 names = day_name
293 else:
294 names = day_abbr
295 return names[day][:width].center(width)
296
297 def formatweekheader(self, width):
298 """
299 Return a header for a week.
300 """
301 return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays())
302
303 def formatmonthname(self, theyear, themonth, width, withyear=True):
304 """
305 Return a formatted month name.
306 """
307 s = month_name[themonth]
308 if withyear:
309 s = "%s %r" % (s, theyear)
310 return s.center(width)
311
312 def prmonth(self, theyear, themonth, w=0, l=0):
313 """
314 Print a month's calendar.
315 """
316 print self.formatmonth(theyear, themonth, w, l),
317
318 def formatmonth(self, theyear, themonth, w=0, l=0):
319 """
320 Return a month's calendar string (multi-line).
321 """
322 w = max(2, w)
323 l = max(1, l)
324 s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
325 s = s.rstrip()
326 s += '\n' * l
327 s += self.formatweekheader(w).rstrip()
328 s += '\n' * l
329 for week in self.monthdays2calendar(theyear, themonth):
330 s += self.formatweek(week, w).rstrip()
331 s += '\n' * l
332 return s
333
334 def formatyear(self, theyear, w=2, l=1, c=6, m=3):
335 """
336 Returns a year's calendar as a multi-line string.
337 """
338 w = max(2, w)
339 l = max(1, l)
340 c = max(2, c)
341 colwidth = (w + 1) * 7 - 1
342 v = []
343 a = v.append
344 a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
345 a('\n'*l)
346 header = self.formatweekheader(w)
347 for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
348 # months in this row
349 months = range(m*i+1, min(m*(i+1)+1, 13))
350 a('\n'*l)
351 names = (self.formatmonthname(theyear, k, colwidth, False)
352 for k in months)
353 a(formatstring(names, colwidth, c).rstrip())
354 a('\n'*l)
355 headers = (header for k in months)
356 a(formatstring(headers, colwidth, c).rstrip())
357 a('\n'*l)
358 # max number of weeks for this row
359 height = max(len(cal) for cal in row)
360 for j in range(height):
361 weeks = []
362 for cal in row:
363 if j >= len(cal):
364 weeks.append('')
365 else:
366 weeks.append(self.formatweek(cal[j], w))
367 a(formatstring(weeks, colwidth, c).rstrip())
368 a('\n' * l)
369 return ''.join(v)
370
371 def pryear(self, theyear, w=0, l=0, c=6, m=3):
372 """Print a year's calendar."""
373 print self.formatyear(theyear, w, l, c, m)
374
375
376 class HTMLCalendar(Calendar):
377 """
378 This calendar returns complete HTML pages.
379 """
380
381 # CSS classes for the day <td>s
382 cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
383
384 def formatday(self, day, weekday):
385 """
386 Return a day as a table cell.
387 """
388 if day == 0:
389 return '<td class="noday">&nbsp;</td>' # day outside month
390 else:
391 return '<td class="%s">%d</td>' % (self.cssclasses[weekday], day)
392
393 def formatweek(self, theweek):
394 """
395 Return a complete week as a table row.
396 """
397 s = ''.join(self.formatday(d, wd) for (d, wd) in theweek)
398 return '<tr>%s</tr>' % s
399
400 def formatweekday(self, day):
401 """
402 Return a weekday name as a table header.
403 """
404 return '<th class="%s">%s</th>' % (self.cssclasses[day], day_abbr[day])
405
406 def formatweekheader(self):
407 """
408 Return a header for a week as a table row.
409 """
410 s = ''.join(self.formatweekday(i) for i in self.iterweekdays())
411 return '<tr>%s</tr>' % s
412
413 def formatmonthname(self, theyear, themonth, withyear=True):
414 """
415 Return a month name as a table row.
416 """
417 if withyear:
418 s = '%s %s' % (month_name[themonth], theyear)
419 else:
420 s = '%s' % month_name[themonth]
421 return '<tr><th colspan="7" class="month">%s</th></tr>' % s
422
423 def formatmonth(self, theyear, themonth, withyear=True):
424 """
425 Return a formatted month as a table.
426 """
427 v = []
428 a = v.append
429 a('<table border="0" cellpadding="0" cellspacing="0" class="month">')
430 a('\n')
431 a(self.formatmonthname(theyear, themonth, withyear=withyear))
432 a('\n')
433 a(self.formatweekheader())
434 a('\n')
435 for week in self.monthdays2calendar(theyear, themonth):
436 a(self.formatweek(week))
437 a('\n')
438 a('</table>')
439 a('\n')
440 return ''.join(v)
441
442 def formatyear(self, theyear, width=3):
443 """
444 Return a formatted year as a table of tables.
445 """
446 v = []
447 a = v.append
448 width = max(width, 1)
449 a('<table border="0" cellpadding="0" cellspacing="0" class="year">')
450 a('\n')
451 a('<tr><th colspan="%d" class="year">%s</th></tr>' % (width, theyear))
452 for i in range(January, January+12, width):
453 # months in this row
454 months = range(i, min(i+width, 13))
455 a('<tr>')
456 for m in months:
457 a('<td>')
458 a(self.formatmonth(theyear, m, withyear=False))
459 a('</td>')
460 a('</tr>')
461 a('</table>')
462 return ''.join(v)
463
464 def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None):
465 """
466 Return a formatted year as a complete HTML page.
467 """
468 if encoding is None:
469 encoding = sys.getdefaultencoding()
470 v = []
471 a = v.append
472 a('<?xml version="1.0" encoding="%s"?>\n' % encoding)
473 a('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n')
474 a('<html>\n')
475 a('<head>\n')
476 a('<meta http-equiv="Content-Type" content="text/html; charset=%s" />\n' % encoding)
477 if css is not None:
478 a('<link rel="stylesheet" type="text/css" href="%s" />\n' % css)
479 a('<title>Calendar for %d</title>\n' % theyear)
480 a('</head>\n')
481 a('<body>\n')
482 a(self.formatyear(theyear, width))
483 a('</body>\n')
484 a('</html>\n')
485 return ''.join(v).encode(encoding, "xmlcharrefreplace")
486
487
488 class TimeEncoding:
489 def __init__(self, locale):
490 self.locale = locale
491
492 def __enter__(self):
493 self.oldlocale = _locale.getlocale(_locale.LC_TIME)
494 _locale.setlocale(_locale.LC_TIME, self.locale)
495 return _locale.getlocale(_locale.LC_TIME)[1]
496
497 def __exit__(self, *args):
498 _locale.setlocale(_locale.LC_TIME, self.oldlocale)
499
500
501 class LocaleTextCalendar(TextCalendar):
502 """
503 This class can be passed a locale name in the constructor and will return
504 month and weekday names in the specified locale. If this locale includes
505 an encoding all strings containing month and weekday names will be returned
506 as unicode.
507 """
508
509 def __init__(self, firstweekday=0, locale=None):
510 TextCalendar.__init__(self, firstweekday)
511 if locale is None:
512 locale = _locale.getdefaultlocale()
513 self.locale = locale
514
515 def formatweekday(self, day, width):
516 with TimeEncoding(self.locale) as encoding:
517 if width >= 9:
518 names = day_name
519 else:
520 names = day_abbr
521 name = names[day]
522 if encoding is not None:
523 name = name.decode(encoding)
524 return name[:width].center(width)
525
526 def formatmonthname(self, theyear, themonth, width, withyear=True):
527 with TimeEncoding(self.locale) as encoding:
528 s = month_name[themonth]
529 if encoding is not None:
530 s = s.decode(encoding)
531 if withyear:
532 s = "%s %r" % (s, theyear)
533 return s.center(width)
534
535
536 class LocaleHTMLCalendar(HTMLCalendar):
537 """
538 This class can be passed a locale name in the constructor and will return
539 month and weekday names in the specified locale. If this locale includes
540 an encoding all strings containing month and weekday names will be returned
541 as unicode.
542 """
543 def __init__(self, firstweekday=0, locale=None):
544 HTMLCalendar.__init__(self, firstweekday)
545 if locale is None:
546 locale = _locale.getdefaultlocale()
547 self.locale = locale
548
549 def formatweekday(self, day):
550 with TimeEncoding(self.locale) as encoding:
551 s = day_abbr[day]
552 if encoding is not None:
553 s = s.decode(encoding)
554 return '<th class="%s">%s</th>' % (self.cssclasses[day], s)
555
556 def formatmonthname(self, theyear, themonth, withyear=True):
557 with TimeEncoding(self.locale) as encoding:
558 s = month_name[themonth]
559 if encoding is not None:
560 s = s.decode(encoding)
561 if withyear:
562 s = '%s %s' % (s, theyear)
563 return '<tr><th colspan="7" class="month">%s</th></tr>' % s
564
565
566 # Support for old module level interface
567 c = TextCalendar()
568
569 firstweekday = c.getfirstweekday
570
571 def setfirstweekday(firstweekday):
572 try:
573 firstweekday.__index__
574 except AttributeError:
575 raise IllegalWeekdayError(firstweekday)
576 if not MONDAY <= firstweekday <= SUNDAY:
577 raise IllegalWeekdayError(firstweekday)
578 c.firstweekday = firstweekday
579
580 monthcalendar = c.monthdayscalendar
581 prweek = c.prweek
582 week = c.formatweek
583 weekheader = c.formatweekheader
584 prmonth = c.prmonth
585 month = c.formatmonth
586 calendar = c.formatyear
587 prcal = c.pryear
588
589
590 # Spacing of month columns for multi-column year calendar
591 _colwidth = 7*3 - 1 # Amount printed by prweek()
592 _spacing = 6 # Number of spaces between columns
593
594
595 def format(cols, colwidth=_colwidth, spacing=_spacing):
596 """Prints multi-column formatting for year calendars"""
597 print formatstring(cols, colwidth, spacing)
598
599
600 def formatstring(cols, colwidth=_colwidth, spacing=_spacing):
601 """Returns a string formatted from n strings, centered within n columns."""
602 spacing *= ' '
603 return spacing.join(c.center(colwidth) for c in cols)
604
605
606 EPOCH = 1970
607 _EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal()
608
609
610 def timegm(tuple):
611 """Unrelated but handy function to calculate Unix timestamp from GMT."""
612 year, month, day, hour, minute, second = tuple[:6]
613 days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1
614 hours = days*24 + hour
615 minutes = hours*60 + minute
616 seconds = minutes*60 + second
617 return seconds
618
619
620 def main(args):
621 import optparse
622 parser = optparse.OptionParser(usage="usage: %prog [options] [year [month]]")
623 parser.add_option(
624 "-w", "--width",
625 dest="width", type="int", default=2,
626 help="width of date column (default 2, text only)"
627 )
628 parser.add_option(
629 "-l", "--lines",
630 dest="lines", type="int", default=1,
631 help="number of lines for each week (default 1, text only)"
632 )
633 parser.add_option(
634 "-s", "--spacing",
635 dest="spacing", type="int", default=6,
636 help="spacing between months (default 6, text only)"
637 )
638 parser.add_option(
639 "-m", "--months",
640 dest="months", type="int", default=3,
641 help="months per row (default 3, text only)"
642 )
643 parser.add_option(
644 "-c", "--css",
645 dest="css", default="calendar.css",
646 help="CSS to use for page (html only)"
647 )
648 parser.add_option(
649 "-L", "--locale",
650 dest="locale", default=None,
651 help="locale to be used from month and weekday names"
652 )
653 parser.add_option(
654 "-e", "--encoding",
655 dest="encoding", default=None,
656 help="Encoding to use for output"
657 )
658 parser.add_option(
659 "-t", "--type",
660 dest="type", default="text",
661 choices=("text", "html"),
662 help="output type (text or html)"
663 )
664
665 (options, args) = parser.parse_args(args)
666
667 if options.locale and not options.encoding:
668 parser.error("if --locale is specified --encoding is required")
669 sys.exit(1)
670
671 locale = options.locale, options.encoding
672
673 if options.type == "html":
674 if options.locale:
675 cal = LocaleHTMLCalendar(locale=locale)
676 else:
677 cal = HTMLCalendar()
678 encoding = options.encoding
679 if encoding is None:
680 encoding = sys.getdefaultencoding()
681 optdict = dict(encoding=encoding, css=options.css)
682 if len(args) == 1:
683 print cal.formatyearpage(datetime.date.today().year, **optdict)
684 elif len(args) == 2:
685 print cal.formatyearpage(int(args[1]), **optdict)
686 else:
687 parser.error("incorrect number of arguments")
688 sys.exit(1)
689 else:
690 if options.locale:
691 cal = LocaleTextCalendar(locale=locale)
692 else:
693 cal = TextCalendar()
694 optdict = dict(w=options.width, l=options.lines)
695 if len(args) != 3:
696 optdict["c"] = options.spacing
697 optdict["m"] = options.months
698 if len(args) == 1:
699 result = cal.formatyear(datetime.date.today().year, **optdict)
700 elif len(args) == 2:
701 result = cal.formatyear(int(args[1]), **optdict)
702 elif len(args) == 3:
703 result = cal.formatmonth(int(args[1]), int(args[2]), **optdict)
704 else:
705 parser.error("incorrect number of arguments")
706 sys.exit(1)
707 if options.encoding:
708 result = result.encode(options.encoding)
709 print result
710
711
712 if __name__ == "__main__":
713 main(sys.argv)