3
\$\begingroup\$

The script calls the sunrise-sunset API to get one of the possible values, then converts it to 24-Hour format and MET time (as the times from the API are 12-hour and UTC). At the end, it joins the list to a string and prints it. I know this isn't commented very well, but are there any significant flaws in this code? I neither have any experience with Python nor with programming in general, this is just for a personal project and Python seemed to be the best way to do this, so I'm open for any advice and help.

import json
import urllib.request as request


def apiGetList(key):
    """ Function twilightApiGetList(key):
        Calls the sunrise-sunset api to return the value of a given key
        Possible keys: sunrise, sunset, solar_noon, day_length, civil_twilight_begin, civil_twilight_end,
        nautical_twilight_begin, nautical_twilight_end, astronomical_twilight_begin, astronomical_twilight_end
        All times returned are a list in 12-hour format, UTC time
    """
    with request.urlopen('https://api.sunrise-sunset.org/json?lat=44.244622&lng=-7.768863&date=today') as response:
        if response.getcode() == 200:
            source = response.read()
            data = json.loads(source)
            out = list(f"{data['results'][key]}")
            if len(out) == 10:
                out.insert(0, '0')
            else:
                pass
            return(out)
        else:
            print("Not successful")
            return


def convert12To24(lst):
    """ Function convert12To24(lst):
        Converts from 12-hour format to 24-hour format, input and output types are lists
    """
    if lst[9] == "A" and "".join(lst[0:2]) == "12":
        del lst[8:12]
        lst[0:2] = '00'
    elif lst[9] == "A":
        del lst[8:12]
    elif lst[9] == "P" and "".join(lst[0:2]) == "12":
        del lst[8:12]
    else:
        del lst[8:12]
        lst[0:2] = str(int("".join(lst[0:2])) + 12)
    return(lst)


def convertUtcToMet(time):
    """ Function convertUtcToMet(time):
        Converts from UTC time to MET time by adding two hours. Input and output time is in 24-hour format, type is a list
    """
    if int("".join(time[0:2])) < 8:
        time[0:2] = str(int("".join(time[0:2])) + 2)
        time.insert(0, '0')
    elif "".join(time[0:2]) == '22':
        time[0:2] = '00'
    elif "".join(time[0:2]) == '23':
        time[0:2] = '01'
    else:
        time[0:2] = str(int("".join(time[0:2])) + 2)
    return(time)


if __name__ == "__main__":
    ctb = apiGetList("civil_twilight_begin")
    cte = apiGetList("civil_twilight_end")

    print("".join(convertUtcToMet(convert12To24(ctb))))
    print("".join(convertUtcToMet(convert12To24(cte))))


\$\endgroup\$
5
  • \$\begingroup\$ Is MET Middle-European Time? \$\endgroup\$
    – Reinderien
    Apr 13, 2020 at 15:18
  • \$\begingroup\$ If so, MET only has a one-hour offset from UTC, not 2.... \$\endgroup\$
    – Reinderien
    Apr 13, 2020 at 15:22
  • \$\begingroup\$ Also, are you on a boat? Those coordinates are in the Atlantic Ocean north of Portugal - which is not in MET. \$\endgroup\$
    – Reinderien
    Apr 13, 2020 at 16:05
  • \$\begingroup\$ I'm guessing now, but it might be that you have an accidental negative sign in your longitude - the positive longitude would put you in Italy south of Turin, which is probably (?) in the timezone that you indicate. \$\endgroup\$
    – Reinderien
    Apr 13, 2020 at 16:16
  • \$\begingroup\$ @Reinderien Don't worry about these coordinates, I changed the real location for random numbers so that I wouldn't reveal my location! I'm actually located in Germany. Also, you are right, I didn't look up the time difference between UTC and MET because I thought I remembered it correctly, thanks for clarifying! I will work through your answer and try to understand it correctly before accepting it, but already thanks a lot for the help! \$\endgroup\$
    – Jogius
    Apr 14, 2020 at 8:12

1 Answer 1

3
\$\begingroup\$

Welcome to CodeReview! This is a great first question.

Do not do your own time math

...nor your own formatting when possible. Tell Python to do it for you. Some example code:

from dataclasses import dataclass
from datetime import timedelta, datetime, timezone
import requests, pytz


@dataclass(frozen=True)
class DayInfo:
    day_length: timedelta
    sunrise: datetime
    sunset: datetime
    solar_noon: datetime
    civil_twilight_begin: datetime
    civil_twilight_end: datetime
    nautical_twilight_begin: datetime
    nautical_twilight_end: datetime
    astronomical_twilight_begin: datetime
    astronomical_twilight_end: datetime

    @classmethod
    def get(cls, lat: float, long: float, tz: timezone) -> 'DayInfo':
        """ Function twilightApiGetList(key):
            Calls the sunrise-sunset api to return the value of a given key
            All times returned are in the provided timezone.
        """
        with requests.get(
            'https://api.sunrise-sunset.org/json',
            params={
                'lat': lat,
                'lng': long,
                'date': 'today',  # technically this can be omitted
                'formatted': 0,   # we want ISO8601 ("machine-readable")
            },
        ) as response:
            response.raise_for_status()
            data = response.json()

        if data['status'] != 'OK':
            raise requests.HTTPError(data['status'], response=response)
        results = data['results']

        return cls(
            day_length=timedelta(seconds=int(results['day_length'])),
            **{
                k: datetime.fromisoformat(results[k]).astimezone(tz)
                for k in (
                    'sunrise',
                    'sunset',
                    'solar_noon',
                    'civil_twilight_begin',
                    'civil_twilight_end',
                    'nautical_twilight_begin',
                    'nautical_twilight_end',
                    'astronomical_twilight_begin',
                    'astronomical_twilight_end',
                )
            },
        )


if __name__ == "__main__":
    day_info = DayInfo.get(44.244622, -7.768863, pytz.timezone('MET'))
    print(f'{day_info.civil_twilight_begin:%H:%m}')
    print(f'{day_info.civil_twilight_end:%H:%m}')

Notes:

  • You didn't parameterize the right things. Parameters should be coordinates and timezone.
  • Ask the API for machine-readable (ISO) dates.
  • Might as well parse everything from the API.
  • Do not use string manipulation to get your desired format. What is shown is string interpolation with standard Python time fields.
  • Use requests - it has a lot of niceties that make this easier.
  • Only issue one call to the API, returning an object that has both of the keys you care about. If you're never, ever going to use the other keys, you can delete them from this class, but they're inexpensive to generate - compared to a second HTTP call, which is much more expensive.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.