#
# Copyright (C) 2007-2011 Edgewall Software, 2013-2023 the Babel team
# All rights reserved.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution. The terms
# are also available at http://babel.edgewall.org/wiki/License.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://babel.edgewall.org/log/.

import decimal
import unittest
from datetime import date

import pytest

from babel import localedata, numbers
from babel.numbers import (
    UnknownCurrencyError,
    get_currency_precision,
    get_currency_unit_pattern,
    get_decimal_precision,
    is_currency,
    list_currencies,
    normalize_currency,
    validate_currency,
)


class FormatDecimalTestCase(unittest.TestCase):

    def test_patterns(self):
        assert numbers.format_decimal(12345, '##0', locale='en_US') == '12345'
        assert numbers.format_decimal(6.5, '0.00', locale='sv') == '6,50'
        assert numbers.format_decimal((10.0 ** 20), '#.00', locale='en_US') == '100000000000000000000.00'
        # regression test for #183, fraction digits were not correctly cut
        # if the input was a float value and the value had more than 7
        # significant digits
        assert numbers.format_decimal(12345678.051, '#,##0.00', locale='en_US') == '12,345,678.05'

    def test_subpatterns(self):
        assert numbers.format_decimal((- 12345), '#,##0.##;-#', locale='en_US') == '-12,345'
        assert numbers.format_decimal((- 12345), '#,##0.##;(#)', locale='en_US') == '(12,345)'

    def test_default_rounding(self):
        """
        Testing Round-Half-Even (Banker's rounding)

        A '5' is rounded to the closest 'even' number
        """
        assert numbers.format_decimal(5.5, '0', locale='sv') == '6'
        assert numbers.format_decimal(6.5, '0', locale='sv') == '6'
        assert numbers.format_decimal(6.5, '0', locale='sv') == '6'
        assert numbers.format_decimal(1.2325, locale='sv') == '1,232'
        assert numbers.format_decimal(1.2335, locale='sv') == '1,234'

    def test_significant_digits(self):
        """Test significant digits patterns"""
        assert numbers.format_decimal(123004, '@@', locale='en_US') == '120000'
        assert numbers.format_decimal(1.12, '@', locale='sv') == '1'
        assert numbers.format_decimal(1.1, '@@', locale='sv') == '1,1'
        assert numbers.format_decimal(1.1, '@@@@@##', locale='sv') == '1,1000'
        assert numbers.format_decimal(0.0001, '@@@', locale='sv') == '0,000100'
        assert numbers.format_decimal(0.0001234, '@@@', locale='sv') == '0,000123'
        assert numbers.format_decimal(0.0001234, '@@@#', locale='sv') == '0,0001234'
        assert numbers.format_decimal(0.0001234, '@@@#', locale='sv') == '0,0001234'
        assert numbers.format_decimal(0.12345, '@@@', locale='sv') == '0,123'
        assert numbers.format_decimal(3.14159, '@@##', locale='sv') == '3,142'
        assert numbers.format_decimal(1.23004, '@@##', locale='sv') == '1,23'
        assert numbers.format_decimal(1230.04, '@@,@@', locale='en_US') == '12,30'
        assert numbers.format_decimal(123.41, '@@##', locale='en_US') == '123.4'
        assert numbers.format_decimal(1, '@@', locale='en_US') == '1.0'
        assert numbers.format_decimal(0, '@', locale='en_US') == '0'
        assert numbers.format_decimal(0.1, '@', locale='en_US') == '0.1'
        assert numbers.format_decimal(0.1, '@#', locale='en_US') == '0.1'
        assert numbers.format_decimal(0.1, '@@', locale='en_US') == '0.10'

    def test_decimals(self):
        """Test significant digits patterns"""
        assert numbers.format_decimal(decimal.Decimal('1.2345'), '#.00', locale='en_US') == '1.23'
        assert numbers.format_decimal(decimal.Decimal('1.2345000'), '#.00', locale='en_US') == '1.23'
        assert numbers.format_decimal(decimal.Decimal('1.2345000'), '@@', locale='en_US') == '1.2'
        assert numbers.format_decimal(decimal.Decimal('12345678901234567890.12345'), '#.00', locale='en_US') == '12345678901234567890.12'

    def test_scientific_notation(self):
        assert numbers.format_scientific(0.1, '#E0', locale='en_US') == '1E-1'
        assert numbers.format_scientific(0.01, '#E0', locale='en_US') == '1E-2'
        assert numbers.format_scientific(10, '#E0', locale='en_US') == '1E1'
        assert numbers.format_scientific(1234, '0.###E0', locale='en_US') == '1.234E3'
        assert numbers.format_scientific(1234, '0.#E0', locale='en_US') == '1.2E3'
        # Exponent grouping
        assert numbers.format_scientific(12345, '##0.####E0', locale='en_US') == '1.2345E4'
        # Minimum number of int digits
        assert numbers.format_scientific(12345, '00.###E0', locale='en_US') == '12.345E3'
        assert numbers.format_scientific(-12345.6, '00.###E0', locale='en_US') == '-12.346E3'
        assert numbers.format_scientific(-0.01234, '00.###E0', locale='en_US') == '-12.34E-3'
        # Custom pattern suffix
        assert numbers.format_scientific(123.45, '#.##E0 m/s', locale='en_US') == '1.23E2 m/s'
        # Exponent patterns
        assert numbers.format_scientific(123.45, '#.##E00 m/s', locale='en_US') == '1.23E02 m/s'
        assert numbers.format_scientific(0.012345, '#.##E00 m/s', locale='en_US') == '1.23E-02 m/s'
        assert numbers.format_scientific(decimal.Decimal('12345'), '#.##E+00 m/s', locale='en_US') == '1.23E+04 m/s'
        # 0 (see ticket #99)
        assert numbers.format_scientific(0, '#E0', locale='en_US') == '0E0'

    def test_formatting_of_very_small_decimals(self):
        # previously formatting very small decimals could lead to a type error
        # because the Decimal->string conversion was too simple (see #214)
        number = decimal.Decimal("7E-7")
        assert numbers.format_decimal(number, format="@@@", locale='en_US') == '0.000000700'

    def test_nan_and_infinity(self):
        assert numbers.format_decimal(decimal.Decimal('Infinity'), locale='en_US') == '∞'
        assert numbers.format_decimal(decimal.Decimal('-Infinity'), locale='en_US') == '-∞'
        assert numbers.format_decimal(decimal.Decimal('NaN'), locale='en_US') == 'NaN'
        assert numbers.format_compact_decimal(decimal.Decimal('Infinity'), locale='en_US', format_type="short") == '∞'
        assert numbers.format_compact_decimal(decimal.Decimal('-Infinity'), locale='en_US', format_type="short") == '-∞'
        assert numbers.format_compact_decimal(decimal.Decimal('NaN'), locale='en_US', format_type="short") == 'NaN'
        assert numbers.format_currency(decimal.Decimal('Infinity'), 'USD', locale='en_US') == '$∞'
        assert numbers.format_currency(decimal.Decimal('-Infinity'), 'USD', locale='en_US') == '-$∞'

    def test_group_separator(self):
        assert numbers.format_decimal(29567.12, locale='en_US', group_separator=False) == '29567.12'
        assert numbers.format_decimal(29567.12, locale='fr_CA', group_separator=False) == '29567,12'
        assert numbers.format_decimal(29567.12, locale='pt_BR', group_separator=False) == '29567,12'
        assert numbers.format_currency(1099.98, 'USD', locale='en_US', group_separator=False) == '$1099.98'
        assert numbers.format_currency(101299.98, 'EUR', locale='fr_CA', group_separator=False) == '101299,98\xa0€'
        assert numbers.format_currency(101299.98, 'EUR', locale='en_US', group_separator=False, format_type='name') == '101299.98 euros'
        assert numbers.format_percent(251234.1234, locale='sv_SE', group_separator=False) == '25123412\xa0%'

        assert numbers.format_decimal(29567.12, locale='en_US', group_separator=True) == '29,567.12'
        assert numbers.format_decimal(29567.12, locale='fr_CA', group_separator=True) == '29\xa0567,12'
        assert numbers.format_decimal(29567.12, locale='pt_BR', group_separator=True) == '29.567,12'
        assert numbers.format_currency(1099.98, 'USD', locale='en_US', group_separator=True) == '$1,099.98'
        assert numbers.format_currency(101299.98, 'EUR', locale='fr_CA', group_separator=True) == '101\xa0299,98\xa0€'
        assert numbers.format_currency(101299.98, 'EUR', locale='en_US', group_separator=True, format_type='name') == '101,299.98 euros'
        assert numbers.format_percent(251234.1234, locale='sv_SE', group_separator=True) == '25\xa0123\xa0412\xa0%'

    def test_compact(self):
        assert numbers.format_compact_decimal(1, locale='en_US', format_type="short") == '1'
        assert numbers.format_compact_decimal(999, locale='en_US', format_type="short") == '999'
        assert numbers.format_compact_decimal(1000, locale='en_US', format_type="short") == '1K'
        assert numbers.format_compact_decimal(9000, locale='en_US', format_type="short") == '9K'
        assert numbers.format_compact_decimal(9123, locale='en_US', format_type="short", fraction_digits=2) == '9.12K'
        assert numbers.format_compact_decimal(10000, locale='en_US', format_type="short") == '10K'
        assert numbers.format_compact_decimal(10000, locale='en_US', format_type="short", fraction_digits=2) == '10K'
        assert numbers.format_compact_decimal(1000000, locale='en_US', format_type="short") == '1M'
        assert numbers.format_compact_decimal(9000999, locale='en_US', format_type="short") == '9M'
        assert numbers.format_compact_decimal(9000900099, locale='en_US', format_type="short", fraction_digits=5) == '9.0009B'
        assert numbers.format_compact_decimal(1, locale='en_US', format_type="long") == '1'
        assert numbers.format_compact_decimal(999, locale='en_US', format_type="long") == '999'
        assert numbers.format_compact_decimal(1000, locale='en_US', format_type="long") == '1 thousand'
        assert numbers.format_compact_decimal(9000, locale='en_US', format_type="long") == '9 thousand'
        assert numbers.format_compact_decimal(9000, locale='en_US', format_type="long", fraction_digits=2) == '9 thousand'
        assert numbers.format_compact_decimal(10000, locale='en_US', format_type="long") == '10 thousand'
        assert numbers.format_compact_decimal(10000, locale='en_US', format_type="long", fraction_digits=2) == '10 thousand'
        assert numbers.format_compact_decimal(1000000, locale='en_US', format_type="long") == '1 million'
        assert numbers.format_compact_decimal(9999999, locale='en_US', format_type="long") == '10 million'
        assert numbers.format_compact_decimal(9999999999, locale='en_US', format_type="long", fraction_digits=5) == '10 billion'
        assert numbers.format_compact_decimal(1, locale='ja_JP', format_type="short") == '1'
        assert numbers.format_compact_decimal(999, locale='ja_JP', format_type="short") == '999'
        assert numbers.format_compact_decimal(1000, locale='ja_JP', format_type="short") == '1000'
        assert numbers.format_compact_decimal(9123, locale='ja_JP', format_type="short") == '9123'
        assert numbers.format_compact_decimal(10000, locale='ja_JP', format_type="short") == '1万'
        assert numbers.format_compact_decimal(1234567, locale='ja_JP', format_type="long") == '123万'
        assert numbers.format_compact_decimal(-1, locale='en_US', format_type="short") == '-1'
        assert numbers.format_compact_decimal(-1234, locale='en_US', format_type="short", fraction_digits=2) == '-1.23K'
        assert numbers.format_compact_decimal(-123456789, format_type='short', locale='en_US') == '-123M'
        assert numbers.format_compact_decimal(-123456789, format_type='long', locale='en_US') == '-123 million'
        assert numbers.format_compact_decimal(2345678, locale='mk', format_type='long') == '2 милиони'
        assert numbers.format_compact_decimal(21000000, locale='mk', format_type='long') == '21 милион'
        assert numbers.format_compact_decimal(21345, locale="gv", format_type="short") == '21K'
        assert numbers.format_compact_decimal(1000, locale='it', format_type='long') == 'mille'
        assert numbers.format_compact_decimal(1234, locale='it', format_type='long') == '1 mila'
        assert numbers.format_compact_decimal(1000, locale='fr', format_type='long') == 'mille'
        assert numbers.format_compact_decimal(1234, locale='fr', format_type='long') == '1 millier'
        assert numbers.format_compact_decimal(
            12345, format_type="short", locale='ar_EG', fraction_digits=2, numbering_system='default',
        ) == '12٫34\xa0ألف'
        assert numbers.format_compact_decimal(
            12345, format_type="short", locale='ar_EG', fraction_digits=2, numbering_system='latn',
        ) == '12.34\xa0ألف'


class NumberParsingTestCase(unittest.TestCase):

    def test_can_parse_decimals(self):
        assert decimal.Decimal('1099.98') == numbers.parse_decimal('1,099.98', locale='en_US')
        assert decimal.Decimal('1099.98') == numbers.parse_decimal('1.099,98', locale='de')
        assert decimal.Decimal('1099.98') == numbers.parse_decimal('1٬099٫98', locale='ar', numbering_system="default")
        with pytest.raises(numbers.NumberFormatError):
            numbers.parse_decimal('2,109,998', locale='de')
        with pytest.raises(numbers.UnsupportedNumberingSystemError):
            numbers.parse_decimal('2,109,998', locale='de', numbering_system="unknown")

    def test_parse_decimal_strict_mode(self):
        # Numbers with a misplaced grouping symbol should be rejected
        with pytest.raises(numbers.NumberFormatError) as info:
            numbers.parse_decimal('11.11', locale='de', strict=True)
        assert info.value.suggestions == ['1.111', '11,11']
        # Numbers with two misplaced grouping symbols should be rejected
        with pytest.raises(numbers.NumberFormatError) as info:
            numbers.parse_decimal('80.00.00', locale='de', strict=True)
        assert info.value.suggestions == ['800.000']
        # Partially grouped numbers should be rejected
        with pytest.raises(numbers.NumberFormatError) as info:
            numbers.parse_decimal('2000,000', locale='en_US', strict=True)
        assert info.value.suggestions == ['2,000,000', '2,000']
        # Numbers with duplicate grouping symbols should be rejected
        with pytest.raises(numbers.NumberFormatError) as info:
            numbers.parse_decimal('0,,000', locale='en_US', strict=True)
        assert info.value.suggestions == ['0']
        # Return only suggestion for 0 on strict
        with pytest.raises(numbers.NumberFormatError) as info:
            numbers.parse_decimal('0.00', locale='de', strict=True)
        assert info.value.suggestions == ['0']
        # Properly formatted numbers should be accepted
        assert str(numbers.parse_decimal('1.001', locale='de', strict=True)) == '1001'
        # Trailing zeroes should be accepted
        assert str(numbers.parse_decimal('3.00', locale='en_US', strict=True)) == '3.00'
        # Numbers w<response clipped><NOTE>Due to the max output limit, only part of the full response has been shown to you.</NOTE>et_date_format():
    us = dates.get_date_format(locale='en_US')
    assert us.pattern == 'MMM d, y'
    de = dates.get_date_format('full', locale='de_DE')
    assert de.pattern == 'EEEE, d. MMMM y'


def test_get_datetime_format():
    assert dates.get_datetime_format(locale='en_US') == '{1}, {0}'


def test_get_time_format():
    assert dates.get_time_format(locale='en_US').pattern == 'h:mm:ss\u202fa'
    assert (dates.get_time_format('full', locale='de_DE').pattern ==
            'HH:mm:ss zzzz')


def test_get_timezone_gmt(timezone_getter):
    dt = datetime(2007, 4, 1, 15, 30)
    assert dates.get_timezone_gmt(dt, locale='en') == 'GMT+00:00'
    assert dates.get_timezone_gmt(dt, locale='en', return_z=True) == 'Z'
    assert dates.get_timezone_gmt(dt, locale='en', width='iso8601_short') == '+00'
    tz = timezone_getter('America/Los_Angeles')
    dt = _localize(tz, datetime(2007, 4, 1, 15, 30))
    assert dates.get_timezone_gmt(dt, locale='en') == 'GMT-07:00'
    assert dates.get_timezone_gmt(dt, 'short', locale='en') == '-0700'
    assert dates.get_timezone_gmt(dt, locale='en', width='iso8601_short') == '-07'
    assert dates.get_timezone_gmt(dt, 'long', locale='fr_FR') == 'UTC-07:00'


def test_get_timezone_location(timezone_getter):
    tz = timezone_getter('America/St_Johns')
    assert (dates.get_timezone_location(tz, locale='de_DE') ==
            "Kanada (St. John\u2019s) (Ortszeit)")
    assert (dates.get_timezone_location(tz, locale='en') ==
            'Canada (St. John’s) Time')
    assert (dates.get_timezone_location(tz, locale='en', return_city=True) ==
            'St. John’s')

    tz = timezone_getter('America/Mexico_City')
    assert (dates.get_timezone_location(tz, locale='de_DE') ==
            'Mexiko (Mexiko-Stadt) (Ortszeit)')

    tz = timezone_getter('Europe/Berlin')
    assert (dates.get_timezone_location(tz, locale='de_DE') ==
            'Deutschland (Berlin) (Ortszeit)')


@pytest.mark.parametrize(
    "tzname, params, expected",
    [
        ("America/Los_Angeles", {"locale": "en_US"}, "Pacific Time"),
        ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, "PT"),
        ("Europe/Berlin", {"locale": "de_DE"}, "Mitteleurop\xe4ische Zeit"),
        ("Europe/Berlin", {"locale": "pt_BR"}, "Hor\xe1rio da Europa Central"),
        ("America/St_Johns", {"locale": "de_DE"}, "Neufundland-Zeit"),
        (
            "America/Los_Angeles",
            {"locale": "en", "width": "short", "zone_variant": "generic"},
            "PT",
        ),
        (
            "America/Los_Angeles",
            {"locale": "en", "width": "short", "zone_variant": "standard"},
            "PST",
        ),
        (
            "America/Los_Angeles",
            {"locale": "en", "width": "short", "zone_variant": "daylight"},
            "PDT",
        ),
        (
            "America/Los_Angeles",
            {"locale": "en", "width": "long", "zone_variant": "generic"},
            "Pacific Time",
        ),
        (
            "America/Los_Angeles",
            {"locale": "en", "width": "long", "zone_variant": "standard"},
            "Pacific Standard Time",
        ),
        (
            "America/Los_Angeles",
            {"locale": "en", "width": "long", "zone_variant": "daylight"},
            "Pacific Daylight Time",
        ),
        ("Europe/Berlin", {"locale": "en_US"}, "Central European Time"),
    ],
)
def test_get_timezone_name_tzinfo(timezone_getter, tzname, params, expected):
    tz = timezone_getter(tzname)
    assert dates.get_timezone_name(tz, **params) == expected


@pytest.mark.parametrize("timezone_getter", ["pytz.timezone"], indirect=True)
@pytest.mark.parametrize(
    "tzname, params, expected",
    [
        ("America/Los_Angeles", {"locale": "en_US"}, "Pacific Standard Time"),
        (
            "America/Los_Angeles",
            {"locale": "en_US", "return_zone": True},
            "America/Los_Angeles",
        ),
        ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, "PST"),
    ],
)
def test_get_timezone_name_time_pytz(timezone_getter, tzname, params, expected):
    """pytz (by design) can't determine if the time is in DST or not,
    so it will always return Standard time"""
    dt = time(15, 30, tzinfo=timezone_getter(tzname))
    assert dates.get_timezone_name(dt, **params) == expected


def test_get_timezone_name_misc(timezone_getter):
    localnow = datetime.now(timezone_getter('UTC')).astimezone(dates.LOCALTZ)
    assert (dates.get_timezone_name(None, locale='en_US') ==
            dates.get_timezone_name(localnow, locale='en_US'))

    assert (dates.get_timezone_name('Europe/Berlin', locale='en_US') == "Central European Time")

    assert (dates.get_timezone_name(1400000000, locale='en_US', width='short') == "Unknown Region (UTC) Time")
    assert (dates.get_timezone_name(time(16, 20), locale='en_US', width='short') == "UTC")


def test_format_date():
    d = date(2007, 4, 1)
    assert dates.format_date(d, locale='en_US') == 'Apr 1, 2007'
    assert (dates.format_date(d, format='full', locale='de_DE') ==
            'Sonntag, 1. April 2007')
    assert (dates.format_date(d, "EEE, MMM d, ''yy", locale='en') ==
            "Sun, Apr 1, '07")


def test_format_datetime(timezone_getter):
    dt = datetime(2007, 4, 1, 15, 30)
    assert (dates.format_datetime(dt, locale='en_US') ==
            'Apr 1, 2007, 3:30:00\u202fPM')

    full = dates.format_datetime(
        dt, 'full',
        tzinfo=timezone_getter('Europe/Paris'),
        locale='fr_FR',
    )
    assert full == (
        'dimanche 1 avril 2007, 17:30:00 heure '
        'd’été d’Europe centrale'
    )
    custom = dates.format_datetime(
        dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz",
        tzinfo=timezone_getter('US/Eastern'),
        locale='en',
    )
    assert custom == '2007.04.01 AD at 11:30:00 EDT'


def test_format_time(timezone_getter):
    t = time(15, 30)
    assert dates.format_time(t, locale='en_US') == '3:30:00\u202fPM'
    assert dates.format_time(t, format='short', locale='de_DE') == '15:30'

    assert (dates.format_time(t, "hh 'o''clock' a", locale='en') ==
            "03 o'clock PM")

    paris = timezone_getter('Europe/Paris')
    eastern = timezone_getter('US/Eastern')

    t = _localize(paris, datetime(2007, 4, 1, 15, 30))
    fr = dates.format_time(t, format='full', tzinfo=paris, locale='fr_FR')
    assert fr == '15:30:00 heure d’été d’Europe centrale'

    custom = dates.format_time(t, "hh 'o''clock' a, zzzz", tzinfo=eastern, locale='en')
    assert custom == "09 o'clock AM, Eastern Daylight Time"

    with freezegun.freeze_time("2023-01-01"):
        t = time(15, 30)
        paris = dates.format_time(t, format='full', tzinfo=paris, locale='fr_FR')
        assert paris == '15:30:00 heure normale d’Europe centrale'

        us_east = dates.format_time(t, format='full', tzinfo=eastern, locale='en_US')
        assert us_east == '3:30:00\u202fPM Eastern Standard Time'


def test_format_skeleton(timezone_getter):
    dt = datetime(2007, 4, 1, 15, 30)
    assert (dates.format_skeleton('yMEd', dt, locale='en_US') == 'Sun, 4/1/2007')
    assert (dates.format_skeleton('yMEd', dt, locale='th') == 'อา. 1/4/2007')

    assert (dates.format_skeleton('EHm', dt, locale='en') == 'Sun 15:30')
    assert (dates.format_skeleton('EHm', dt, tzinfo=timezone_getter('Asia/Bangkok'), locale='th') == 'อา. 22:30 น.')


def test_format_timedelta():
    assert (dates.format_timedelta(timedelta(weeks=12), locale='en_US')
            == '3 months')
    assert (dates.format_timedelta(timedelta(seconds=1), locale='es')
            == '1 segundo')

    assert (dates.format_timedelta(timedelta(hours=3), granularity='day',
                                   locale='en_US')
            == '1 day')

    assert (dates.format_timedelta(timedelta(hours=23), threshold=0.9,
                                   locale='en_US')
            == '1 day')
    assert (dates.format_timedelta(timedelta(hours=23), threshold=1.1,
                                   locale='en_US')
            == '23 hours')


def test_parse_date():
    assert dates.parse_date('4/1/04', locale='en_US') == date(2004, 4, 1)
    assert dates.parse_date('01.04.2004', locale='de_DE') == date(2004, 4, 1)
    assert dates.parse_date('2004-04-01', locale='sv_SE', format='short') == date(2004, 4, 1)


@pytest.mark.parametrize('input, expected', [
    # base case, fully qualified time
    ('15:30:00', time(15, 30)),
    # test digits
    ('15:30', time(15, 30)),
    ('3:30', time(3, 30)),
    ('00:30', time(0, 30)),
    # test am parsing
    ('03:30 am', time(3, 30)),
    ('3:30:21 am', time(3, 30, 21)),
    ('3:30 am', time(3, 30)),
    # test pm parsing
    ('03:30 pm', time(15, 30)),
    ('03:30 pM', time(15, 30)),
    ('03:30 Pm', time(15, 30)),
    ('03:30 PM', time(15, 30)),
    # test hour-only parsing
    ('4 pm', time(16, 0)),
])
def test_parse_time(input, expected):
    assert dates.parse_time(input, locale='en_US') == expected


@pytest.mark.parametrize('case', ['', 'a', 'aaa'])
@pytest.mark.parametrize('func', [dates.parse_date, dates.parse_time])
def test_parse_errors(case, func):
    with pytest.raises(dates.ParseError):
        func(case, locale='en_US')


def test_datetime_format_get_week_number():
    format = dates.DateTimeFormat(date(2006, 1, 8), Locale.parse('de_DE'))
    assert format.get_week_number(6) == 1

    format = dates.DateTimeFormat(date(2006, 1, 8), Locale.parse('en_US'))
    assert format.get_week_number(6) == 2


def test_parse_pattern():
    assert dates.parse_pattern("MMMMd").format == '%(MMMM)s%(d)s'
    assert (dates.parse_pattern("MMM d, yyyy").format ==
            '%(MMM)s %(d)s, %(yyyy)s')
    assert (dates.parse_pattern("H:mm' Uhr 'z").format ==
            '%(H)s:%(mm)s Uhr %(z)s')
    assert dates.parse_pattern("hh' o''clock'").format == "%(hh)s o'clock"


def test_lithuanian_long_format():
    assert (
        dates.format_date(date(2015, 12, 10), locale='lt_LT', format='long') ==
        '2015 m. gruodžio 10 d.'
    )


def test_zh_TW_format():
    # Refs GitHub issue #378
    assert dates.format_time(datetime(2016, 4, 8, 12, 34, 56), locale='zh_TW') == '中午12:34:56'


def test_format_current_moment():
    frozen_instant = datetime.now(UTC)
    with freezegun.freeze_time(time_to_freeze=frozen_instant):
        assert dates.format_datetime(locale="en_US") == dates.format_datetime(frozen_instant, locale="en_US")


@pytest.mark.all_locales
def test_no_inherit_metazone_marker_never_in_output(locale, timezone_getter):
    # See: https://github.com/python-babel/babel/issues/428
    tz = timezone_getter('America/Los_Angeles')
    t = _localize(tz, datetime(2016, 1, 6, 7))
    assert NO_INHERITANCE_MARKER not in dates.format_time(t, format='long', locale=locale)
    assert NO_INHERITANCE_MARKER not in dates.get_timezone_name(t, width='short', locale=locale)


def test_no_inherit_metazone_formatting(timezone_getter):
    # See: https://github.com/python-babel/babel/issues/428
    tz = timezone_getter('America/Los_Angeles')
    t = _localize(tz, datetime(2016, 1, 6, 7))
    assert dates.format_time(t, format='long', locale='en_US') == "7:00:00\u202fAM PST"
    assert dates.format_time(t, format='long', locale='en_GB') == "07:00:00 Pacific Standard Time"
    assert dates.get_timezone_name(t, width='short', locale='en_US') == "PST"
    assert dates.get_timezone_name(t, width='short', locale='en_GB') == "Pacific Standard Time"


def test_russian_week_numbering():
    # See https://github.com/python-babel/babel/issues/485
    v = date(2017, 1, 1)
    assert dates.format_date(v, format='YYYY-ww', locale='ru_RU') == '2016-52'  # This would have returned 2017-01 prior to CLDR 32
    assert dates.format_date(v, format='YYYY-ww', locale='de_DE') == '2016-52'


def test_en_gb_first_weekday():
    assert Locale.parse('en').first_week_day == 0  # Monday in general
    assert Locale.parse('en_US').first_week_day == 6  # Sunday in the US
    assert Locale.parse('en_GB').first_week_day == 0  # Monday in the UK


def test_issue_798():
    assert dates.format_timedelta(timedelta(), format='narrow', locale='es_US') == '0s'
import datetime

from babel import dates
from babel.util import UTC

TEST_DT = datetime.datetime(2016, 1, 8, 11, 46, 15)
TEST_TIME = TEST_DT.time()
TEST_DATE = TEST_DT.date()


def test_format_interval_same_instant_1():
    assert dates.format_interval(TEST_DT, TEST_DT, "yMMMd", fuzzy=False, locale="fi") == "8. tammik. 2016"


def test_format_interval_same_instant_2():
    assert dates.format_interval(TEST_DT, TEST_DT, "xxx", fuzzy=False, locale="fi") == "8.1.2016 11.46.15"


def test_format_interval_same_instant_3():
    assert dates.format_interval(TEST_TIME, TEST_TIME, "xxx", fuzzy=False, locale="fi") == "11.46.15"


def test_format_interval_same_instant_4():
    assert dates.format_interval(TEST_DATE, TEST_DATE, "xxx", fuzzy=False, locale="fi") == "8.1.2016"


def test_format_interval_no_difference():
    t1 = TEST_DT
    t2 = t1 + datetime.timedelta(minutes=8)
    assert dates.format_interval(t1, t2, "yMd", fuzzy=False, locale="fi") == "8.1.2016"


def test_format_interval_in_tz(timezone_getter):
    t1 = TEST_DT.replace(tzinfo=UTC)
    t2 = t1 + datetime.timedelta(minutes=18)
    hki_tz = timezone_getter("Europe/Helsinki")
    assert dates.format_interval(t1, t2, "Hmv", tzinfo=hki_tz, locale="fi") == "13.46\u201314.04 aikavyöhyke: Suomi"


def test_format_interval_12_hour():
    t2 = TEST_DT
    t1 = t2 - datetime.timedelta(hours=1)
    assert dates.format_interval(t1, t2, "hm", locale="en") == "10:46\u2009\u2013\u200911:46\u202fAM"


def test_format_interval_invalid_skeleton():
    t1 = TEST_DATE
    t2 = TEST_DATE + datetime.timedelta(days=1)
    assert dates.format_interval(t1, t2, "mumumu", fuzzy=False, locale="fi") == "8.1.2016\u20139.1.2016"
    assert dates.format_interval(t1, t2, fuzzy=False, locale="fi") == "8.1.2016\u20139.1.2016"


def test_issue_825():
    assert dates.format_timedelta(
        datetime.timedelta(hours=1),
        granularity='hour',
        threshold=100,
        format='short',
        locale='pt',
    ) == '1 h'
from datetime import time

import pytest

import babel.dates as dates


@pytest.mark.parametrize("locale, time, expected_period_id", [
    ("de", time(7, 42), "morning1"),  # (from, before)
    ("de", time(3, 11), "night1"),  # (after, before)
    ("fi", time(0), "midnight"),  # (at)
    ("en_US", time(12), "noon"),  # (at)
    ("en_US", time(21), "night1"),  # (from, before) across 0:00
    ("en_US", time(5), "night1"),  # (from, before) across 0:00
    ("en_US", time(6), "morning1"),  # (from, before)
    ("agq", time(10), "am"),  # no periods defined
    ("agq", time(22), "pm"),  # no periods defined
    ("am", time(14), "afternoon1"),  # (before, after)
])
def test_day_period_rules(locale, time, expected_period_id):
    assert dates.get_period_id(time, locale=locale) == expected_period_id
[The command completed with exit code 0.]
[Current working directory: /workspace/babel]
[Python interpreter: /usr/bin/python]
[Command finished with exit code 0]