def get_plural(locale=LC_CTYPE): """A tuple with the information catalogs need to perform proper pluralization. The first item of the tuple is the number of plural forms, the second the plural expression. >>> get_plural(locale='en') (2, '(n != 1)') >>> get_plural(locale='ga') (3, '(n==1 ? 0 : n==2 ? 1 : 2)') The object returned is a special tuple with additional members: >>> tup = get_plural("ja") >>> tup.num_plurals 1 >>> tup.plural_expr '0' >>> tup.plural_forms 'npurals=1; plural=0' Converting the tuple into a string prints the plural forms for a gettext catalog: >>> str(tup) 'npurals=1; plural=0' """ locale = Locale.parse(locale) try: tup = PLURALS[str(locale)] except KeyError: try: tup = PLURALS[locale.language] except KeyError: tup = DEFAULT_PLURAL return _PluralTuple(tup)
def format_decimal(number, format=None, locale=LC_NUMERIC): u"""Return the given decimal number formatted for a specific locale. >>> format_decimal(1.2345, locale='en_US') u'1.234' >>> format_decimal(1.2346, locale='en_US') u'1.235' >>> format_decimal(-1.2346, locale='en_US') u'-1.235' >>> format_decimal(1.2345, locale='sv_SE') u'1,234' >>> format_decimal(1.2345, locale='de') u'1,234' The appropriate thousands grouping and the decimal separator are used for each locale: >>> format_decimal(12345.5, locale='en_US') u'12,345.5' :param number: the number to format :param format: :param locale: the `Locale` object or locale identifier """ locale = Locale.parse(locale) if not format: format = locale.decimal_formats.get(format) pattern = parse_pattern(format) return pattern.apply(number, locale)
def format_date(date=None, format="medium", locale=LC_TIME): """Return a date formatted according to the given pattern. >>> d = date(2007, 04, 01) >>> format_date(d, locale='en_US') u'Apr 1, 2007' >>> format_date(d, format='full', locale='de_DE') u'Sonntag, 1. April 2007' If you don't want to use the locale default formats, you can specify a custom date pattern: >>> format_date(d, "EEE, MMM d, ''yy", locale='en') u"Sun, Apr 1, '07" :param date: the ``date`` or ``datetime`` object; if `None`, the current date is used :param format: one of "full", "long", "medium", or "short", or a custom date/time pattern :param locale: a `Locale` object or a locale identifier """ if date is None: date = date_.today() elif isinstance(date, datetime): date = date.date() locale = Locale.parse(locale) if format in ("full", "long", "medium", "short"): format = get_date_format(format, locale=locale) pattern = parse_pattern(format) return pattern.apply(date, locale)
def format_currency(number, currency, format=None, locale=LC_NUMERIC): u"""Return formatted currency value. >>> format_currency(1099.98, 'USD', locale='en_US') u'$1,099.98' >>> format_currency(1099.98, 'USD', locale='es_CO') u'1.099,98\\xa0US$' >>> format_currency(1099.98, 'EUR', locale='de_DE') u'1.099,98\\xa0\\u20ac' The pattern can also be specified explicitly. The currency is placed with the '¤' sign. As the sign gets repeated the format expands (¤ being the symbol, ¤¤ is the currency abbreviation and ¤¤¤ is the full name of the currency): >>> format_currency(1099.98, 'EUR', u'\xa4\xa4 #,##0.00', locale='en_US') u'EUR 1,099.98' >>> format_currency(1099.98, 'EUR', u'#,##0.00 \xa4\xa4\xa4', locale='en_US') u'1,099.98 euros' :param number: the number to format :param currency: the currency code :param locale: the `Locale` object or locale identifier """ locale = Locale.parse(locale) if not format: format = locale.currency_formats.get(format) pattern = parse_pattern(format) return pattern.apply(number, locale, currency=currency)
def parse_decimal(string, locale=LC_NUMERIC): """Parse localized decimal string into a decimal. >>> parse_decimal('1,099.98', locale='en_US') Decimal('1099.98') >>> parse_decimal('1.099,98', locale='de') Decimal('1099.98') When the given string cannot be parsed, an exception is raised: >>> parse_decimal('2,109,998', locale='de') Traceback (most recent call last): ... NumberFormatError: '2,109,998' is not a valid decimal number :param string: the string to parse :param locale: the `Locale` object or locale identifier :raise NumberFormatError: if the string can not be converted to a decimal number """ locale = Locale.parse(locale) try: return Decimal(string.replace(get_group_symbol(locale), '') .replace(get_decimal_symbol(locale), '.')) except InvalidOperation: raise NumberFormatError('%r is not a valid decimal number' % string)
def __init__(self, locale=None, domain=None, header_comment=DEFAULT_HEADER, project=None, version=None, copyright_holder=None, msgid_bugs_address=None, creation_date=None, revision_date=None, last_translator=None, language_team=None, charset=None, fuzzy=True): """Initialize the catalog object. :param locale: the locale identifier or `Locale` object, or `None` if the catalog is not bound to a locale (which basically means it's a template) :param domain: the message domain :param header_comment: the header comment as string, or `None` for the default header :param project: the project's name :param version: the project's version :param copyright_holder: the copyright holder of the catalog :param msgid_bugs_address: the email address or URL to submit bug reports to :param creation_date: the date the catalog was created :param revision_date: the date the catalog was revised :param last_translator: the name and email of the last translator :param language_team: the name and email of the language team :param charset: the encoding to use in the output (defaults to utf-8) :param fuzzy: the fuzzy bit on the catalog header """ self.domain = domain #: The message domain if locale: locale = Locale.parse(locale) self.locale = locale #: The locale or `None` self._header_comment = header_comment self._messages = odict() self.project = project or 'PROJECT' #: The project name self.version = version or 'VERSION' #: The project version self.copyright_holder = copyright_holder or 'ORGANIZATION' self.msgid_bugs_address = msgid_bugs_address or 'EMAIL@ADDRESS' self.last_translator = last_translator or 'FULL NAME <EMAIL@ADDRESS>' """Name and email address of the last translator.""" self.language_team = language_team or 'LANGUAGE <*****@*****.**>' """Name and email address of the language team.""" self.charset = charset or 'utf-8' if creation_date is None: creation_date = datetime.now(LOCALTZ) elif isinstance(creation_date, datetime) and not creation_date.tzinfo: creation_date = creation_date.replace(tzinfo=LOCALTZ) self.creation_date = creation_date #: Creation date of the template if revision_date is None: revision_date = 'YEAR-MO-DA HO:MI+ZONE' elif isinstance(revision_date, datetime) and not revision_date.tzinfo: revision_date = revision_date.replace(tzinfo=LOCALTZ) self.revision_date = revision_date #: Last revision date of the catalog self.fuzzy = fuzzy #: Catalog header fuzzy bit (`True` or `False`) self.obsolete = odict() #: Dictionary of obsolete messages self._num_plurals = None self._plural_expr = None
def __init__(self, locale, tzinfo=None): """Initialize the formatter. :param locale: the locale identifier or `Locale` instance :param tzinfo: the time-zone info (a `tzinfo` instance or `None`) """ self.locale = Locale.parse(locale) self.tzinfo = tzinfo
def get_decimal_symbol(locale=LC_NUMERIC): """Return the symbol used by the locale to separate decimal fractions. >>> get_decimal_symbol('en_US') u'.' :param locale: the `Locale` object or locale identifier """ return Locale.parse(locale).number_symbols.get('decimal', u'.')
def get_minus_sign_symbol(locale=LC_NUMERIC): """Return the plus sign symbol used by the current locale. >>> get_minus_sign_symbol('en_US') u'-' :param locale: the `Locale` object or locale identifier """ return Locale.parse(locale).number_symbols.get('minusSign', u'-')
def get_exponential_symbol(locale=LC_NUMERIC): """Return the symbol used by the locale to separate mantissa and exponent. >>> get_exponential_symbol('en_US') u'E' :param locale: the `Locale` object or locale identifier """ return Locale.parse(locale).number_symbols.get('exponential', u'E')
def get_group_symbol(locale=LC_NUMERIC): """Return the symbol used by the locale to separate groups of thousands. >>> get_group_symbol('en_US') u',' :param locale: the `Locale` object or locale identifier """ return Locale.parse(locale).number_symbols.get('group', u',')
def get_period_names(locale=LC_TIME): """Return the names for day periods (AM/PM) used by the locale. >>> get_period_names(locale='en_US')['am'] u'AM' :param locale: the `Locale` object, or a locale string """ return Locale.parse(locale).periods
def get_currency_symbol(currency, locale=LC_NUMERIC): """Return the symbol used by the locale for the specified currency. >>> get_currency_symbol('USD', locale='en_US') u'$' :param currency: the currency code :param locale: the `Locale` object or locale identifier """ return Locale.parse(locale).currency_symbols.get(currency, currency)
def test_1_num_plurals_checkers(self): for _locale in [p for p in PLURALS if PLURALS[p][0] == 1]: try: locale = Locale.parse(_locale) except UnknownLocaleError: # Just an alias? Not what we're testing here, let's continue continue po_file = (u"""\ # %(english_name)s translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. # FIRST AUTHOR <EMAIL@ADDRESS>, 2007. # msgid "" msgstr "" "Project-Id-Version: TestProject 0.1\\n" "Report-Msgid-Bugs-To: [email protected]\\n" "POT-Creation-Date: 2007-04-01 15:30+0200\\n" "PO-Revision-Date: %(date)s\\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n" "Language-Team: %(locale)s <*****@*****.**>\n" "Plural-Forms: nplurals=%(num_plurals)s; plural=%(plural_expr)s\\n" "MIME-Version: 1.0\\n" "Content-Type: text/plain; charset=utf-8\\n" "Content-Transfer-Encoding: 8bit\\n" "Generated-By: Babel %(version)s\\n" #. This will be a translator comment, #. that will include several lines #: project/file1.py:8 msgid "bar" msgstr "" #: project/file2.py:9 msgid "foobar" msgid_plural "foobars" msgstr[0] "" """ % dict(locale = _locale, english_name = locale.english_name, version = VERSION, year = time.strftime('%Y'), date = format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale=_locale), num_plurals = PLURALS[_locale][0], plural_expr = PLURALS[_locale][0])).encode('utf-8') # This test will fail for revisions <= 406 because so far # catalog.num_plurals was neglected catalog = read_po(BytesIO(po_file), _locale) message = catalog['foobar'] checkers.num_plurals(catalog, message)
def get_era_names(width="wide", locale=LC_TIME): """Return the era names used by the locale for the specified format. >>> get_era_names('wide', locale='en_US')[1] u'Anno Domini' >>> get_era_names('abbreviated', locale='de_DE')[1] u'n. Chr.' :param width: the width to use, either "wide", "abbreviated", or "narrow" :param locale: the `Locale` object, or a locale string """ return Locale.parse(locale).eras[width]
def get_quarter_names(width="wide", context="format", locale=LC_TIME): """Return the quarter names used by the locale for the specified format. >>> get_quarter_names('wide', locale='en_US')[1] u'1st quarter' >>> get_quarter_names('abbreviated', locale='de_DE')[1] u'Q1' :param width: the width to use, one of "wide", "abbreviated", or "narrow" :param context: the context, either "format" or "stand-alone" :param locale: the `Locale` object, or a locale string """ return Locale.parse(locale).quarters[context][width]
def get_time_format(format="medium", locale=LC_TIME): """Return the time formatting patterns used by the locale for the specified format. >>> get_time_format(locale='en_US') <DateTimePattern u'h:mm:ss a'> >>> get_time_format('full', locale='de_DE') <DateTimePattern u'HH:mm:ss zzzz'> :param format: the format to use, one of "full", "long", "medium", or "short" :param locale: the `Locale` object, or a locale string """ return Locale.parse(locale).time_formats[format]
def get_month_names(width="wide", context="format", locale=LC_TIME): """Return the month names used by the locale for the specified format. >>> get_month_names('wide', locale='en_US')[1] u'January' >>> get_month_names('abbreviated', locale='es')[1] u'ene' >>> get_month_names('narrow', context='stand-alone', locale='de_DE')[1] u'J' :param width: the width to use, one of "wide", "abbreviated", or "narrow" :param context: the context, either "format" or "stand-alone" :param locale: the `Locale` object, or a locale string """ return Locale.parse(locale).months[context][width]
def get_datetime_format(format="medium", locale=LC_TIME): """Return the datetime formatting patterns used by the locale for the specified format. >>> get_datetime_format(locale='en_US') u'{1}, {0}' :param format: the format to use, one of "full", "long", "medium", or "short" :param locale: the `Locale` object, or a locale string """ patterns = Locale.parse(locale).datetime_formats if format not in patterns: format = None return patterns[format]
def format_datetime(datetime=None, format="medium", tzinfo=None, locale=LC_TIME): r"""Return a date formatted according to the given pattern. >>> dt = datetime(2007, 04, 01, 15, 30) >>> format_datetime(dt, locale='en_US') u'Apr 1, 2007, 3:30:00 PM' For any pattern requiring the display of the time-zone, the third-party ``pytz`` package is needed to explicitly specify the time-zone: >>> format_datetime(dt, 'full', tzinfo=get_timezone('Europe/Paris'), ... locale='fr_FR') u'dimanche 1 avril 2007 17:30:00 heure avanc\xe9e d\u2019Europe centrale' >>> format_datetime(dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz", ... tzinfo=get_timezone('US/Eastern'), locale='en') u'2007.04.01 AD at 11:30:00 EDT' :param datetime: the `datetime` object; if `None`, the current date and time is used :param format: one of "full", "long", "medium", or "short", or a custom date/time pattern :param tzinfo: the timezone to apply to the time for display :param locale: a `Locale` object or a locale identifier """ if datetime is None: datetime = datetime_.utcnow() elif isinstance(datetime, number_types): datetime = datetime_.utcfromtimestamp(datetime) elif isinstance(datetime, time): datetime = datetime_.combine(date.today(), datetime) if datetime.tzinfo is None: datetime = datetime.replace(tzinfo=UTC) if tzinfo is not None: datetime = datetime.astimezone(get_timezone(tzinfo)) if hasattr(tzinfo, "normalize"): # pytz datetime = tzinfo.normalize(datetime) locale = Locale.parse(locale) if format in ("full", "long", "medium", "short"): return ( get_datetime_format(format, locale=locale) .replace("'", "") .replace("{0}", format_time(datetime, format, tzinfo=None, locale=locale)) .replace("{1}", format_date(datetime, format, locale=locale)) ) else: return parse_pattern(format).apply(datetime, locale)
def get_timezone_gmt(datetime=None, width="long", locale=LC_TIME): """Return the timezone associated with the given `datetime` object formatted as string indicating the offset from GMT. >>> dt = datetime(2007, 4, 1, 15, 30) >>> get_timezone_gmt(dt, locale='en') u'GMT+00:00' >>> tz = get_timezone('America/Los_Angeles') >>> dt = datetime(2007, 4, 1, 15, 30, tzinfo=tz) >>> get_timezone_gmt(dt, locale='en') u'GMT-08:00' >>> get_timezone_gmt(dt, 'short', locale='en') u'-0800' The long format depends on the locale, for example in France the acronym UTC string is used instead of GMT: >>> get_timezone_gmt(dt, 'long', locale='fr_FR') u'UTC-08:00' .. versionadded:: 0.9 :param datetime: the ``datetime`` object; if `None`, the current date and time in UTC is used :param width: either "long" or "short" :param locale: the `Locale` object, or a locale string """ if datetime is None: datetime = datetime_.utcnow() elif isinstance(datetime, integer_types): datetime = datetime_.utcfromtimestamp(datetime).time() if datetime.tzinfo is None: datetime = datetime.replace(tzinfo=UTC) locale = Locale.parse(locale) offset = datetime.tzinfo.utcoffset(datetime) seconds = offset.days * 24 * 60 * 60 + offset.seconds hours, seconds = divmod(seconds, 3600) if width == "short": pattern = u"%+03d%02d" else: pattern = locale.zone_formats["gmt"] % "%+03d:%02d" return pattern % (hours, seconds // 60)
def get_currency_name(currency, count=None, locale=LC_NUMERIC): """Return the name used by the locale for the specified currency. >>> get_currency_name('USD', locale='en_US') u'US Dollar' .. versionadded:: 0.9.4 :param currency: the currency code :param count: the optional count. If provided the currency name will be pluralized to that number if possible. :param locale: the `Locale` object or locale identifier """ loc = Locale.parse(locale) if count is not None: plural_form = loc.plural_form(count) plural_names = loc._data['currency_names_plural'] if currency in plural_names: return plural_names[currency][plural_form] return loc.currencies.get(currency, currency)
def format_scientific(number, format=None, locale=LC_NUMERIC): """Return value formatted in scientific notation for a specific locale. >>> format_scientific(10000, locale='en_US') u'1E4' The format pattern can also be specified explicitly: >>> format_scientific(1234567, u'##0E00', locale='en_US') u'1.23E06' :param number: the number to format :param format: :param locale: the `Locale` object or locale identifier """ locale = Locale.parse(locale) if not format: format = locale.scientific_formats.get(format) pattern = parse_pattern(format) return pattern.apply(number, locale)
def format_percent(number, format=None, locale=LC_NUMERIC): """Return formatted percent value for a specific locale. >>> format_percent(0.34, locale='en_US') u'34%' >>> format_percent(25.1234, locale='en_US') u'2,512%' >>> format_percent(25.1234, locale='sv_SE') u'2\\xa0512\\xa0%' The format pattern can also be specified explicitly: >>> format_percent(25.1234, u'#,##0\u2030', locale='en_US') u'25,123\u2030' :param number: the percent number to format :param format: :param locale: the `Locale` object or locale identifier """ locale = Locale.parse(locale) if not format: format = locale.percent_formats.get(format) pattern = parse_pattern(format) return pattern.apply(number, locale)
def __init__(self, value, locale): assert isinstance(value, (date, datetime, time)) if isinstance(value, (datetime, time)) and value.tzinfo is None: value = value.replace(tzinfo=UTC) self.value = value self.locale = Locale.parse(locale)
def get_timezone_location(dt_or_tzinfo=None, locale=LC_TIME): """Return a representation of the given timezone using "location format". The result depends on both the local display name of the country and the city associated with the time zone: >>> tz = get_timezone('America/St_Johns') >>> get_timezone_location(tz, locale='de_DE') u"Kanada (St. John's) Zeit" >>> tz = get_timezone('America/Mexico_City') >>> get_timezone_location(tz, locale='de_DE') u'Mexiko (Mexiko-Stadt) Zeit' If the timezone is associated with a country that uses only a single timezone, just the localized country name is returned: >>> tz = get_timezone('Europe/Berlin') >>> get_timezone_name(tz, locale='de_DE') u'Mitteleurop\\xe4ische Zeit' .. versionadded:: 0.9 :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines the timezone; if `None`, the current date and time in UTC is assumed :param locale: the `Locale` object, or a locale string :return: the localized timezone name using location format """ if dt_or_tzinfo is None: dt = datetime.now() tzinfo = LOCALTZ elif isinstance(dt_or_tzinfo, string_types): dt = None tzinfo = get_timezone(dt_or_tzinfo) elif isinstance(dt_or_tzinfo, integer_types): dt = None tzinfo = UTC elif isinstance(dt_or_tzinfo, (datetime, time)): dt = dt_or_tzinfo if dt.tzinfo is not None: tzinfo = dt.tzinfo else: tzinfo = UTC else: dt = None tzinfo = dt_or_tzinfo locale = Locale.parse(locale) if hasattr(tzinfo, "zone"): zone = tzinfo.zone else: zone = tzinfo.tzname(dt or datetime.utcnow()) # Get the canonical time-zone code zone = get_global("zone_aliases").get(zone, zone) info = locale.time_zones.get(zone, {}) # Otherwise, if there is only one timezone for the country, return the # localized country name region_format = locale.zone_formats["region"] territory = get_global("zone_territories").get(zone) if territory not in locale.territories: territory = "ZZ" # invalid/unknown territory_name = locale.territories[territory] if territory and len(get_global("territory_zones").get(territory, [])) == 1: return region_format % (territory_name) # Otherwise, include the city in the output fallback_format = locale.zone_formats["fallback"] if "city" in info: city_name = info["city"] else: metazone = get_global("meta_zones").get(zone) metazone_info = locale.meta_zones.get(metazone, {}) if "city" in metazone_info: city_name = metazone_info["city"] elif "/" in zone: city_name = zone.split("/", 1)[1].replace("_", " ") else: city_name = zone.replace("_", " ") return region_format % (fallback_format % {"0": city_name, "1": territory_name})
def get_timezone_name(dt_or_tzinfo=None, width="long", uncommon=False, locale=LC_TIME, zone_variant=None): r"""Return the localized display name for the given timezone. The timezone may be specified using a ``datetime`` or `tzinfo` object. >>> dt = time(15, 30, tzinfo=get_timezone('America/Los_Angeles')) >>> get_timezone_name(dt, locale='en_US') u'Pacific Standard Time' >>> get_timezone_name(dt, width='short', locale='en_US') u'PST' If this function gets passed only a `tzinfo` object and no concrete `datetime`, the returned display name is indenpendent of daylight savings time. This can be used for example for selecting timezones, or to set the time of events that recur across DST changes: >>> tz = get_timezone('America/Los_Angeles') >>> get_timezone_name(tz, locale='en_US') u'Pacific Time' >>> get_timezone_name(tz, 'short', locale='en_US') u'PT' If no localized display name for the timezone is available, and the timezone is associated with a country that uses only a single timezone, the name of that country is returned, formatted according to the locale: >>> tz = get_timezone('Europe/Berlin') >>> get_timezone_name(tz, locale='de_DE') u'Mitteleurop\xe4ische Zeit' >>> get_timezone_name(tz, locale='pt_BR') u'Hor\xe1rio da Europa Central' On the other hand, if the country uses multiple timezones, the city is also included in the representation: >>> tz = get_timezone('America/St_Johns') >>> get_timezone_name(tz, locale='de_DE') u'Neufundland-Zeit' Note that short format is currently not supported for all timezones and all locales. This is partially because not every timezone has a short code in every locale. In that case it currently falls back to the long format. For more information see `LDML Appendix J: Time Zone Display Names <http://www.unicode.org/reports/tr35/#Time_Zone_Fallback>`_ .. versionadded:: 0.9 .. versionchanged:: 1.0 Added `zone_variant` support. :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines the timezone; if a ``tzinfo`` object is used, the resulting display name will be generic, i.e. independent of daylight savings time; if `None`, the current date in UTC is assumed :param width: either "long" or "short" :param uncommon: deprecated and ignored :param zone_variant: defines the zone variation to return. By default the variation is defined from the datetime object passed in. If no datetime object is passed in, the ``'generic'`` variation is assumed. The following values are valid: ``'generic'``, ``'daylight'`` and ``'standard'``. :param locale: the `Locale` object, or a locale string """ if dt_or_tzinfo is None: dt = datetime.now() tzinfo = LOCALTZ elif isinstance(dt_or_tzinfo, string_types): dt = None tzinfo = get_timezone(dt_or_tzinfo) elif isinstance(dt_or_tzinfo, integer_types): dt = None tzinfo = UTC elif isinstance(dt_or_tzinfo, (datetime, time)): dt = dt_or_tzinfo if dt.tzinfo is not None: tzinfo = dt.tzinfo else: tzinfo = UTC else: dt = None tzinfo = dt_or_tzinfo locale = Locale.parse(locale) if hasattr(tzinfo, "zone"): zone = tzinfo.zone else: zone = tzinfo.tzname(dt) if zone_variant is None: if dt is None: zone_variant = "generic" else: dst = tzinfo.dst(dt) if dst: zone_variant = "daylight" else: zone_variant = "standard" else: if zone_variant not in ("generic", "standard", "daylight"): raise ValueError("Invalid zone variation") # Get the canonical time-zone code zone = get_global("zone_aliases").get(zone, zone) info = locale.time_zones.get(zone, {}) # Try explicitly translated zone names first if width in info: if zone_variant in info[width]: return info[width][zone_variant] metazone = get_global("meta_zones").get(zone) if metazone: metazone_info = locale.meta_zones.get(metazone, {}) if width in metazone_info: if zone_variant in metazone_info[width]: return metazone_info[width][zone_variant] # If we have a concrete datetime, we assume that the result can't be # independent of daylight savings time, so we return the GMT offset if dt is not None: return get_timezone_gmt(dt, width=width, locale=locale) return get_timezone_location(dt_or_tzinfo, locale=locale)
def format_time(time=None, format="medium", tzinfo=None, locale=LC_TIME): r"""Return a time formatted according to the given pattern. >>> t = time(15, 30) >>> format_time(t, locale='en_US') u'3:30:00 PM' >>> format_time(t, format='short', locale='de_DE') u'15:30' If you don't want to use the locale default formats, you can specify a custom time pattern: >>> format_time(t, "hh 'o''clock' a", locale='en') u"03 o'clock PM" For any pattern requiring the display of the time-zone a timezone has to be specified explicitly: >>> t = datetime(2007, 4, 1, 15, 30) >>> tzinfo = get_timezone('Europe/Paris') >>> t = tzinfo.localize(t) >>> format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR') u'15:30:00 heure avanc\xe9e d\u2019Europe centrale' >>> format_time(t, "hh 'o''clock' a, zzzz", tzinfo=get_timezone('US/Eastern'), ... locale='en') u"09 o'clock AM, Eastern Daylight Time" As that example shows, when this function gets passed a ``datetime.datetime`` value, the actual time in the formatted string is adjusted to the timezone specified by the `tzinfo` parameter. If the ``datetime`` is "naive" (i.e. it has no associated timezone information), it is assumed to be in UTC. These timezone calculations are **not** performed if the value is of type ``datetime.time``, as without date information there's no way to determine what a given time would translate to in a different timezone without information about whether daylight savings time is in effect or not. This means that time values are left as-is, and the value of the `tzinfo` parameter is only used to display the timezone name if needed: >>> t = time(15, 30) >>> format_time(t, format='full', tzinfo=get_timezone('Europe/Paris'), ... locale='fr_FR') u'15:30:00 heure normale de l\u2019Europe centrale' >>> format_time(t, format='full', tzinfo=get_timezone('US/Eastern'), ... locale='en_US') u'3:30:00 PM Eastern Standard Time' :param time: the ``time`` or ``datetime`` object; if `None`, the current time in UTC is used :param format: one of "full", "long", "medium", or "short", or a custom date/time pattern :param tzinfo: the time-zone to apply to the time for display :param locale: a `Locale` object or a locale identifier """ if time is None: time = datetime.utcnow() elif isinstance(time, number_types): time = datetime.utcfromtimestamp(time) if time.tzinfo is None: time = time.replace(tzinfo=UTC) if isinstance(time, datetime): if tzinfo is not None: time = time.astimezone(tzinfo) if hasattr(tzinfo, "normalize"): # pytz time = tzinfo.normalize(time) time = time.timetz() elif tzinfo is not None: time = time.replace(tzinfo=tzinfo) locale = Locale.parse(locale) if format in ("full", "long", "medium", "short"): format = get_time_format(format, locale=locale) return parse_pattern(format).apply(time, locale)
def format_timedelta(delta, granularity="second", threshold=0.85, add_direction=False, format="medium", locale=LC_TIME): """Return a time delta according to the rules of the given locale. >>> format_timedelta(timedelta(weeks=12), locale='en_US') u'3 months' >>> format_timedelta(timedelta(seconds=1), locale='es') u'1 segundo' The granularity parameter can be provided to alter the lowest unit presented, which defaults to a second. >>> format_timedelta(timedelta(hours=3), granularity='day', ... locale='en_US') u'1 day' The threshold parameter can be used to determine at which value the presentation switches to the next higher unit. A higher threshold factor means the presentation will switch later. For example: >>> format_timedelta(timedelta(hours=23), threshold=0.9, locale='en_US') u'1 day' >>> format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US') u'23 hours' In addition directional information can be provided that informs the user if the date is in the past or in the future: >>> format_timedelta(timedelta(hours=1), add_direction=True, locale='en') u'In 1 hour' >>> format_timedelta(timedelta(hours=-1), add_direction=True, locale='en') u'1 hour ago' :param delta: a ``timedelta`` object representing the time difference to format, or the delta in seconds as an `int` value :param granularity: determines the smallest unit that should be displayed, the value can be one of "year", "month", "week", "day", "hour", "minute" or "second" :param threshold: factor that determines at which point the presentation switches to the next higher unit :param add_direction: if this flag is set to `True` the return value will include directional information. For instance a positive timedelta will include the information about it being in the future, a negative will be information about the value being in the past. :param format: the format (currently only "medium" and "short" are supported) :param locale: a `Locale` object or a locale identifier """ if format not in ("short", "medium"): raise TypeError('Format can only be one of "short" or "medium"') if isinstance(delta, timedelta): seconds = int((delta.days * 86400) + delta.seconds) else: seconds = delta locale = Locale.parse(locale) def _iter_choices(unit): if add_direction: if seconds >= 0: yield unit + "-future" else: yield unit + "-past" yield unit + ":" + format yield unit for unit, secs_per_unit in TIMEDELTA_UNITS: value = abs(seconds) / secs_per_unit if value >= threshold or unit == granularity: if unit == granularity and value > 0: value = max(1, value) value = int(round(value)) plural_form = locale.plural_form(value) pattern = None for choice in _iter_choices(unit): patterns = locale._data["unit_patterns"].get(choice) if patterns is not None: pattern = patterns[plural_form] break # This really should not happen if pattern is None: return u"" return pattern.replace("{0}", str(value)) return u""