diff --git a/src/python/scripts/lon_tz.py b/src/python/scripts/lon_tz.py index 581006c..b0ac8ec 100755 --- a/src/python/scripts/lon_tz.py +++ b/src/python/scripts/lon_tz.py @@ -125,7 +125,7 @@ def _do_tzfile() -> None: def _do_lon_tz(args: dict) -> ErrStr | None: - """call timezone_solar to generate time zone from parmeters on command line""" + """call TimeZoneSolar to generate time zone from parmeters on command line""" err = None # collect parameters @@ -142,7 +142,7 @@ def _do_lon_tz(args: dict) -> ErrStr | None: case "longitude": tzs_params["use_lon_tz"] = True - # instantiate timezone_solar object and print requested field + # instantiate TimeZoneSolar object and print requested field(s) tzs = TimeZoneSolar(**tzs_params) try: get_keys = (','.join(args["get"])).split(sep=',') @@ -157,6 +157,27 @@ def _do_lon_tz(args: dict) -> ErrStr | None: return err + +def _do_named_tz(args: dict) -> ErrStr | None: + """call TimeZoneSolar to generate date for a named time zone""" + err = None + + # instantiate TimeZoneSolar object and print requested field(s) + tzname = args["tzname"] + tzs = TimeZoneSolar(tzname=tzname) + try: + get_keys = (','.join(args["get"])).split(sep=',') + for get_key in get_keys: + value = tzs.get(get_key) + if value is None: + print("") + else: + print(value) + except ValueError as tz_exc: + err = str(tz_exc) + + return err + # # command-line parsing functions # @@ -184,7 +205,7 @@ def _gen_arg_parser() -> argparse.ArgumentParser: help="turn on debugging mode", ) - # mutually-exclusive arguments: --tzfile and --longitude + # mutually-exclusive arguments: --tzfile, --tzname and --longitude excl_group = top_parser.add_mutually_exclusive_group(required=True) # --tzfile/tzdata flag triggers output of tzdata file and ends program @@ -195,14 +216,21 @@ def _gen_arg_parser() -> argparse.ArgumentParser: help="generate solar time zones tzdata text", ) + # --tzname sets a name for a specified time zone, no other parameters allowed when this is used + excl_group.add_argument( + "--tzname", + type=str, + help="name of a specific solar time zone (mutually exclusive with --tzfile & --longitude)", + ) + # --longitude sets degrees of longitude for a specified time zone excl_group.add_argument( "--longitude", type=float, - help="longitude for solar time zone (required when not using --tzfile)", + help="longitude for solar time zone (mutually exclusive with --tzfile & --tzname)", ) - # parameters for timezone_solar + # parameters for TimeZoneSolar top_parser.add_argument( "--latitude", type=float, @@ -218,7 +246,7 @@ def _gen_arg_parser() -> argparse.ArgumentParser: top_parser.add_argument( "--get", action='append', - help="specify solar time zone field to output", + help="specify solar time zone field(s) for output", ) return top_parser @@ -249,6 +277,8 @@ def main(): # call function named in argument parser settings with a dictionary of the CLI arguments if "tzfile" in args and args["tzfile"] is True: _do_tzfile() + elif "tzname" in args and args["tzname"] is not None: + err = _do_named_tz(args) else: err = _do_lon_tz(args) except Exception as exc: diff --git a/src/python/timezone_solar/timezone_solar.py b/src/python/timezone_solar/timezone_solar.py index 5aaaea8..38163cd 100644 --- a/src/python/timezone_solar/timezone_solar.py +++ b/src/python/timezone_solar/timezone_solar.py @@ -111,13 +111,43 @@ def _tz_params_latitude(cls, tz_params): # no effects on results from latitude between 80° north & south return None + # generate time zone parameters from given time zone name + @classmethod + def _tz_name2params(cls, tzname: str) -> dict: + match = re.fullmatch(r"^Lon(\d{3})([EW])$", tzname, flags=re.IGNORECASE) + if match: + is_west = match.group(2) == "W" + longitude = int(match.group(1)) * (-1 if is_west else 1) + if abs(int(longitude)) > 180: + raise ValueError(f"longitude {longitude} is out of bounds ±180") + use_lon_tz = True + return {"longitude": longitude, "use_lon_tz": use_lon_tz} + match = re.fullmatch(r"^(East|West)(\d{2})$", tzname, flags=re.IGNORECASE) + if match: + is_west = match.group(1) == "West" + hour_num = int(match.group(2)) + if hour_num > 12: + raise ValueError(f"time zone hour {hour_num} is out of bounds ±12") + longitude = hour_num * 15 * (-1 if is_west else 1) + if abs(int(longitude)) > 180: + raise ValueError(f"longitude {longitude} is out of bounds ±180") + use_lon_tz = False + return {"longitude": longitude, "use_lon_tz": use_lon_tz} + raise ValueError(f"{tzname} is not a valid solar/natural time zone name") + # get timezone parameters (name and minutes offset) - called by __new__() @classmethod - def _tz_params(cls, tz_params) -> dict: + def _tz_params(cls, tz_params: dict) -> dict: + + # rewrite parameters based on time zone if a tzname was provided + if "tzname" in tz_params and tz_params["tzname"] is not None: + tz_params = cls._tz_name2params(tz_params["tzname"]) + + # longitude is required if "longitude" not in tz_params: raise ValueError("_tz_params: longitude parameter missing") - # set time zone from longitude and latitude + # set time zone from longitude and latitude, if latitude was provided if "latitude" in tz_params and tz_params["latitude"] is not None: lat_params = cls._tz_params_latitude(tz_params) if lat_params is not None: