12
\$\begingroup\$

I'm a bit of a romantic, I love taking my wife out to see the sunrises and sunsets in the place we are located. For the sake of this exercise let's say I don't have code that can tell me the time of either sunset or sunrise for whatever date, latitude and longitude I would happen to be in.

Your task, coders, is to generate the smallest code possible that takes a decimal latitude and longitude (taken in degrees N and W, so degrees S and E will be taken as negatives) and a date in the format YYYY-MM-DD (from Jan 1, 2000 onwards) and it will spit out two times in 24hr format for the sunrise and sunset.

e.g. For today in Sydney, Australia

riseset -33.87 -151.2 2013-12-27

05:45 20:09

Bonuses: -100 if you can factor in elevation -100 if you can factor daylight savings

The code MUST spit out times in the relevant time zone specified in the input based off the latitude and longitude OR in the client machine's own time zone.

\$\endgroup\$
  • 3
    \$\begingroup\$ Wait, what, we have to do a [latitude x longitude] => [timezone] lookup? Do we get a data file for that? Or a server we can access? Or is there a language that has such stuff built in? Can you tell us which one? Or we have to memorize the timezone boundaries? To what precision? Where do we get this data? Do you realize this data will take up most of the code length? What about coordinates that fall exactly on the timezone boundary? Say, the geographic poles? Also, what behavior is allowed when the input is a polar region during a polar night / day? What about out-of-range coordinates? \$\endgroup\$ – John Dvorak Dec 26 '13 at 22:11
  • \$\begingroup\$ I would love the challenge to calculate the horizon based on a point above an idealised sphere, but I hate the associated challenge to find, hand-compress, programatically decopmress and then look up in, a timezone lookup map. Unless, of course, we can use idealized timezones (the offset is chosen so that the sun is the highest during noon, then it is rounded to the nearest hour) as well. \$\endgroup\$ – John Dvorak Dec 26 '13 at 22:23
  • 1
    \$\begingroup\$ @JanDvorak Use whatever you can, if the language you use can exploit the client's time zone then by all means do so... \$\endgroup\$ – WallyWest Dec 26 '13 at 23:20
  • 1
    \$\begingroup\$ What is the desired behavior for polar regions when it's a polar day/night? \$\endgroup\$ – John Dvorak Dec 27 '13 at 7:44
  • 1
    \$\begingroup\$ Here is a tool that does exactly the same: weatherimages.org/latlonsun.html \$\endgroup\$ – Eisa Adil Dec 29 '13 at 10:01
4
\$\begingroup\$

I've spent quite some time writing this:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from math import *


class RiseSet(object):

    __ZENITH = {'official': 90.833,
                'civil': '96',
                'nautical': '102',
                'astronomical': '108'}

    def __init__(self, day, month, year, latitude, longitude, daylight=False,
                 elevation=840, zenith='official'):
        ''' elevation is set to 840 (m) because that is the mean height of land above the sea level '''

        if abs(latitude) > 63.572375290155:
            raise ValueError('Invalid latitude: {0}.'.format(latitude))

        if zenith not in self.__ZENITH:
            raise ValueError('Invalid zenith value, must be one of {0}.'.format
                            (self.__ZENITH.keys()))

        self.day = day
        self.month = month
        self.year = year
        self.latitude = latitude
        self.longitude = longitude
        self.daylight = daylight
        self.elevation = elevation
        self.zenith = zenith

    def getZenith(self):
        return cos(radians(self.__ZENITH[self.zenith]))

    def dayOfTheYear(self):
        n0 = floor(275*self.month/9)
        n1 = floor((self.month + 9) / 12)
        n2 = (1 + floor((self.year - 4*floor(self.year/4) + 2) / 3))
        return n0 - (n1*n2) + self.day - 30

    def approxTime(self):
        sunrise = self.dayOfTheYear() + ((6 - (self.longitude/15.0)) / 24)
        sunset = self.dayOfTheYear() + ((18 - (self.longitude/15.0)) / 24)
        return (sunrise, sunset)

    def sunMeanAnomaly(self):
        sunrise = (0.9856 * self.approxTime()[0]) - 3.289
        sunset = (0.9856 * self.approxTime()[1]) - 3.289
        return (sunrise, sunset)

    def sunTrueLongitude(self):
        sma = self.sunMeanAnomaly()
        sunrise = sma[0] + (1.916*sin(radians(sma[0]))) + \
                  (0.020*sin(radians(2*sma[0]))) + 282.634

        if sunrise < 0:
            sunrise += 360
        if sunrise > 360:
            sunrise -= 360

        sunset = sma[1] + (1.916*sin(radians(sma[1]))) + \
                 (0.020*sin(radians(2*sma[1]))) + 282.634

        if sunset <= 0:
            sunset += 360
        if sunset > 360:
            sunset -= 360

        return (sunrise, sunset)

    def sunRightAscension(self):
        stl = self.sunTrueLongitude()
        sunrise = atan(radians(0.91764*tan(radians(stl[0]))))

        if sunrise <= 0:
            sunrise += 360
        if sunrise > 360:
            sunrise -= 360

        sunset = atan(radians(0.91764*tan(radians(stl[1]))))

        if sunset <= 0:
            sunset += 360
        if sunset > 360:
            sunset -= 360

        sunrise_stl_q = (floor(stl[0]/90)) * 90
        sunrise_ra_q = (floor(sunrise/90)) * 90
        sunrise = sunrise + (sunrise_stl_q - sunrise_ra_q)
        sunrise = sunrise/15.0

        sunset_stl_q = (floor(stl[1]/90)) * 90
        sunset_ra_q = (floor(sunset/90)) * 90
        sunset = sunrise + (sunset_stl_q - sunset_ra_q)
        sunset /= 15.0

        return (sunrise, sunset)

    def sunDeclination(self):
        sunrise_sin_dec = 0.39782 * sin(radians(self.sunTrueLongitude()[0]))
        sunrise_cos_dec = cos(radians(asin(radians(sunrise_sin_dec))))

        sunset_sin_dec = 0.39782 * sin(radians(self.sunTrueLongitude()[1]))
        sunset_cos_dec = cos(radians(asin(radians(sunrise_sin_dec))))

        return (sunrise_sin_dec, sunrise_cos_dec,
                sunset_sin_dec, sunset_cos_dec)

    def sunHourAngle(self):
        sd = self.sunDeclination()
        sunrise_cos_h = (cos(radians(self.getZenith())) - (sd[0]* \
                         sin(radians(self.latitude))) / (sd[1]* \
                         cos(radians(self.latitude))))
        if sunrise_cos_h > 1:
            raise Exception('The sun never rises on this location.')

        sunset_cos_h = (cos(radians(self.getZenith())) - (sd[2]* \
                         sin(radians(self.latitude))) / (sd[3]* \
                         cos(radians(self.latitude))))
        if sunset_cos_h < -1:
            raise Exception('The sun never sets on this location.')

        sunrise = 360 - acos(radians(sunrise_cos_h))
        sunrise /= 15.0

        sunset = acos(radians(sunrise_cos_h))
        sunset /= 15.0

        return (sunrise, sunset)

    def localMeanTime(self):
        sunrise = self.sunHourAngle()[0] + self.sunRightAscension()[0] - \
                 (0.06571*self.approxTime()[0]) - 6.622
        sunset = self.sunHourAngle()[1] + self.sunRightAscension()[1] - \
                 (0.06571*self.approxTime()[1]) - 6.622
        return (sunrise, sunset)

    def convertToUTC(self):
        sunrise = self.localMeanTime()[0] - (self.longitude/15.0)

        if sunrise <= 0:
            sunrise += 24
        if sunrise > 24:
            sunrise -= 24

        sunset = self.localMeanTime()[1] - (self.longitude/15.0)

        if sunset <= 0:
            sunset += 24
        if sunset > 24:
            sunset -= 24

        return (sunrise, sunset)

    def __str__(self):
        return None

Now it's not yet functional (I screwed up some calculations) - I'll come back to it later (if I'll still have the courage) to complete it / comment it.

Also, some interesting resources that I found while researching the subject:

\$\endgroup\$
  • 3
    \$\begingroup\$ I just saw your comment of # It's late, I'm tired, and OP is a prick for asking me to do this. There was no obligation to do this task... Please don't put comments like this in your code... It does not sit favorably with other coders... including me. I admire the fact that you gave it a red hot go, and the other links you've provided, but please do not use comments like this ever again... \$\endgroup\$ – WallyWest Jan 3 '14 at 10:01
  • \$\begingroup\$ @Eliseod'Annunzio You have my apologies. \$\endgroup\$ – Deneb Jan 3 '14 at 12:16
  • \$\begingroup\$ @Eliseod'Annunzio I did not intend to offend you. I would also like to thank you for giving me an absolutely fantastic idea to research and code. Now I want to turn this into a self-standing python module (with sys arguments and so on). It turns out to be a little more complicated than I previously thought, but I intend to pull this off. Thank you again. \$\endgroup\$ – Deneb Jan 3 '14 at 12:26
  • \$\begingroup\$ @Alex, do you realize this challenge is a year old? I'm pretty sure he won. \$\endgroup\$ – mbomb007 Jan 28 '15 at 20:52
  • \$\begingroup\$ @mbomb007: Didn't realize. \$\endgroup\$ – Alex A. Jan 28 '15 at 20:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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