diff --git a/lingua_franca/lang/parse_fa.py b/lingua_franca/lang/parse_fa.py index bda9293f..462a0362 100644 --- a/lingua_franca/lang/parse_fa.py +++ b/lingua_franca/lang/parse_fa.py @@ -25,6 +25,7 @@ import re import json from lingua_franca.internal import resolve_resource_file +from lingua_franca.time import now_local def _is_number(s): @@ -215,7 +216,7 @@ def extract_datetime_fa(text, anchorDate=None, default_time=None): if not anchorDate: - anchorDate = datetime.now() + anchorDate = now_local() today = anchorDate.replace(hour=0, minute=0, second=0, microsecond=0) today_weekday = int(anchorDate.strftime("%w")) weekday_names = [ @@ -383,11 +384,11 @@ def extract_number_fa(text, ordinals=False): return False return x[0] -class EnglishNormalizer(Normalizer): - with open(resolve_resource_file("text/en-us/normalize.json")) as f: +class FarsiNormalizer(Normalizer): + with open(resolve_resource_file("text/fa-ir/normalize.json")) as f: _default_config = json.load(f) def normalize_fa(text, remove_articles=True): - """ English string normalization """ - return EnglishNormalizer().normalize(text, remove_articles) + """ Farsi string normalization """ + return FarsiNormalizer().normalize(text, remove_articles) diff --git a/lingua_franca/lang/parse_fr.py b/lingua_franca/lang/parse_fr.py index 19561829..9728653f 100644 --- a/lingua_franca/lang/parse_fr.py +++ b/lingua_franca/lang/parse_fr.py @@ -944,8 +944,7 @@ def date_found(): if not hasYear: temp = datetime.strptime(datestr, "%B %d") if extractedDate.tzinfo: - temp = temp.replace(tzinfo=gettz("UTC")) - temp = temp.astimezone(extractedDate.tzinfo) + temp = temp.replace(tzinfo=extractedDate.tzinfo) temp = temp.replace(year=extractedDate.year) if extractedDate < temp: extractedDate = extractedDate.replace(year=int(currentYear), diff --git a/lingua_franca/time.py b/lingua_franca/time.py index 17f46d01..ab5b5782 100644 --- a/lingua_franca/time.py +++ b/lingua_franca/time.py @@ -46,7 +46,7 @@ def now_utc(): Returns: (datetime): The current time in Universal Time, aka GMT """ - return to_utc(datetime.utcnow()) + return datetime.now(gettz("UTC")) def now_local(tz=None): @@ -62,7 +62,6 @@ def now_local(tz=None): tz = default_timezone() return datetime.now(tz) - def to_utc(dt): """ Convert a datetime with timezone info to a UTC datetime @@ -71,11 +70,16 @@ def to_utc(dt): Returns: (datetime): time converted to UTC """ - tzUTC = gettz("UTC") + tz = gettz("UTC") if dt.tzinfo: - return dt.astimezone(tzUTC) + return dt.astimezone(tz) else: - return dt.replace(tzinfo=gettz("UTC")).astimezone(tzUTC) + # naive datetimes assumed to be in default timezone already! + # in the case of datetime.now this corresponds to tzlocal() + # otherwise timezone is undefined and can not be guessed, we assume + # the user means "my timezone" and that LN was configured to use it + # beforehand, if unconfigured default == tzlocal() + return dt.replace(tzinfo=default_timezone()).astimezone(tz) def to_local(dt): @@ -90,5 +94,9 @@ def to_local(dt): if dt.tzinfo: return dt.astimezone(tz) else: - return dt.replace(tzinfo=gettz("UTC")).astimezone(tz) - + # naive datetimes assumed to be in default timezone already! + # in the case of datetime.now this corresponds to tzlocal() + # otherwise timezone is undefined and can not be guessed, we assume + # the user means "my timezone" and that LN was configured to use it + # beforehand, if unconfigured default == tzlocal() + return dt.replace(tzinfo=tz) diff --git a/test/test_format.py b/test/test_format.py index 2a3800b9..d3e5ffa8 100644 --- a/test/test_format.py +++ b/test/test_format.py @@ -19,6 +19,7 @@ import ast import warnings import sys +from dateutil import tz from pathlib import Path # TODO either write a getter for lingua_franca.internal._SUPPORTED_LANGUAGES, @@ -35,7 +36,9 @@ from lingua_franca.format import pronounce_number from lingua_franca.format import date_time_format from lingua_franca.format import join_list -from lingua_franca.time import default_timezone +from lingua_franca.time import default_timezone, set_default_tz, now_local, \ + to_local + def setUpModule(): @@ -385,8 +388,34 @@ def test_ordinals(self): short_scale=False), "eighteen " "trillionth") -# def nice_time(dt, lang="en-us", speech=True, use_24hour=False, -# use_ampm=False): +class TestTimezones(unittest.TestCase): + def test_default_tz(self): + set_default_tz("America/Chicago") + + local_time = now_local() + local_tz = default_timezone() + us_time = datetime.datetime.now(tz=tz.gettz("America/Chicago")) + self.assertEqual(nice_date_time(local_time), + nice_date_time(us_time)) + self.assertEqual(local_time.tzinfo, local_tz) + + # naive datetimes assumed to be in default timezone already! + # in the case of datetime.now this corresponds to tzlocal() + # otherwise timezone is undefined and can not be guessed, we assume + # the user means "my timezone" and that LN was configured to use it + # beforehand, if unconfigured default == tzlocal() + dt = datetime.datetime(2021, 6, 23, 00, 43, 39) + dt_local = to_local(dt) + self.assertEqual(nice_time(dt), nice_time(dt_local)) + + def test_tz_conversion(self): + naive = datetime.datetime.now() + system_time = datetime.datetime.now(tz.tzlocal()) + # naive == datetime.now() == tzlocal() internally + # NOTE nice_date_time is not a localized function, it just formats + # the datetime object directly + self.assertEqual(nice_date_time(naive), + nice_date_time(system_time)) class TestNiceDateFormat(unittest.TestCase): diff --git a/test/test_parse.py b/test/test_parse.py index a494cc2f..4b2d9c4f 100644 --- a/test/test_parse.py +++ b/test/test_parse.py @@ -19,7 +19,7 @@ from lingua_franca import load_language, unload_language, set_default_lang from lingua_franca.internal import FunctionNotLocalizedError -from lingua_franca.time import default_timezone +from lingua_franca.time import default_timezone, now_local, set_default_tz from lingua_franca.parse import extract_datetime from lingua_franca.parse import extract_duration from lingua_franca.parse import extract_number, extract_numbers @@ -38,6 +38,43 @@ def setUpModule(): def tearDownModule(): unload_language('en') +class TestTimezones(unittest.TestCase): + def test_default_tz(self): + naive = datetime.now() + + # convert to default tz + set_default_tz("Europe/London") + dt = extract_datetime("tomorrow", anchorDate=naive)[0] + self.assertEqual(dt.tzinfo, tz.gettz("Europe/London")) + + set_default_tz("America/Chicago") + dt = extract_datetime("tomorrow", anchorDate=naive)[0] + self.assertEqual(dt.tzinfo, tz.gettz("America/Chicago")) + + def test_convert_to_anchorTZ(self): + naive = datetime.now() + local = now_local() + london_time = datetime.now(tz=tz.gettz("Europe/London")) + us_time = datetime.now(tz=tz.gettz("America/Chicago")) + + # convert to anchor date + dt = extract_datetime("tomorrow", anchorDate=naive)[0] + self.assertEqual(dt.tzinfo, default_timezone()) + dt = extract_datetime("tomorrow", anchorDate=local)[0] + self.assertEqual(dt.tzinfo, local.tzinfo) + dt = extract_datetime("tomorrow", anchorDate=london_time)[0] + self.assertEqual(dt.tzinfo, london_time.tzinfo) + dt = extract_datetime("tomorrow", anchorDate=us_time)[0] + self.assertEqual(dt.tzinfo, us_time.tzinfo) + + # test naive == default tz + set_default_tz("America/Chicago") + dt = extract_datetime("tomorrow", anchorDate=naive)[0] + self.assertEqual(dt.tzinfo, default_timezone()) + set_default_tz("Europe/London") + dt = extract_datetime("tomorrow", anchorDate=naive)[0] + self.assertEqual(dt.tzinfo, default_timezone()) + class TestFuzzyMatch(unittest.TestCase): def test_matches(self):