1# -*- coding: utf-8 -*- 2# 3# Gramps - a GTK+/GNOME based genealogy program 4# 5# Copyright (C) 2004-2006 Donald N. Allingham 6# Copyright (C) 2013 Vassilii Khachaturov 7# Copyright (C) 2014-2018 Paul Franklin 8# 9# This program is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation; either version 2 of the License, or 12# (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with this program; if not, write to the Free Software 21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22# 23 24""" 25U.S English date display class. Should serve as the base class for all 26localized tasks. 27""" 28 29#------------------------------------------------------------------------- 30# 31# Python modules 32# 33#------------------------------------------------------------------------- 34import datetime 35 36#------------------------------------------------------------------------- 37# 38# set up logging 39# 40#------------------------------------------------------------------------- 41import logging 42log = logging.getLogger(".DateDisplay") 43 44#------------------------------------------------------------------------- 45# 46# Gramps modules 47# 48#------------------------------------------------------------------------- 49from ..lib.date import Date 50from ..const import GRAMPS_LOCALE as glocale 51from ..utils.grampslocale import GrampsLocale 52from ._datestrings import DateStrings 53 54# _T_ is a gramps-defined keyword -- see po/update_po.py and po/genpot.sh 55def _T_(value): # enable deferred translations (see Python docs 22.1.3.4) 56 return value 57 58#------------------------------------------------------------------------- 59# 60# DateDisplay 61# 62#------------------------------------------------------------------------- 63class DateDisplay: 64 """ 65 Base date display class. 66 """ 67 68 formats = ( 69 # format 0 - must always be ISO 70 _T_("YYYY-MM-DD (ISO)"), 71 72 # format # 1 - must always be locale-preferred numerical format 73 # such as YY.MM.DD, MM-DD-YY, or whatever your locale prefers. 74 # This should be the format that is used under the locale by 75 # strftime() for '%x'. 76 # You may translate this as "Numerical", "System preferred", or similar. 77 _T_("date format|Numerical"), 78 79 # Full month name, day, year 80 _T_("Month Day, Year"), 81 82 # Abbreviated month name, day, year 83 _T_("MON DAY, YEAR"), 84 85 # Day, full month name, year 86 _T_("Day Month Year"), 87 88 # Day, abbreviated month name, year 89 _T_("DAY MON YEAR") 90 ) 91 """ 92 .. note:: Will be overridden if a locale-specific date displayer exists. 93 94 If your localized :meth:`~_display_calendar`/:meth:`~_display_gregorian` 95 are overridden, you should override the whole formats list according 96 to your own formats, and you need not localize the format names here. 97 This ``formats`` must agree with 98 :meth:`~_display_calendar`/:meth:`~_display_gregorian`. 99 """ 100 101 newyear = ("", "Mar1", "Mar25", "Sep1") 102 103 _bce_str = "%s B.C.E." 104 # this will be overridden if a locale-specific date displayer exists 105 106 def __init__(self, format=None, blocale=None): 107 """ 108 :param blocale: allow translation of dates and date formats 109 :type blocale: a :class:`.GrampsLocale` instance 110 """ 111 from ._datehandler import locale_tformat # circular import if above 112 if blocale: 113 self._locale = blocale 114 elif not hasattr(self, '_locale'): 115 self._locale = glocale 116 if self._locale.calendar in locale_tformat: 117 self.dhformat = locale_tformat[self._locale.calendar] # date format 118 else: 119 self.dhformat = locale_tformat['en_GB'] # something is required 120 self.formats_changed() # allow overriding so a subclass can modify 121 self._ds = DateStrings(self._locale) 122 calendar = list(self._ds.calendar) 123 calendar[Date.CAL_GREGORIAN] = "" # that string only used in parsing, 124 # gregorian cal name shouldn't be output! 125 self.calendar = tuple(calendar) 126 self.short_months = self._ds.short_months 127 self.swedish = self.long_months = self._ds.long_months 128 self.hebrew = self._ds.hebrew 129 self.french = self._ds.french 130 self.persian = self._ds.persian 131 self.islamic = self._ds.islamic 132 self.display_cal = [ 133 self._display_gregorian, 134 self._display_julian, 135 self._display_hebrew, 136 self._display_french, 137 self._display_persian, 138 self._display_islamic, 139 self._display_swedish] 140 self._mod_str = self._ds.modifiers 141 self._qual_str = self._ds.qualifiers 142 self.long_days = self._ds.long_days 143 self.short_days = self._ds.short_days # Icelandic needs this 144 145 if format is None: 146 self.format = 0 147 else: 148 self.format = format 149 150 self._ = _ = self._locale.translation.sgettext 151 self.FORMATS_long_month_year = { 152# Inflection control due to modifier. 153# Protocol: DateDisplayXX passes a key to the dictionary in the 154# parameter ``inflect`` to ``_display_calendar``. 155# The modifier passed is not necessarily the one printed, it's just 156# a representative that induces the same inflection control. 157# For example, in Russian "before May", "after May", and "about May" 158# all require genitive form for May, whereas no modifier (just "May 1234") 159# require nominative, so DateDisplayRU.display will pass "before" 160# in all 3 cases, collapsing the 3 modifiers into 1. 161# 162# Another example in Russian is that "between April 1234 and June 1235" 163# requires the same inflection for both April and June, so just "between" 164# is used by DateDisplayRU.display, collapsing two more modifiers into 1. 165# 166# If inflect is not specified, then it means that the modifier doesn't have 167# grammatical control over the format, and so the format can be 168# localized in a context-free way. 169# The translator is responsible for: 170# 1) proper collapse of modifier classes 171# 2) translating the formats that are selected in runtime 172# 3) ignoring the other formats in .po (it does no harm to translate them, 173# it's just a lot of extra work) 174# 175# To prevent POT pollution, not all possibilities are populated here yet. 176# To be amended as the actual localized handlers use it. 177# 178# Not moving to DateStrings, as this is part of display code only, 179# coupled tightly with the formats used in this file. 180 "" 181 : _("{long_month} {year}"), 182 183 "from" 184 # first date in a span 185 # If "from <Month>" needs a special inflection in your 186 # language, translate this to "{long_month.f[X]} {year}" 187 # (where X is one of the month-name inflections you defined) 188 # else leave it untranslated 189 : _("from|{long_month} {year}"), 190 191 "to" 192 # second date in a span 193 # If "to <Month>" needs a special inflection in your 194 # language, translate this to "{long_month.f[X]} {year}" 195 # (where X is one of the month-name inflections you defined) 196 # else leave it untranslated 197 : _("to|{long_month} {year}"), 198 199 "between" 200 # first date in a range 201 # If "between <Month>" needs a special inflection in your 202 # language, translate this to "{long_month.f[X]} {year}" 203 # (where X is one of the month-name inflections you defined) 204 # else leave it untranslated 205 : _("between|{long_month} {year}"), 206 207 "and" 208 # second date in a range 209 # If "and <Month>" needs a special inflection in your 210 # language, translate this to "{long_month.f[X]} {year}" 211 # (where X is one of the month-name inflections you defined) 212 # else leave it untranslated 213 : _("and|{long_month} {year}"), 214 215 "before" 216 # If "before <Month>" needs a special inflection in your 217 # language, translate this to "{long_month.f[X]} {year}" 218 # (where X is one of the month-name inflections you defined) 219 # else leave it untranslated 220 : _("before|{long_month} {year}"), 221 222 "after" 223 # If "after <Month>" needs a special inflection in your 224 # language, translate this to "{long_month.f[X]} {year}" 225 # (where X is one of the month-name inflections you defined) 226 # else leave it untranslated 227 : _("after|{long_month} {year}"), 228 229 "about" 230 # If "about <Month>" needs a special inflection in your 231 # language, translate this to "{long_month.f[X]} {year}" 232 # (where X is one of the month-name inflections you defined) 233 # else leave it untranslated 234 : _("about|{long_month} {year}"), 235 236 "estimated" 237 # If "estimated <Month>" needs a special inflection in your 238 # language, translate this to "{long_month.f[X]} {year}" 239 # (where X is one of the month-name inflections you defined) 240 # else leave it untranslated 241 : _("estimated|{long_month} {year}"), 242 243 "calculated" 244 # If "calculated <Month>" needs a special inflection in your 245 # language, translate this to "{long_month.f[X]} {year}" 246 # (where X is one of the month-name inflections you defined) 247 # else leave it untranslated 248 : _("calculated|{long_month} {year}"), 249 } 250 251 self.FORMATS_short_month_year = { 252 "" 253 : _("{short_month} {year}"), 254 255 "from" 256 # first date in a span 257 # If "from <Month>" needs a special inflection in your 258 # language, translate this to "{short_month.f[X]} {year}" 259 # (where X is one of the month-name inflections you defined) 260 # else leave it untranslated 261 : _("from|{short_month} {year}"), 262 263 "to" 264 # second date in a span 265 # If "to <Month>" needs a special inflection in your 266 # language, translate this to "{short_month.f[X]} {year}" 267 # (where X is one of the month-name inflections you defined) 268 # else leave it untranslated 269 : _("to|{short_month} {year}"), 270 271 "between" 272 # first date in a range 273 # If "between <Month>" needs a special inflection in your 274 # language, translate this to "{short_month.f[X]} {year}" 275 # (where X is one of the month-name inflections you defined) 276 # else leave it untranslated 277 : _("between|{short_month} {year}"), 278 279 "and" 280 # second date in a range 281 # If "and <Month>" needs a special inflection in your 282 # language, translate this to "{short_month.f[X]} {year}" 283 # (where X is one of the month-name inflections you defined) 284 # else leave it untranslated 285 : _("and|{short_month} {year}"), 286 287 "before" 288 # If "before <Month>" needs a special inflection in your 289 # language, translate this to "{short_month.f[X]} {year}" 290 # (where X is one of the month-name inflections you defined) 291 # else leave it untranslated 292 : _("before|{short_month} {year}"), 293 294 "after" 295 # If "after <Month>" needs a special inflection in your 296 # language, translate this to "{short_month.f[X]} {year}" 297 # (where X is one of the month-name inflections you defined) 298 # else leave it untranslated 299 : _("after|{short_month} {year}"), 300 301 "about" 302 # If "about <Month>" needs a special inflection in your 303 # language, translate this to "{short_month.f[X]} {year}" 304 # (where X is one of the month-name inflections you defined) 305 # else leave it untranslated 306 : _("about|{short_month} {year}"), 307 308 "estimated" 309 # If "estimated <Month>" needs a special inflection in your 310 # language, translate this to "{short_month.f[X]} {year}" 311 # (where X is one of the month-name inflections you defined) 312 # else leave it untranslated 313 : _("estimated|{short_month} {year}"), 314 315 "calculated" 316 # If "calculated <Month>" needs a special inflection in your 317 # language, translate this to "{short_month.f[X]} {year}" 318 # (where X is one of the month-name inflections you defined) 319 # else leave it untranslated 320 : _("calculated|{short_month} {year}"), 321 } 322 323 def formats_changed(self): 324 """ Allow overriding so a subclass can modify """ 325 pass 326 327 def set_format(self, format): 328 self.format = format 329 330 def format_extras(self, cal, newyear): 331 """ 332 Formats the extra items (calendar, newyear) for a date. 333 """ 334 scal = self.calendar[cal] 335 if isinstance(newyear, int) and newyear <= len(self.newyear): 336 snewyear = self.newyear[newyear] 337 elif isinstance(newyear, (list, tuple)): 338 snewyear = "%s-%s" % (newyear[0], newyear[1]) 339 else: 340 snewyear = "Err" 341 retval = "" 342 for item in [scal, snewyear]: 343 if item: 344 if retval: 345 # TODO for Arabic, should the next comma be translated? 346 retval += ", " 347 retval += item 348 if retval: 349 return " (%s)" % retval 350 return "" 351 352 def display(self, date): 353 """ 354 Return a text string representing the date. 355 356 Disregard any format settings and use display_iso for each date. 357 358 (Will be overridden if a locale-specific date displayer exists.) 359 360 (The usage is "displayer.display(...)" (or a variant, e.g. _dd.display) 361 so any subclass must have a "display" method, somehow, or use this.) 362 """ 363 mod = date.get_modifier() 364 cal = date.get_calendar() 365 qual = date.get_quality() 366 start = date.get_start_date() 367 newyear = date.get_new_year() 368 369 qual_str = self._qual_str[qual] 370 371 if mod == Date.MOD_TEXTONLY: 372 return date.get_text() 373 elif start == Date.EMPTY: 374 return "" 375 elif mod == Date.MOD_SPAN or mod == Date.MOD_RANGE: 376 d1 = self.display_iso(start) 377 d2 = self.display_iso(date.get_stop_date()) 378 scal = self.format_extras(cal, newyear) 379 return "%s %s - %s%s" % (qual_str, d1, d2, scal) 380 else: 381 text = self.display_iso(start) 382 scal = self.format_extras(cal, newyear) 383 return "%s%s%s%s" % (qual_str, self._mod_str[mod], text, scal) 384 385 def _slash_year(self, val, slash): 386 if val < 0: 387 val = - val 388 389 if slash: 390 if (val-1) % 100 == 99: 391 year = "%d/%d" % (val - 1, (val%1000)) 392 elif (val-1) % 10 == 9: 393 year = "%d/%d" % (val - 1, (val%100)) 394 else: 395 year = "%d/%d" % (val - 1, (val%10)) 396 else: 397 year = "%d" % (val) 398 399 return year 400 401 def display_iso(self, date_val): 402 # YYYY-MM-DD (ISO) 403 year = self._slash_year(date_val[2], date_val[3]) 404 # This produces 1789, 1789-00-11 and 1789-11-00 for incomplete dates. 405 if date_val[0] == date_val[1] == 0: 406 # No month and no day -> year 407 value = year 408 else: 409 value = "%s-%02d-%02d" % (year, date_val[1], date_val[0]) 410 if date_val[2] < 0: 411 return self._bce_str % value 412 else: 413 return value 414 415 def dd_span(self, date): 416 """ 417 Return a text string representing the span date 418 (it may be overridden if a locale-specific date displayer exists) 419 """ 420 cal = date.get_calendar() 421 qual_str = self._qual_str[date.get_quality()] 422 scal = self.format_extras(cal, date.get_new_year()) 423 d1 = self.display_cal[cal](date.get_start_date(), 424 # If there is no special inflection for "from <Month>" 425 # in your language, DON'T translate this string. Otherwise, 426 # "translate" this to "from" in ENGLISH!!! ENGLISH!!! 427 inflect=self._("from-date|")) 428 d2 = self.display_cal[cal](date.get_stop_date(), 429 # If there is no special inflection for "to <Month>" 430 # in your language, DON'T translate this string. Otherwise, 431 # "translate" this to "to" in ENGLISH!!! ENGLISH!!! 432 inflect=self._("to-date|")) 433 return self._("{date_quality}from {date_start} to {date_stop}" 434 "{nonstd_calendar_and_ny}").format( 435 date_quality=qual_str, 436 date_start=d1, 437 date_stop=d2, 438 nonstd_calendar_and_ny=scal) 439 440 def dd_range(self, date): 441 """ 442 Return a text string representing the range date 443 (it may be overridden if a locale-specific date displayer exists) 444 """ 445 cal = date.get_calendar() 446 qual_str = self._qual_str[date.get_quality()] 447 scal = self.format_extras(cal, date.get_new_year()) 448 d1 = self.display_cal[cal](date.get_start_date(), 449 # If there is no special inflection for "between <Month>" 450 # in your language, DON'T translate this string. Otherwise, 451 # "translate" this to "between" in ENGLISH!!! ENGLISH!!! 452 inflect=self._("between-date|")) 453 d2 = self.display_cal[cal](date.get_stop_date(), 454 # If there is no special inflection for "and <Month>" 455 # in your language, DON'T translate this string. Otherwise, 456 # "translate" this to "and" in ENGLISH!!! ENGLISH!!! 457 inflect=self._("and-date|")) 458 return self._("{date_quality}between {date_start} and {date_stop}" 459 "{nonstd_calendar_and_ny}").format( 460 date_quality=qual_str, 461 date_start=d1, 462 date_stop=d2, 463 nonstd_calendar_and_ny=scal) 464 465 def display_formatted(self, date): 466 """ 467 Return a text string representing the date, according to the format. 468 """ 469 mod = date.get_modifier() 470 cal = date.get_calendar() 471 qual = date.get_quality() 472 start = date.get_start_date() 473 newyear = date.get_new_year() 474 475 qual_str = self._qual_str[qual] 476 _ = self._ 477 478 if mod == Date.MOD_TEXTONLY: 479 return date.get_text() 480 elif start == Date.EMPTY: 481 return "" 482 elif mod == Date.MOD_SPAN: 483 return self.dd_span(date) 484 elif mod == Date.MOD_RANGE: 485 return self.dd_range(date) 486 else: 487 if mod == Date.MOD_BEFORE: 488 # If there is no special inflection for "before <Month>" 489 # in your language, DON'T translate this string. Otherwise, 490 # "translate" this to "before" in ENGLISH!!! ENGLISH!!! 491 date_type = _("before-date|") 492 elif mod == Date.MOD_AFTER: 493 # If there is no special inflection for "after <Month>" 494 # in your language, DON'T translate this string. Otherwise, 495 # "translate" this to "after" in ENGLISH!!! ENGLISH!!! 496 date_type = _("after-date|") 497 elif mod == Date.MOD_ABOUT: 498 # If there is no special inflection for "about <Month>" 499 # in your language, DON'T translate this string. Otherwise, 500 # "translate" this to "about" in ENGLISH!!! ENGLISH!!! 501 date_type = _("about-date|") 502 elif qual == Date.QUAL_ESTIMATED: 503 # If there is no special inflection for "estimated <Month>" 504 # in your language, DON'T translate this string. Otherwise, 505 # "translate" this to "estimated" in ENGLISH!!! ENGLISH!!! 506 date_type = _("estimated-date|") 507 elif qual == Date.QUAL_CALCULATED: 508 # If there is no special inflection for "calculated <Month>" 509 # in your language, DON'T translate this string. Otherwise, 510 # "translate" this to "calculated" in ENGLISH!!! ENGLISH!!! 511 date_type = _("calculated-date|") 512 else: 513 date_type = "" 514 # TODO -- do "estimated" and "calculated" need their own "if"? 515 # i.e., what happens if a date is both "modified" and "qualified"? 516 # it won't matter if the month gets the same lexeme type, but 517 # what should be done if the types differ? there can only be one 518 # lexeme type for any month so which one should be last? so we 519 # will wait and see if any language ever requires such fine tuning 520 # as maybe it will be as simple as putting the "elif" choices for 521 # "estimated" and "calculated" before the others, or something 522 text = self.display_cal[cal](start, inflect=date_type) 523 modifier = self._mod_str[mod] 524 # some languages have a modifier after the date (e.g. Finnish) 525 # (if so, they are specified in DateParser.modifier_after_to_int) 526 if modifier.startswith(' '): 527 text += modifier 528 modifier = '' 529 scal = self.format_extras(cal, newyear) 530 return _("{date_quality}{noncompound_modifier}{date}" 531 "{nonstd_calendar_and_ny}").format( 532 date_quality=qual_str, 533 noncompound_modifier=modifier, 534 date=text, 535 nonstd_calendar_and_ny=scal) 536 537 def _display_gregorian(self, date_val, **kwargs): 538 return self._display_calendar(date_val, self.long_months, 539 self.short_months, **kwargs) 540 541 # Julian and Swedish date display is the same as Gregorian 542 _display_julian = _display_swedish = _display_gregorian 543 544 def format_long_month_year(self, month, year, inflect, long_months): 545 if not hasattr(long_months[1], 'f'): # not a Lexeme: no inflection 546 return "{long_month} {year}".format( 547 long_month = long_months[month], year = year) 548 return self.FORMATS_long_month_year[inflect].format( 549 long_month = long_months[month], year = year) 550 551 def format_short_month_year(self, month, year, inflect, short_months): 552 if not hasattr(short_months[1], 'f'): # not a Lexeme: no inflection 553 return "{short_month} {year}".format( 554 short_month = short_months[month], year = year) 555 return self.FORMATS_short_month_year[inflect].format( 556 short_month = short_months[month], year = year) 557 558 def format_long_month(self, month, inflect, long_months): 559 if not hasattr(long_months[1], 'f'): # not a Lexeme: no inflection 560 return "{long_month}".format(long_month = long_months[month]) 561 return self.FORMATS_long_month_year[inflect].format( 562 long_month = long_months[month], year = '').rstrip() 563 564 def format_short_month(self, month, inflect, short_months): 565 if not hasattr(short_months[1], 'f'): # not a Lexeme: no inflection 566 return "{short_month}".format(short_month = short_months[month]) 567 return self.FORMATS_short_month_year[inflect].format( 568 short_month = short_months[month], year = '').rstrip() 569 570 def _get_short_weekday(self, date_val): 571 if (date_val[0] == 0 or date_val[1] == 0 # no day or no month or both 572 or date_val[1] == 13 # Hebrew has 13 months 573 or date_val[2] > datetime.MAXYEAR # bug 10815 574 or date_val[2] < 0): # B.C.E. date 575 return '' 576 w_day = datetime.date(date_val[2], date_val[1], date_val[0]) # y, m, d 577 return self.short_days[((w_day.weekday() + 1) % 7) + 1] 578 579 def _get_long_weekday(self, date_val): 580 if (date_val[0] == 0 or date_val[1] == 0 # no day or no month or both 581 or date_val[1] == 13 # Hebrew has 13 months 582 or date_val[2] > datetime.MAXYEAR # bug 10815 583 or date_val[2] < 0): # B.C.E. date 584 return '' 585 w_day = datetime.date(date_val[2], date_val[1], date_val[0]) # y, m, d 586 return self.long_days[((w_day.weekday() + 1) % 7) + 1] 587 588 def _get_localized_year(self, year): 589 """ Allow a subclass to modify the year, e.g. add a period """ 590 return year 591 592 def dd_dformat01(self, date_val): 593 """ 594 numerical 595 596 this must agree with DateDisplayEn's "formats" definition 597 (it may be overridden if a locale-specific date displayer exists) 598 """ 599 if date_val[3]: 600 return self.display_iso(date_val) 601 else: 602 if date_val[0] == date_val[1] == 0: 603 return self._get_localized_year(str(date_val[2])) 604 else: 605 value = self.dhformat.replace('%m', str(date_val[1])) 606 if '%b' in value or '%B' in value: 607 # some locales have %b for the month (ar_EG, is_IS, nb_NO) 608 # so it would be "Jan" but as it's "numeric" make it "1" 609 value = value.replace('%b', str(date_val[1])) 610 # some locales have %B for the month, e.g. ta_IN 611 # so it would be "January" but as it's "numeric" make it 1 612 value = value.replace('%B', str(date_val[1])) 613 if '%a' in value or '%A' in value: 614 # some locales have %a for the abbreviated day, e.g. is_IS 615 value = value.replace('%a', 616 self._get_short_weekday(date_val)) 617 # some locales have %A for the long/full day, e.g. ta_IN 618 value = value.replace('%A', 619 self._get_long_weekday(date_val)) 620 if date_val[0] == 0: # ignore the zero day and its delimiter 621 i_day = value.find('%d') 622 if len(value) == i_day + 2: # delimiter is left of the day 623 value = value.replace(value[i_day-1:i_day+2], '') 624 else: # delimiter is to the right of the day 625 value = value.replace(value[i_day:i_day+3], '') 626 value = value.replace('%d', str(date_val[0])) 627 value = value.replace('%Y', str(abs(date_val[2]))) 628 return value.replace('-', '/') 629 630 def dd_dformat02(self, date_val, inflect, long_months): 631 """ 632 month_name day, year 633 634 this must agree with DateDisplayEn's "formats" definition 635 (it may be overridden if a locale-specific date displayer exists) 636 """ 637 _ = self._locale.translation.sgettext 638 year = self._slash_year(date_val[2], date_val[3]) 639 if date_val[0] == 0: 640 if date_val[1] == 0: 641 return self._get_localized_year(year) 642 else: 643 return self.format_long_month_year(date_val[1], year, 644 inflect, long_months) 645 elif date_val[1] == 0: # month is zero but day is not (see 8477) 646 return self.display_iso(date_val) 647 else: 648 # TRANSLATORS: this month is ALREADY inflected: ignore it 649 return _("{long_month} {day:d}, {year}").format( 650 long_month = self.format_long_month(date_val[1], 651 inflect, 652 long_months), 653 day = date_val[0], 654 year = year) 655 656 def dd_dformat03(self, date_val, inflect, short_months): 657 """ 658 month_abbreviation day, year 659 660 this must agree with DateDisplayEn's "formats" definition 661 (it may be overridden if a locale-specific date displayer exists) 662 """ 663 _ = self._locale.translation.sgettext 664 year = self._slash_year(date_val[2], date_val[3]) 665 if date_val[0] == 0: 666 if date_val[1] == 0: 667 return self._get_localized_year(year) 668 else: 669 return self.format_short_month_year(date_val[1], year, 670 inflect, short_months) 671 elif date_val[1] == 0: # month is zero but day is not (see 8477) 672 return self.display_iso(date_val) 673 else: 674 # TRANSLATORS: this month is ALREADY inflected: ignore it 675 return _("{short_month} {day:d}, {year}").format( 676 short_month = self.format_short_month(date_val[1], 677 inflect, 678 short_months), 679 day = date_val[0], 680 year = year) 681 682 def dd_dformat04(self, date_val, inflect, long_months): 683 """ 684 day month_name year 685 686 this must agree with DateDisplayEn's "formats" definition 687 (it may be overridden if a locale-specific date displayer exists) 688 """ 689 _ = self._locale.translation.sgettext 690 year = self._slash_year(date_val[2], date_val[3]) 691 if date_val[0] == 0: 692 if date_val[1] == 0: 693 return self._get_localized_year(year) 694 else: 695 return self.format_long_month_year(date_val[1], year, 696 inflect, long_months) 697 elif date_val[1] == 0: # month is zero but day is not (see 8477) 698 return self.display_iso(date_val) 699 else: 700 # TRANSLATORS: this month is ALREADY inflected: ignore it 701 return _("{day:d} {long_month} {year}").format( 702 day = date_val[0], 703 long_month = self.format_long_month(date_val[1], 704 inflect, 705 long_months), 706 year = year) 707 708 def dd_dformat05(self, date_val, inflect, short_months): 709 """ 710 day month_abbreviation year 711 712 this must agree with DateDisplayEn's "formats" definition 713 (it may be overridden if a locale-specific date displayer exists) 714 """ 715 _ = self._locale.translation.sgettext 716 year = self._slash_year(date_val[2], date_val[3]) 717 if date_val[0] == 0: 718 if date_val[1] == 0: 719 return self._get_localized_year(year) 720 else: 721 return self.format_short_month_year(date_val[1], year, 722 inflect, short_months) 723 elif date_val[1] == 0: # month is zero but day is not (see 8477) 724 return self.display_iso(date_val) 725 else: 726 # TRANSLATORS: this month is ALREADY inflected: ignore it 727 return _("{day:d} {short_month} {year}").format( 728 day = date_val[0], 729 short_month = self.format_short_month(date_val[1], 730 inflect, 731 short_months), 732 year = year) 733 734 def _display_calendar(self, date_val, long_months, short_months = None, 735 inflect=""): 736 """ 737 this must agree with DateDisplayEn's "formats" definition 738 (it may be overridden if a locale-specific date displayer exists) 739 """ 740 741 if short_months is None: 742 # Let the short formats work the same as long formats 743 short_months = long_months 744 745 if self.format == 0: 746 return self.display_iso(date_val) 747 elif self.format == 1: 748 # numerical 749 value = self.dd_dformat01(date_val) 750 elif self.format == 2: 751 # month_name day, year 752 value = self.dd_dformat02(date_val, inflect, long_months) 753 elif self.format == 3: 754 # month_abbreviation day, year 755 value = self.dd_dformat03(date_val, inflect, short_months) 756 elif self.format == 4: 757 # day month_name year 758 value = self.dd_dformat04(date_val, inflect, long_months) 759 # elif self.format == 5: 760 else: 761 # day month_abbreviation year 762 value = self.dd_dformat05(date_val, inflect, short_months) 763 if date_val[2] < 0: 764 # TODO fix BUG 7064: non-Gregorian calendars wrongly use BCE notation for negative dates 765 return self._bce_str % value 766 else: 767 return value 768 769 770 def _display_french(self, date_val, **kwargs): 771 return self._display_calendar(date_val, self.french, **kwargs) 772 773 def _display_hebrew(self, date_val, **kwargs): 774 return self._display_calendar(date_val, self.hebrew, **kwargs) 775 776 def _display_persian(self, date_val, **kwargs): 777 return self._display_calendar(date_val, self.persian, **kwargs) 778 779 def _display_islamic(self, date_val, **kwargs): 780 return self._display_calendar(date_val, self.islamic, **kwargs) 781 782class DateDisplayEn(DateDisplay): 783 """ 784 English language date display class. 785 """ 786 display = DateDisplay.display_formatted 787 788class DateDisplayGB(DateDisplay): 789 """ 790 British-English language date display class (its format is different). 791 """ 792 display = DateDisplay.display_formatted 793