Repository: ansible/ansible-navigator (language: python)

Issue:
playbook-artifact file name with OS timezone-based time
<!-- Issues are for **concrete, actionable bugs and feature requests** only - if you're just asking for debugging help or technical support, please use:

TODO: Add ansible-navigator specific channels

We have to limit this because of limited volunteer time to respond to issues! -->

##### ISSUE TYPE

- Feature Idea

##### SUMMARY

<!-- Briefly describe the problem or desired enhancement. -->

I also want the time based on the OS time zone as a key to be included in the file name of playbook-artifact. Not just ts_utc.








Discussion / hints:

Fix patch:
diff --git a/.config/dictionary.txt b/.config/dictionary.txt
index 19ddf97b6..d0b3c3a6c 100644
--- a/.config/dictionary.txt
+++ b/.config/dictionary.txt
@@ -5,3 +5,6 @@ setuptools          # Used in _version.pyi
 Towncrier           # Changelog generator
 
 mqueue              # https://github.com/ansible/ansible-runner/issues/984
+
+
+astimezone
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d980a0edc..9acdb5209 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -202,10 +202,12 @@ repos:
           - libtmux
           - pytest
           - sphinx
+          - types-backports # Needed for checking py3.6 with a later interpreter
           - types-dataclasses # Needed for checking py3.6 with a later interpreter
           - types-docutils
-          - types-setuptools # Needed logging version
           - types-PyYAML
+          - types-setuptools # Needed for logging version
+          - types-typed-ast # Needed for checking py3.6 with a later interpreter
         args:
           - --python-version=3.6
 
diff --git a/setup.cfg b/setup.cfg
index a562e53e2..0ac097d1c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -41,6 +41,8 @@ install_requires =
     pyyaml
     importlib-resources; python_version < "3.9.0"
     dataclasses; python_version < "3.7.0"
+    backports.zoneinfo; python_version < "3.9.0"
+    tzdata
 python_requires = >=3.6.1
 
 [options.extras_require]
diff --git a/src/ansible_navigator/utils/compatibility.py b/src/ansible_navigator/utils/compatibility.py
index c3aff0e92..c3b419413 100644
--- a/src/ansible_navigator/utils/compatibility.py
+++ b/src/ansible_navigator/utils/compatibility.py
@@ -1,5 +1,7 @@
 """Conditional imports related to python versions."""
 
+# pylint: disable=unused-import
+
 import sys
 
 from typing import TYPE_CHECKING
@@ -11,3 +13,9 @@
         from typing import TypeAlias
     else:
         from typing_extensions import TypeAlias
+
+
+if sys.version_info < (3, 9):
+    from backports import zoneinfo
+else:
+    import zoneinfo
diff --git a/src/ansible_navigator/utils/functions.py b/src/ansible_navigator/utils/functions.py
index ece73b203..c1c274970 100644
--- a/src/ansible_navigator/utils/functions.py
+++ b/src/ansible_navigator/utils/functions.py
@@ -1,6 +1,7 @@
 """some utilities that are specific to ansible_navigator
 """
 import ast
+import datetime
 import decimal
 import html
 import logging
@@ -28,6 +29,8 @@
 from jinja2 import StrictUndefined
 from jinja2 import TemplateError
 
+from .compatibility import zoneinfo
+
 
 logger = logging.getLogger(__name__)
 
@@ -369,6 +372,21 @@ def human_time(seconds: Union[int, float]) -> str:
     return f"{sign_string!s}{seconds:d}s"
 
 
+def now_iso(time_zone: str) -> str:
+    """Return the current time as an ISO 8601 formatted string, given a time zone.
+
+    :params time_zone: The IANA timezone name or local.
+    :returns: The ISO 8601 formatted time zone string
+    """
+    if time_zone == "local":
+        return datetime.datetime.now(tz=datetime.timezone.utc).astimezone().isoformat()
+    try:
+        return datetime.datetime.now(tz=zoneinfo.ZoneInfo(time_zone)).isoformat()
+    except zoneinfo.ZoneInfoNotFoundError:
+        logger.error("The time zone '%s' could not be found. Using UTC.", time_zone)
+        return datetime.datetime.now(tz=datetime.timezone.utc).isoformat()
+
+
 PASCAL_REGEX = re.compile("((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))")
 
 


Test patch:
diff --git a/tests/unit/utils/test_functions.py b/tests/unit/utils/test_functions.py
index 411015c86..8af2e51ad 100644
--- a/tests/unit/utils/test_functions.py
+++ b/tests/unit/utils/test_functions.py
@@ -1,5 +1,6 @@
 """Test the functions exposed in the :mod:`~ansible_navigator.utils.functions` subpackage."""
 import os
+import re
 
 from pathlib import Path
 from typing import List
@@ -205,3 +206,48 @@ def test_path_is_relative_to():
     file_in_directory = Path("/tmp/test/file.txt")
     assert functions.path_is_relative_to(child=file_in_directory, parent=directory)
     assert not functions.path_is_relative_to(child=directory, parent=file_in_directory)
+
+
+iso8601 = re.compile(
+    r"""
+    ^
+    (?P<year>-?(?:[1-9][0-9]*)?[0-9]{4})-
+    (?P<month>1[0-2]|0[1-9])-
+    (?P<day>3[01]|0[1-9]|[12][0-9])
+    T
+    (?P<hour>2[0-3]|[01][0-9]):
+    (?P<minute>[0-5][0-9]):
+    (?P<second>[0-5][0-9])
+    (?P<ms>\.[0-9]+)?
+    (?P<timezone>Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?
+    $""",
+    re.VERBOSE,
+)
+
+
+@pytest.mark.parametrize("time_zone", ("local", "America/Los_Angeles", "UTC", "bogus"))
+def test_now_iso(caplog: pytest.LogCaptureFixture, time_zone: str):
+    """Test the using local as a time zone.
+
+    :param caplog: The log capture fixture
+    :param time_zone: The timezone
+    """
+    time_string = functions.now_iso(time_zone=time_zone)
+    re_matched = iso8601.match(time_string)
+    assert re_matched is not None
+    matched = re_matched.groupdict()
+    assert len(matched["year"]) == 4
+    assert len(matched["month"]) == 2
+    assert len(matched["day"]) == 2
+    assert len(matched["hour"]) == 2
+    assert len(matched["minute"]) == 2
+    assert len(matched["second"]) == 2
+    assert matched.get("ms", ".").startswith(".")
+    assert len(matched["timezone"]) == 6
+    if time_zone == "America/Los_Angeles":
+        assert matched["timezone"] in ("-08:00", "-07:00")
+    if time_zone == "UTC":
+        assert matched["timezone"] == "+00:00"
+    if time_zone == "bogus":
+        assert matched["timezone"] == "+00:00"
+        assert "The time zone 'bogus' could not be found. Using UTC." in caplog.text