From e8a715fb29296872d3af966f75f5ca0de94286d6 Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+jarbasal@users.noreply.github.com> Date: Mon, 29 Mar 2021 23:43:10 +0100 Subject: [PATCH] patch/locale_switching + packaging/LN+LF_support (#6)(#7)(#27)(#62) - integration of timezone support with LN (was a dummy method until now) - lingua_franca and lingua_nostra need to be able to coexist - skills might be importing one of those libs directly - mycroft-lib will call property setters (lang/timezone) for both libs if possible - all other wrapped methods (`mycroft.util.parse` + `mycroft.util.format` + `mycroft.util.time`) will give preference to LN if it is available, use LF otherwise - this should be irrelevant for end users since LN is a drop in replacement --- mycroft/audio/__main__.py | 3 +- mycroft/client/enclosure/__main__.py | 2 + mycroft/client/speech/__main__.py | 4 +- mycroft/client/text/__main__.py | 3 +- mycroft/configuration/__init__.py | 4 +- mycroft/configuration/locale.py | 117 ++++++++++++++++++++++++++- mycroft/skills/__main__.py | 6 +- mycroft/skills/intent_service.py | 8 +- mycroft/util/format.py | 58 ++++++++----- mycroft/util/parse.py | 79 ++++++++++++++---- mycroft/util/time.py | 70 ++++++++-------- test/unittests/util/test_format.py | 9 ++- test/unittests/util/test_parse.py | 29 ++++--- test/unittests/util/test_time.py | 33 +++++--- 14 files changed, 317 insertions(+), 108 deletions(-) diff --git a/mycroft/audio/__main__.py b/mycroft/audio/__main__.py index 340137207745..be7379480641 100644 --- a/mycroft/audio/__main__.py +++ b/mycroft/audio/__main__.py @@ -24,7 +24,7 @@ ) from mycroft.util.log import LOG from mycroft.util.process_utils import ProcessStatus, StatusCallbackMap - +from mycroft.configuration import setup_locale import mycroft.audio.speech as speech from mycroft.audio.audioservice import AudioService @@ -53,6 +53,7 @@ def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping): on_stopping=stopping_hook) status = ProcessStatus('audio', bus, callbacks) + setup_locale() speech.init(bus) # Connect audio service instance to message bus diff --git a/mycroft/client/enclosure/__main__.py b/mycroft/client/enclosure/__main__.py index 0ce5b361c12b..f347ac10a91d 100644 --- a/mycroft/client/enclosure/__main__.py +++ b/mycroft/client/enclosure/__main__.py @@ -17,6 +17,7 @@ This provides any "enclosure" specific functionality, for example GUI or control over the Mark-1 Faceplate. """ +from mycroft.configuration import setup_locale from mycroft.configuration import Configuration from mycroft.util.log import LOG from mycroft.util import wait_for_exit_signal, reset_sigint_handler @@ -78,6 +79,7 @@ def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping): LOG.debug("Enclosure created") try: reset_sigint_handler() + setup_locale() enclosure.run() ready_hook() wait_for_exit_signal() diff --git a/mycroft/client/speech/__main__.py b/mycroft/client/speech/__main__.py index 8f362a1a0a73..6d6e5abb710b 100644 --- a/mycroft/client/speech/__main__.py +++ b/mycroft/client/speech/__main__.py @@ -17,7 +17,7 @@ from mycroft import dialog from mycroft.enclosure.api import EnclosureAPI from mycroft.client.speech.listener import RecognizerLoop -from mycroft.configuration import Configuration +from mycroft.configuration import Configuration, setup_locale from mycroft.identity import IdentityManager from mycroft.lock import Lock as PIDLock # Create/Support PID locking file from mycroft.messagebus.message import Message @@ -228,7 +228,7 @@ def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping, callbacks = StatusCallbackMap(on_ready=ready_hook, on_error=error_hook, on_stopping=stopping_hook) status = ProcessStatus('speech', bus, callbacks) - + setup_locale() # Register handlers on internal RecognizerLoop bus loop = RecognizerLoop(watchdog) connect_loop_events(loop) diff --git a/mycroft/client/text/__main__.py b/mycroft/client/text/__main__.py index 3e04f537b08a..4da64c8d87e3 100644 --- a/mycroft/client/text/__main__.py +++ b/mycroft/client/text/__main__.py @@ -23,7 +23,7 @@ start_log_monitor, start_mic_monitor, connect_to_mycroft, ctrl_c_handler ) -from mycroft.configuration import Configuration +from mycroft.configuration import Configuration, setup_locale sys.stdout = io.StringIO() sys.stderr = io.StringIO() @@ -54,6 +54,7 @@ def main(): start_mic_monitor(os.path.join(get_ipc_directory(), "mic_level")) connect_to_mycroft() + setup_locale() if '--simple' in sys.argv: sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ diff --git a/mycroft/configuration/__init__.py b/mycroft/configuration/__init__.py index 9f741660eadb..4a22989fb4f1 100644 --- a/mycroft/configuration/__init__.py +++ b/mycroft/configuration/__init__.py @@ -13,5 +13,7 @@ # limitations under the License. # from mycroft.configuration.config import Configuration, LocalConf, RemoteConf -from mycroft.configuration.locale import set_default_lf_lang +from mycroft.configuration.locale import set_default_lf_lang, setup_locale, \ + set_default_tz, set_default_lang, get_default_tz, get_default_lang, \ + get_config_tz, get_primary_lang_code, load_languages, load_language from mycroft.configuration.locations import SYSTEM_CONFIG, USER_CONFIG diff --git a/mycroft/configuration/locale.py b/mycroft/configuration/locale.py index 8ffc613e8f9e..366545bb1c17 100644 --- a/mycroft/configuration/locale.py +++ b/mycroft/configuration/locale.py @@ -18,10 +18,123 @@ The mycroft.util.lang module provides the main interface for setting up the lingua-franca (https://github.com/mycroftai/lingua-franca) selected language """ +from dateutil.tz import gettz, tzlocal +from mycroft.configuration.config import Configuration +# we want to support both LF and LN +# when using the mycroft wrappers this is not an issue, but skills might use +# either, so mycroft-lib needs to account for this and set the defaults for +# both libs. -from lingua_franca import set_default_lang as _set_default_lf_lang +# lingua_franca/lingua_nostra are optional and might not be installed +# exceptions should only be raised in the parse and format utils +try: + import lingua_franca as LF +except ImportError: + LF = None + +try: + import lingua_nostra as LN +except ImportError: + LN = None + +_lang = "en-us" +_tz = tzlocal() + + +def get_primary_lang_code(): + if LN: + return LN.get_primary_lang_code() + if LF: + return LF.get_primary_lang_code() + return _lang.split("-")[0] + + +def get_default_lang(): + if LN: + return LN.get_default_lang() + if LF: + return LF.get_default_lang() + return _lang + + +def set_default_lang(lang): + global _lang + _lang = lang + if LN: + LN.set_default_lang(lang) + if LF: + LF.set_default_lang(lang) + + +def get_config_tz(): + config = Configuration.get() + code = config["location"]["timezone"]["code"] + return gettz(code) + + +def get_default_tz(): + tz = None + # go with LF/LN default timezone, in case skills changed it temporarily + # for some arcane reason... + if LN: + tz = tz or LN.time.default_timezone() + if LF: + try: + tz = tz or LF.time.default_timezone() + except Exception: + # old versions of LF + # AttributeError: module 'lingua_franca' has no attribute 'time' + pass + + # use the timezone from .conf + tz = tz or get_config_tz() + + # Just go with global default + return tz or _tz + + +def set_default_tz(tz=None): + """ configure both LF and LN """ + global _tz + tz = tz or get_config_tz() or tzlocal() + _tz = tz + if LN: + LN.time.set_default_tz(tz) + if LF: + # tz added in recently, depends on version + try: + LF.time.set_default_tz(tz) + except: + pass + + +def load_languages(langs): + if LN: + LN.load_languages(langs) + if LF: + LF.load_languages(langs) + + +def load_language(lang): + if LN: + LN.load_language(lang) + if LF: + LF.load_language(lang) + + +def setup_locale(lang=None, tz=None): + lang_code = lang or Configuration.get().get("lang", "en-us") + # Load language resources, currently en-us must also be loaded at all times + load_languages([lang_code, "en-us"]) + # Set the active lang to match the configured one + set_default_lang(lang_code) + # Set the default timezone to match the configured one + set_default_tz(tz) + + +# mycroft-core backwards compat LF only interface def set_default_lf_lang(lang_code="en-us"): """Set the default language of Lingua Franca for parsing and formatting. @@ -32,4 +145,4 @@ def set_default_lf_lang(lang_code="en-us"): Args: lang (str): BCP-47 language code, e.g. "en-us" or "es-mx" """ - return _set_default_lf_lang(lang_code=lang_code) + return set_default_lang(lang_code) \ No newline at end of file diff --git a/mycroft/skills/__main__.py b/mycroft/skills/__main__.py index d2ba6250ac10..c62dc154ce29 100644 --- a/mycroft/skills/__main__.py +++ b/mycroft/skills/__main__.py @@ -22,14 +22,13 @@ from threading import Event from msm.exceptions import MsmException -from lingua_franca import load_languages import mycroft.lock from mycroft import dialog from mycroft.api import is_paired, BackendDown, DeviceApi from mycroft.audio import wait_while_speaking from mycroft.enclosure.api import EnclosureAPI -from mycroft.configuration import Configuration +from mycroft.configuration import Configuration, setup_locale from mycroft.messagebus.message import Message from mycroft.util import ( connected, @@ -199,8 +198,7 @@ def main(alive_hook=on_alive, started_hook=on_started, ready_hook=on_ready, # Create PID file, prevent multiple instances of this service mycroft.lock.Lock('skills') config = Configuration.get() - lang_code = config.get("lang", "en-us") - load_languages([lang_code, "en-us"]) + setup_locale() # Connect this process to the Mycroft message bus bus = start_message_bus_client("SKILLS") diff --git a/mycroft/skills/intent_service.py b/mycroft/skills/intent_service.py index ab2d5ac0a9a3..08e4a5975b1d 100644 --- a/mycroft/skills/intent_service.py +++ b/mycroft/skills/intent_service.py @@ -16,7 +16,7 @@ from copy import copy import time -from mycroft.configuration import Configuration, set_default_lf_lang +from mycroft.configuration import Configuration, setup_locale from mycroft.util.log import LOG from mycroft.util.parse import normalize from mycroft.metrics import report_timing, Stopwatch @@ -33,7 +33,7 @@ def _get_message_lang(message): message: message to check for language code. Returns: - The languge code from the message or the default language. + The language code from the message or the default language. """ default_lang = Configuration.get().get('lang', 'en-us') return message.data.get('lang', default_lang).lower() @@ -152,7 +152,7 @@ def get_skill_name(self, skill_id): def reset_converse(self, message): """Let skills know there was a problem with speech recognition""" lang = _get_message_lang(message) - set_default_lf_lang(lang) + setup_locale(lang) # restore default lang for skill in copy(self.active_skills): self.do_converse(None, skill[0], lang, message) @@ -273,7 +273,7 @@ def handle_utterance(self, message): """ try: lang = _get_message_lang(message) - set_default_lf_lang(lang) + setup_locale(lang) # set default lang utterances = message.data.get('utterances', []) combined = _normalize_all_utterances(utterances) diff --git a/mycroft/util/format.py b/mycroft/util/format.py index de2e52073a7d..f32f947343cb 100644 --- a/mycroft/util/format.py +++ b/mycroft/util/format.py @@ -30,24 +30,44 @@ from calendar import leapdays from enum import Enum -# These are the main functions we are using lingua franca to provide -from lingua_franca import get_default_loc -# TODO 21.08 - move nice_duration methods to Lingua Franca. -from lingua_franca.format import ( - join_list, - nice_date, - nice_date_time, - nice_number, - nice_time, - nice_year, - pronounce_number -) -# TODO 21.08 - remove import of private method _translate_word -# Consider whether the remaining items here are necessary. -from lingua_franca.format import (NUMBER_TUPLE, DateTimeFormat, - date_time_format, expand_options, - _translate_word) -from padatious.util import expand_parentheses +from mycroft.util.bracket_expansion import expand_parentheses, expand_options +from mycroft.configuration.locale import get_default_lang + + +# lingua_franca is optional, both lingua_franca and lingua_nostra are supported +# if both are installed preference is given to LN +# "setters" will be set in both lbs +# LN should be functionality equivalent to LF + +try: + try: + from lingua_nostra.format import (NUMBER_TUPLE, DateTimeFormat, + join_list, + date_time_format, expand_options, + _translate_word, + nice_number, nice_time, + pronounce_number, + nice_date, nice_date_time, nice_year) + except ImportError: + # These are the main functions we are using lingua franca to provide + from lingua_franca.format import (NUMBER_TUPLE, DateTimeFormat, + join_list, + date_time_format, expand_options, + _translate_word, + nice_number, nice_time, + pronounce_number, + nice_date, nice_date_time, nice_year) +except ImportError: + def lingua_franca_error(*args, **kwargs): + raise ImportError("lingua_franca is not installed") + + from mycroft.util.bracket_expansion import expand_options + + NUMBER_TUPLE, DateTimeFormat = None, None + + join_list = date_time_format = _translate_word = nice_number = \ + nice_time = pronounce_number = nice_date = nice_date_time = \ + nice_year = lingua_franca_error class TimeResolution(Enum): @@ -105,7 +125,7 @@ def _duration_handler(time1, lang=None, speech=True, *, time2=None, Returns: str: timespan as a string """ - lang = lang or get_default_loc() + lang = lang or get_default_lang() _leapdays = 0 _input_resolution = resolution milliseconds = 0 diff --git a/mycroft/util/parse.py b/mycroft/util/parse.py index 86692a4bbd26..cbf3f3f75204 100644 --- a/mycroft/util/parse.py +++ b/mycroft/util/parse.py @@ -26,24 +26,73 @@ do most of the actual parsing. However methods may be wrapped specifically for use in Mycroft Skills. """ - -from difflib import SequenceMatcher from warnings import warn +from difflib import SequenceMatcher -from lingua_franca import get_default_loc -from lingua_franca.parse import ( - extract_duration, - extract_number, - extract_numbers, - fuzzy_match, - get_gender, - match_one, - normalize, -) -from lingua_franca.parse import extract_datetime as _extract_datetime +# lingua_franca is optional, both lingua_franca and lingua_nostra are supported +# if both are installed preference is given to LN +# "setters" will be set in both lbs +# LN should be functionality equivalent to LF from mycroft.util.time import now_local from mycroft.util.log import LOG +from mycroft.configuration.locale import get_default_lang + +try: + try: + from lingua_nostra.parse import extract_number, extract_numbers, \ + extract_duration, get_gender, normalize + from lingua_nostra.parse import extract_datetime as lf_extract_datetime + from lingua_nostra.time import now_local + except ImportError: + from lingua_franca.parse import extract_number, extract_numbers, \ + extract_duration, get_gender, normalize + from lingua_franca.parse import extract_datetime as lf_extract_datetime + from lingua_franca.time import now_local +except ImportError: + def lingua_franca_error(*args, **kwargs): + raise ImportError("lingua_franca is not installed") + + extract_number = extract_numbers = extract_duration = get_gender = \ + normalize = lf_extract_datetime = lingua_franca_error + + +def fuzzy_match(x, against): + """Perform a 'fuzzy' comparison between two strings. + Returns: + float: match percentage -- 1.0 for perfect match, + down to 0.0 for no match at all. + """ + return SequenceMatcher(None, x, against).ratio() + + +def match_one(query, choices): + """ + Find best match from a list or dictionary given an input + + Arguments: + query: string to test + choices: list or dictionary of choices + + Returns: tuple with best match, score + """ + if isinstance(choices, dict): + _choices = list(choices.keys()) + elif isinstance(choices, list): + _choices = choices + else: + raise ValueError('a list or dict of choices must be provided') + + best = (_choices[0], fuzzy_match(query, _choices[0])) + for c in _choices[1:]: + score = fuzzy_match(query, c) + if score > best[1]: + best = (c, score) + + if isinstance(choices, dict): + return (choices[best[0]], best[1]) + else: + return best def _log_unsupported_language(language, supported_languages): @@ -116,7 +165,7 @@ def extract_datetime(text, anchorDate="DEFAULT", lang=None, "deprecated. This parameter can be omitted.")) if anchorDate is None or anchorDate == "DEFAULT": anchorDate = now_local() - return _extract_datetime(text, + return lf_extract_datetime(text, anchorDate, - lang or get_default_loc(), + lang or get_default_lang(), default_time) diff --git a/mycroft/util/time.py b/mycroft/util/time.py index 0c86e0ad8db6..019b6f05eb42 100644 --- a/mycroft/util/time.py +++ b/mycroft/util/time.py @@ -20,7 +20,24 @@ from datetime import datetime from dateutil.tz import gettz, tzlocal +# LN/LF are optional and should not be needed because of time utils +# only parse and format utils require LN/LF +# NOTE: lingua_franca has some bad UTC assumptions in date conversions, +# for that reason we do not import from there in this case + +try: + import lingua_franca as LF +except ImportError: + LF = None + +try: + import lingua_nostra as LN +except ImportError: + LN = None + + +# backwards compat import, recommend using get_default_tz instead def default_timezone(): """Get the default timezone @@ -30,19 +47,8 @@ def default_timezone(): Returns: (datetime.tzinfo): Definition of the default timezone """ - try: - # Obtain from user's configurated settings - # location.timezone.code (e.g. "America/Chicago") - # location.timezone.name (e.g. "Central Standard Time") - # location.timezone.offset (e.g. -21600000) - from mycroft.configuration import Configuration - config = Configuration.get() - code = config["location"]["timezone"]["code"] - - return gettz(code) - except Exception: - # Just go with system default timezone - return tzlocal() + from mycroft.configuration.locale import get_default_tz + return get_default_tz() def now_utc(): @@ -51,7 +57,7 @@ def now_utc(): Returns: (datetime): The current time in Universal Time, aka GMT """ - return to_utc(datetime.utcnow()) + return datetime.utcnow().replace(tzinfo=gettz("UTC")) def now_local(tz=None): @@ -63,8 +69,7 @@ def now_local(tz=None): Returns: (datetime): The current time """ - if not tz: - tz = default_timezone() + tz = tz or default_timezone() return datetime.now(tz) @@ -76,26 +81,24 @@ def to_utc(dt): Returns: (datetime): time converted to UTC """ - tzUTC = gettz("UTC") - if dt.tzinfo: - return dt.astimezone(tzUTC) - else: - return dt.replace(tzinfo=gettz("UTC")).astimezone(tzUTC) + tz = gettz("UTC") + if not dt.tzinfo: + dt = dt.replace(tzinfo=default_timezone()) + return dt.astimezone(tz) def to_local(dt): """Convert a datetime to the user's local timezone - Args: - dt (datetime): A datetime (if no timezone, defaults to UTC) - Returns: - (datetime): time converted to the local timezone - """ + Args: + dt (datetime): A datetime (if no timezone, defaults to UTC) + Returns: + (datetime): time converted to the local timezone + """ tz = default_timezone() - if dt.tzinfo: - return dt.astimezone(tz) - else: - return dt.replace(tzinfo=gettz("UTC")).astimezone(tz) + if not dt.tzinfo: + dt = dt.replace(tzinfo=default_timezone()) + return dt.astimezone(tz) def to_system(dt): @@ -107,7 +110,6 @@ def to_system(dt): (datetime): time converted to the operation system's timezone """ tz = tzlocal() - if dt.tzinfo: - return dt.astimezone(tz) - else: - return dt.replace(tzinfo=gettz("UTC")).astimezone(tz) + if not dt.tzinfo: + dt = dt.replace(tzinfo=default_timezone()) + return dt.astimezone(tz) diff --git a/test/unittests/util/test_format.py b/test/unittests/util/test_format.py index d44265d08787..3b3d920988f7 100644 --- a/test/unittests/util/test_format.py +++ b/test/unittests/util/test_format.py @@ -21,10 +21,13 @@ import pytest from pathlib import Path -from lingua_franca import load_language -from lingua_franca.internal import UnsupportedLanguageError +from mycroft.configuration.locale import load_language +try: + from lingua_nostra.internal import UnsupportedLanguageError +except: + from lingua_franca.internal import UnsupportedLanguageError -from mycroft.configuration import Configuration, set_default_lf_lang +from mycroft.configuration import set_default_lf_lang from mycroft.util.format import ( TimeResolution, nice_number, diff --git a/test/unittests/util/test_parse.py b/test/unittests/util/test_parse.py index 52bb0a3c4c34..b6723a74dcb9 100644 --- a/test/unittests/util/test_parse.py +++ b/test/unittests/util/test_parse.py @@ -16,8 +16,11 @@ import unittest from datetime import datetime, timedelta -from lingua_franca import load_language -from lingua_franca.internal import FunctionNotLocalizedError +from mycroft.configuration.locale import load_language, get_default_tz +try: + from lingua_nostra.internal import FunctionNotLocalizedError +except: + from lingua_franca.internal import FunctionNotLocalizedError from mycroft.util.parse import ( extract_datetime, @@ -549,31 +552,31 @@ def testExtract(text, expected_date, expected_leftover): "2017-06-27 19:30:00", "set alarm on weekdays") def test_extract_ambiguous_time_en(self): - morning = datetime(2017, 6, 27, 8, 1, 2) - evening = datetime(2017, 6, 27, 20, 1, 2) - noonish = datetime(2017, 6, 27, 12, 1, 2) + morning = datetime(2017, 6, 27, 8, 1, 2, tzinfo=get_default_tz()) + evening = datetime(2017, 6, 27, 20, 1, 2, tzinfo=get_default_tz()) + noonish = datetime(2017, 6, 27, 12, 1, 2, tzinfo=get_default_tz()) self.assertEqual( extract_datetime('feed fish at 10 o\'clock', morning)[0], - datetime(2017, 6, 27, 10, 0, 0)) + datetime(2017, 6, 27, 10, 0, 0, tzinfo=get_default_tz())) self.assertEqual( extract_datetime('feed fish at 10 o\'clock', noonish)[0], - datetime(2017, 6, 27, 22, 0, 0)) + datetime(2017, 6, 27, 22, 0, 0, tzinfo=get_default_tz())) self.assertEqual( extract_datetime('feed fish at 10 o\'clock', evening)[0], - datetime(2017, 6, 27, 22, 0, 0)) + datetime(2017, 6, 27, 22, 0, 0, tzinfo=get_default_tz())) def test_extract_date_with_may_I_en(self): - now = datetime(2019, 7, 4, 8, 1, 2) - may_date = datetime(2019, 5, 2, 10, 11, 20) + now = datetime(2019, 7, 4, 8, 1, 2, tzinfo=get_default_tz()) + may_date = datetime(2019, 5, 2, 10, 11, 20, tzinfo=get_default_tz()) self.assertEqual( extract_datetime('May I know what time it is tomorrow', now)[0], - datetime(2019, 7, 5, 0, 0, 0)) + datetime(2019, 7, 5, 0, 0, 0, tzinfo=get_default_tz())) self.assertEqual( extract_datetime('May I when 10 o\'clock is', now)[0], - datetime(2019, 7, 4, 10, 0, 0)) + datetime(2019, 7, 4, 10, 0, 0, tzinfo=get_default_tz())) self.assertEqual( extract_datetime('On 24th of may I want a reminder', may_date)[0], - datetime(2019, 5, 24, 0, 0, 0)) + datetime(2019, 5, 24, 0, 0, 0, tzinfo=get_default_tz())) def test_extract_relativedatetime_en(self): def extractWithFormat(text): diff --git a/test/unittests/util/test_time.py b/test/unittests/util/test_time.py index 9716f12ffcc9..e8ada088c159 100644 --- a/test/unittests/util/test_time.py +++ b/test/unittests/util/test_time.py @@ -1,7 +1,7 @@ from datetime import datetime from dateutil.tz import tzfile, tzlocal, gettz from unittest import TestCase, mock - +from mycroft.configuration import setup_locale, set_default_tz from mycroft.util.time import (default_timezone, now_local, now_utc, to_utc, to_local, to_system) @@ -20,19 +20,29 @@ @mock.patch('mycroft.configuration.Configuration') class TestTimeFuncs(TestCase): def test_default_timezone(self, mock_conf): + # Test missing tz-info + # TODO how to ensure setup_locale() not called by a previous test? + # mock_conf.get.return_value = {} + # self.assertEqual(default_timezone(), tzlocal()) + + # Test tz from config mock_conf.get.return_value = test_config + setup_locale() # will load (test) tz from config self.assertEqual(default_timezone(), tzfile('/usr/share/zoneinfo/America/Chicago')) - # Test missing tz-info - mock_conf.get.return_value = {} + + # Test changing tz + set_default_tz(tzlocal()) self.assertEqual(default_timezone(), tzlocal()) @mock.patch('mycroft.util.time.datetime') def test_now_local(self, mock_dt, mock_conf): - dt_test = datetime(year=1985, month=10, day=25, hour=8, minute=18) - mock_dt.now.return_value = dt_test mock_conf.get.return_value = test_config + setup_locale() + dt_test = datetime(year=1985, month=10, day=25, hour=8, minute=18, + tzinfo=default_timezone()) + mock_dt.now.return_value = dt_test self.assertEqual(now_local(), dt_test) expected_timezone = tzfile('/usr/share/zoneinfo/America/Chicago') @@ -44,11 +54,15 @@ def test_now_local(self, mock_dt, mock_conf): @mock.patch('mycroft.util.time.datetime') def test_now_utc(self, mock_dt, mock_conf): + mock_conf.get.return_value = test_config + setup_locale() + dt_test = datetime(year=1985, month=10, day=25, hour=8, minute=18) mock_dt.utcnow.return_value = dt_test - mock_conf.get.return_value = test_config - self.assertEqual(now_utc(), dt_test.replace(tzinfo=gettz('UTC'))) + self.assertEqual(now_utc().tzinfo, gettz('UTC')) + + self.assertEqual(now_utc(), dt_test.astimezone(gettz('UTC'))) mock_dt.utcnow.assert_called_with() def test_to_utc(self, mock_conf): @@ -64,8 +78,9 @@ def test_to_local(self, mock_conf): dt = datetime(year=2000, month=1, day=1, hour=0, minute=0, second=0, tzinfo=gettz('Europe/Stockholm')) - self.assertEqual(to_local(dt), dt) - self.assertEqual(to_local(dt).tzinfo, gettz('America/Chicago')) + self.assertEqual(to_local(dt), + dt.astimezone(default_timezone())) + self.assertEqual(to_local(dt).tzinfo, default_timezone()) def test_to_system(self, mock_conf): mock_conf.get.return_value = test_config