Browse Source

feat(timezone): add support for the tz database

pull/416/head
Jérôme Deuchnord 2 months ago
parent
commit
a2208c03e6
9 changed files with 166 additions and 78 deletions
  1. +33
    -8
      kosmorro/__main__.py
  2. +2
    -0
      kosmorro/environment.py
  3. +32
    -19
      kosmorro/locales/messages.pot
  4. +11
    -0
      kosmorro/utils.py
  5. +1
    -1
      manpage/kosmorro.1.md
  6. +13
    -1
      poetry.lock
  7. +1
    -0
      pyproject.toml
  8. +10
    -6
      tests/general.py
  9. +63
    -43
      tests/timezone.py

+ 33
- 8
kosmorro/__main__.py View File

@@ -17,14 +17,18 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.


import argparse import argparse
import datetime
import sys import sys
import os.path import os.path


import pytz
from babel.dates import format_date from babel.dates import format_date
from kosmorrolib import Position, get_ephemerides, get_events, get_moon_phase from kosmorrolib import Position, get_ephemerides, get_events, get_moon_phase
from kosmorrolib.exceptions import OutOfRangeDateError from kosmorrolib.exceptions import OutOfRangeDateError
from datetime import date from datetime import date


from pytz import timezone

from . import dumper, environment, debug from . import dumper, environment, debug
from .date import parse_date from .date import parse_date
from .geolocation import get_position from .geolocation import get_position
@@ -34,6 +38,7 @@ from .utils import (
colored, colored,
set_colors_activated, set_colors_activated,
print_stderr, print_stderr,
get_timezone,
) )
from .exceptions import ( from .exceptions import (
InvalidOutputFormatError, InvalidOutputFormatError,
@@ -88,12 +93,31 @@ def run():
) )
) )


timezone = args.timezone
timezone = 0


if timezone is None and env_vars.timezone is not None:
timezone = int(env_vars.timezone)
elif timezone is None:
timezone = 0
try:
if args.timezone is not None:
timezone = get_timezone(args.timezone)
elif env_vars.tz is not None:
timezone = get_timezone(env_vars.tz)
elif env_vars.timezone is not None:
print_stderr(
colored(
_(
"Environment variable KOSMORRO_TIMEZONE is deprecated. Use TZ instead, which is more standard."
),
"yellow",
)
)
timezone = get_timezone(env_vars.timezone)
except pytz.UnknownTimeZoneError as error:
print_stderr(
colored(
_("Unknown timezone: {timezone}").format(timezone=error.args[0]),
color="red",
)
)
return -1


try: try:
use_colors = not environment.NO_COLOR and args.colors use_colors = not environment.NO_COLOR and args.colors
@@ -290,11 +314,12 @@ def get_args(output_formats: [str]):
parser.add_argument( parser.add_argument(
"--timezone", "--timezone",
"-t", "-t",
type=int,
type=str,
default=None, default=None,
help=_( help=_(
"The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). "
"Can also be set in the KOSMORRO_TIMEZONE environment variable."
"The timezone to use to display the hours. It can be either a number (e.g. 1 for UTC+1) or a timezone name (e.g. Europe/Paris). "
"See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones to find your timezone. "
"Can also be set in the TZ environment variable."
), ),
) )
parser.add_argument( parser.add_argument(


+ 2
- 0
kosmorro/environment.py View File

@@ -44,6 +44,8 @@ class Environment:
def get_env_vars() -> Environment: def get_env_vars() -> Environment:
environment = Environment() environment = Environment()


environment.tz = os.getenv("TZ")

for var in os.environ: for var in os.environ:
if not re.search("^KOSMORRO_", var): if not re.search("^KOSMORRO_", var):
continue continue


+ 32
- 19
kosmorro/locales/messages.pot View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-04-21 15:35+0200\n"
"POT-Creation-Date: 2025-09-22 17:24+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,89 +17,102 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.17.0\n" "Generated-By: Babel 2.17.0\n"


#: kosmorro/__main__.py:84
#: kosmorro/__main__.py:89
msgid "" msgid ""
"Output file will not contain the ephemerides, because you didn't provide " "Output file will not contain the ephemerides, because you didn't provide "
"the observation coordinates." "the observation coordinates."
msgstr "" msgstr ""


#: kosmorro/__main__.py:135
#: kosmorro/__main__.py:107
msgid ""
"Environment variable KOSMORRO_TIMEZONE is deprecated. Use TZ instead, "
"which is more standard."
msgstr ""

#: kosmorro/__main__.py:116
#, python-brace-format
msgid "Unknown timezone: {timezone}"
msgstr ""

#: kosmorro/__main__.py:159
#, python-brace-format #, python-brace-format
msgid "The file could not be saved in \"{path}\": {error}" msgid "The file could not be saved in \"{path}\": {error}"
msgstr "" msgstr ""


#: kosmorro/__main__.py:149
#: kosmorro/__main__.py:173
msgid "Please provide a file path to export in this format (--output)." msgid "Please provide a file path to export in this format (--output)."
msgstr "" msgstr ""


#: kosmorro/__main__.py:181
#: kosmorro/__main__.py:205
#, python-brace-format #, python-brace-format
msgid "Moon phase can only be computed between {min_date} and {max_date}" msgid "Moon phase can only be computed between {min_date} and {max_date}"
msgstr "" msgstr ""


#: kosmorro/__main__.py:228
#: kosmorro/__main__.py:252
#, python-brace-format #, python-brace-format
msgid "Running on Python {python_version} with Kosmorrolib v{kosmorrolib_version}" msgid "Running on Python {python_version} with Kosmorrolib v{kosmorrolib_version}"
msgstr "" msgstr ""


#: kosmorro/__main__.py:241
#: kosmorro/__main__.py:265
msgid "" msgid ""
"Compute the ephemerides and the events for a given date and a given " "Compute the ephemerides and the events for a given date and a given "
"position on Earth." "position on Earth."
msgstr "" msgstr ""


#: kosmorro/__main__.py:244
#: kosmorro/__main__.py:268
msgid "" msgid ""
"By default, only the events will be computed for today.\n" "By default, only the events will be computed for today.\n"
"To compute also the ephemerides, latitude and longitude arguments are " "To compute also the ephemerides, latitude and longitude arguments are "
"needed." "needed."
msgstr "" msgstr ""


#: kosmorro/__main__.py:256
#: kosmorro/__main__.py:280
msgid "Show the program version" msgid "Show the program version"
msgstr "" msgstr ""


#: kosmorro/__main__.py:265
#: kosmorro/__main__.py:289
msgid "" msgid ""
"The format to output the information to. If not provided, the output " "The format to output the information to. If not provided, the output "
"format will be inferred from the file extension of the output file." "format will be inferred from the file extension of the output file."
msgstr "" msgstr ""


#: kosmorro/__main__.py:275
#: kosmorro/__main__.py:299
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"The observer's position on Earth, in the \"{latitude},{longitude}\" " "The observer's position on Earth, in the \"{latitude},{longitude}\" "
"format. Can also be set in the KOSMORRO_POSITION environment variable." "format. Can also be set in the KOSMORRO_POSITION environment variable."
msgstr "" msgstr ""


#: kosmorro/__main__.py:285
#: kosmorro/__main__.py:309
msgid "" msgid ""
"The date for which the ephemerides must be calculated. Can be in the " "The date for which the ephemerides must be calculated. Can be in the "
"YYYY-MM-DD format or an interval in the \"[+-]YyMmDd\" format (with Y, M," "YYYY-MM-DD format or an interval in the \"[+-]YyMmDd\" format (with Y, M,"
" and D numbers). Defaults to current date." " and D numbers). Defaults to current date."
msgstr "" msgstr ""


#: kosmorro/__main__.py:296
#: kosmorro/__main__.py:320
msgid "" msgid ""
"The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). "
"Can also be set in the KOSMORRO_TIMEZONE environment variable."
"The timezone to use to display the hours. It can be either a number (e.g."
" 1 for UTC+1) or a timezone name (e.g. Europe/Paris). See "
"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones to find your"
" timezone. Can also be set in the TZ environment variable."
msgstr "" msgstr ""


#: kosmorro/__main__.py:304
#: kosmorro/__main__.py:329
msgid "Disable the colors in the console." msgid "Disable the colors in the console."
msgstr "" msgstr ""


#: kosmorro/__main__.py:312
#: kosmorro/__main__.py:337
msgid "A file to export the output to. If not given, the standard output is used." msgid "A file to export the output to. If not given, the standard output is used."
msgstr "" msgstr ""


#: kosmorro/__main__.py:320
#: kosmorro/__main__.py:345
msgid "" msgid ""
"Do not generate a graph to represent the rise and set times in the LaTeX " "Do not generate a graph to represent the rise and set times in the LaTeX "
"file." "file."
msgstr "" msgstr ""


#: kosmorro/__main__.py:327
#: kosmorro/__main__.py:352
msgid "Show debugging messages" msgid "Show debugging messages"
msgstr "" msgstr ""




+ 11
- 0
kosmorro/utils.py View File

@@ -1,5 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from datetime import datetime


import pytz
from termcolor import colored as do_color from termcolor import colored as do_color
from sys import stderr from sys import stderr


@@ -27,3 +29,12 @@ def colored(text, color=None, on_color=None, attrs=None):


def print_stderr(*values: object): def print_stderr(*values: object):
print(*values, file=stderr) print(*values, file=stderr)


def get_timezone(value: int | str) -> float:
try:
timezone = float(value)
except ValueError:
timezone = pytz.timezone(value).utcoffset(datetime.now()).total_seconds() / 3600

return timezone

+ 1
- 1
manpage/kosmorro.1.md View File

@@ -23,7 +23,7 @@
The date for which the ephemerides must be computed, either in the YYYY-MM-DD format or as an interval in the "[+-]YyMmDd" format (with Y, M, and D numbers); defaults to the current date The date for which the ephemerides must be computed, either in the YYYY-MM-DD format or as an interval in the "[+-]YyMmDd" format (with Y, M, and D numbers); defaults to the current date


`--timezone=`_TIMEZONE_, `-t` _TIMEZONE_ `--timezone=`_TIMEZONE_, `-t` _TIMEZONE_
the timezone to display the hours in; e.g. 2 for UTC+2 or -3 for UTC-3
the timezone to use to display the hours; it can be either a number (e.g. 1 for UTC+1) or a timezone name (e.g. Europe/Paris)


`--no-colors` `--no-colors`
disable the colors in the console disable the colors in the console


+ 13
- 1
poetry.lock View File

@@ -391,6 +391,18 @@ files = [
[package.dependencies] [package.dependencies]
six = ">=1.5" six = ">=1.5"


[[package]]
name = "pytz"
version = "2025.2"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"},
{file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"},
]

[[package]] [[package]]
name = "sgp4" name = "sgp4"
version = "2.25" version = "2.25"
@@ -529,4 +541,4 @@ tests = ["pytest", "pytest-cov"]
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "edfb347df82f9895ce51f3af9005b1d924129c24aa7e38b5e2f7bd858e33ec25"
content-hash = "12bf9cdda9cfaf9616bf3d899450e105991f788267601a41a79fed1b5cbd73a9"

+ 1
- 0
pyproject.toml View File

@@ -22,6 +22,7 @@ kosmorrolib = "^1.0"
python-dateutil = "^2.8" python-dateutil = "^2.8"
Babel = "^2.9" Babel = "^2.9"
openlocationcode = "^1.0" openlocationcode = "^1.0"
pytz = "^2025.2"


[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
black = "^24.4" black = "^24.4"


+ 10
- 6
tests/general.py View File

@@ -62,9 +62,11 @@ options:
"[+-]YyMmDd" format (with Y, M, and D numbers). "[+-]YyMmDd" format (with Y, M, and D numbers).
Defaults to current date. Defaults to current date.
--timezone TIMEZONE, -t TIMEZONE --timezone TIMEZONE, -t TIMEZONE
The timezone to display the hours in (e.g. 2 for UTC+2
or -3 for UTC-3). Can also be set in the
KOSMORRO_TIMEZONE environment variable.
The timezone to use to display the hours. It can be
either a number (e.g. 1 for UTC+1) or a timezone name
(e.g. Europe/Paris). See https://en.wikipedia.org/wiki
/List_of_tz_database_time_zones to find your timezone.
Can also be set in the TZ environment variable.
--no-colors Disable the colors in the console. --no-colors Disable the colors in the console.
--output OUTPUT, -o OUTPUT --output OUTPUT, -o OUTPUT
A file to export the output to. If not given, the A file to export the output to. If not given, the
@@ -103,9 +105,11 @@ options:
"[+-]YyMmDd" format (with Y, M, and D numbers). "[+-]YyMmDd" format (with Y, M, and D numbers).
Defaults to current date. Defaults to current date.
--timezone, -t TIMEZONE --timezone, -t TIMEZONE
The timezone to display the hours in (e.g. 2 for UTC+2
or -3 for UTC-3). Can also be set in the
KOSMORRO_TIMEZONE environment variable.
The timezone to use to display the hours. It can be
either a number (e.g. 1 for UTC+1) or a timezone name
(e.g. Europe/Paris). See https://en.wikipedia.org/wiki
/List_of_tz_database_time_zones to find your timezone.
Can also be set in the TZ environment variable.
--no-colors Disable the colors in the console. --no-colors Disable the colors in the console.
--output, -o OUTPUT A file to export the output to. If not given, the --output, -o OUTPUT A file to export the output to. If not given, the
standard output is used. standard output is used.


+ 63
- 43
tests/timezone.py View File

@@ -6,68 +6,88 @@ from .utils import (
) )




def check_command_return_t_plus_one(result):
def test_timezone_with_command_line_arg():
result = execute(KOSMORRO + ["--timezone=1", "-d2020-01-27"])
assert result.successful assert result.successful
assert (
result.stdout
== """Monday, January 27, 2020
assert "Note: All the hours are given in the UTC+1.0 timezone." in result.stdout


New Moon
First Quarter on Sunday, February 2, 2020 at 2:41 AM
result = execute(KOSMORRO + ["--timezone=Europe/Paris", "-d2020-01-27"])
assert result.successful
assert "Note: All the hours are given in the UTC+1.0 timezone." not in result.stdout


Expected events:
9:00 PM Venus and Neptune are in conjunction
result = execute(KOSMORRO + ["--timezone=-5", "-d2020-01-27"])
assert result.successful
assert "Note: All the hours are given in the UTC-5.0 timezone." in result.stdout


Note: All the hours are given in the UTC+1 timezone.
"""
)
result = execute(KOSMORRO + ["--timezone=America/Chicago", "-d2020-01-27"])
assert result.successful
assert "Note: All the hours are given in the UTC-5.0 timezone." in result.stdout




def check_command_return_t_minus_one(result):
def test_timezone_with_env_var():
result = execute(KOSMORRO + ["-d2020-01-27"], environment={"TZ": "1"})
assert result.successful assert result.successful
assert (
result.stdout
== """Monday, January 27, 2020
assert "Note: All the hours are given in the UTC+1.0 timezone." in result.stdout


New Moon
First Quarter on Sunday, February 2, 2020 at 12:41 AM
result = execute(KOSMORRO + ["-d2020-01-27"], environment={"TZ": "Europe/Paris"})
assert result.successful
assert "Note: All the hours are given in the UTC+1.0 timezone." not in result.stdout

result = execute(KOSMORRO + ["-d2020-01-27"], environment={"TZ": "-5"})
assert result.successful
assert "Note: All the hours are given in the UTC-5.0 timezone." in result.stdout

result = execute(KOSMORRO + ["-d2020-01-27"], environment={"TZ": "America/Chicago"})
assert result.successful
assert "Note: All the hours are given in the UTC-5.0 timezone." in result.stdout


Expected events:
7:00 PM Venus and Neptune are in conjunction


Note: All the hours are given in the UTC-1 timezone.
"""
def test_timezone_with_env_var_and_command_line_arg():
result = execute(
KOSMORRO + ["--timezone=3", "-d2020-01-27"], environment={"TZ": "Europe/Paris"}
) )
assert result.successful
assert "Note: All the hours are given in the UTC+3.0 timezone." in result.stdout




def test_timezone():
check_command_return_t_plus_one(
execute(KOSMORRO + ["--timezone=1", "-d2020-01-27"])
def test_timezone_with_deprecated_env_var():
result = execute(
KOSMORRO + ["-d2020-01-27"], environment={"KOSMORRO_TIMEZONE": "1"}
) )
check_command_return_t_minus_one(
execute(KOSMORRO + ["--timezone=-1", "-d2020-01-27"])
assert result.successful
assert (
"Environment variable KOSMORRO_TIMEZONE is deprecated. Use TZ instead, which is more standard."
in result.stderr
) )
assert "Note: All the hours are given in the UTC+1.0 timezone." in result.stdout



def test_timezone_with_env_var():
check_command_return_t_plus_one(
execute(KOSMORRO + ["-d2020-01-27"], environment={"KOSMORRO_TIMEZONE": "1"})
result = execute(
KOSMORRO + ["-d2020-01-27"], environment={"KOSMORRO_TIMEZONE": "Europe/Paris"}
) )
check_command_return_t_minus_one(
execute(KOSMORRO + ["-d2020-01-27"], environment={"KOSMORRO_TIMEZONE": "-1"})
assert result.successful
assert (
"Environment variable KOSMORRO_TIMEZONE is deprecated. Use TZ instead, which is more standard."
in result.stderr
) )
assert "Note: All the hours are given in the UTC+1.0 timezone." not in result.stdout


# If both environment variable and argument are set, use argument:
result = execute(
KOSMORRO + ["-d2020-01-27"], environment={"KOSMORRO_TIMEZONE": "-5"}
)
assert result.successful
assert (
"Environment variable KOSMORRO_TIMEZONE is deprecated. Use TZ instead, which is more standard."
in result.stderr
)
assert "Note: All the hours are given in the UTC-5.0 timezone." in result.stdout


check_command_return_t_plus_one(
execute(
KOSMORRO + ["--timezone=1", "-d2020-01-27"],
environment={"KOSMORRO_TIMEZONE": "-1"},
)
result = execute(
KOSMORRO + ["-d2020-01-27"],
environment={"KOSMORRO_TIMEZONE": "America/Chicago"},
) )
check_command_return_t_minus_one(
execute(
KOSMORRO + ["--timezone=-1", "-d2020-01-27"],
environment={"KOSMORRO_TIMEZONE": "1"},
)
assert result.successful
assert (
"Environment variable KOSMORRO_TIMEZONE is deprecated. Use TZ instead, which is more standard."
in result.stderr
) )
assert "Note: All the hours are given in the UTC-5.0 timezone." in result.stdout

Loading…
Cancel
Save