Local solar time function from UTC and longitude

后端 未结 5 1766
被撕碎了的回忆
被撕碎了的回忆 2020-12-16 17:50

I\'m wondering if there is a python function/module that calculates the local time after midnight (or local solar time) given the UTC time and longitude? It doesn\'t need to

相关标签:
5条回答
  • 2020-12-16 18:22

    I took a look at Jean Meeus' Astronomical Algorithms. I think you might be asking for local hour angle, which can be expressed in time (0-24hr), degrees (0-360) or radians (0-2pi).

    I'm guessing you can do this with ephem. But just for the heck of it, here's some python:

    #!/usr/local/bin/python
    
    import sys
    from datetime import datetime, time, timedelta
    from math import pi, sin, cos, atan2, asin
    
    # helpful constant
    DEG_TO_RAD = pi / 180
    
    # hardcode difference between Dynamical Time and Universal Time
    # delta_T = TD - UT
    # This comes from IERS Bulletin A
    # ftp://maia.usno.navy.mil/ser7/ser7.dat
    DELTA = 35.0
    
    def coords(yr, mon, day):
        # @input year (int)
        # @input month (int)
        # @input day (float)
        # @output right ascention, in radians (float)
        # @output declination, in radians (float)
    
        # get julian day (AA ch7)
        day += DELTA / 60 / 60 / 24 # use dynamical time
        if mon <= 2:
            yr -= 1
            mon += 12
        a = yr / 100
        b = 2 - a + a / 4
        jd = int(365.25 * (yr + 4716)) + int(30.6 * (mon + 1)) + day + b - 1524.5
    
        # get sidereal time at greenwich (AA ch12)
        t = (jd - 2451545.0) / 36525
    
        # Calculate mean equinox of date (degrees)
        l = 280.46646 + 36000.76983 * t + 0.0003032 * t**2
        while (l > 360):
            l -= 360
        while (l < 0):
            l += 360
    
        # Calculate mean anomoly of sun (degrees)
        m = 357.52911 + 35999.05029 * t - 0.0001537 * t**2
    
        # Calculate eccentricity of Earth's orbit
        e = 0.016708634 - 0.000042037 * t - 0.0000001267 * t**2
    
        # Calculate sun's equation of center (degrees)
        c = (1.914602 - 0.004817 * t - .000014 * t**2) * sin(m * DEG_TO_RAD) \
            + (0.019993 - .000101 * t) * sin(2 * m * DEG_TO_RAD) \
            + 0.000289 * sin(3 * m * DEG_TO_RAD)
    
        # Calculate the sun's radius vector (AU)
        o = l + c # sun's true longitude (degrees)
        v = m + c # sun's true anomoly (degrees)
    
        r = (1.000001018 * (1 - e**2)) / (1 + e * cos(v * DEG_TO_RAD))
    
        # Calculate right ascension & declination
        seconds = 21.448 - t * (46.8150 + t * (0.00059 - t * 0.001813))
        e0 = 23 + (26 + (seconds / 60)) / 60
    
        ra = atan2(cos(e0 * DEG_TO_RAD) * sin(o * DEG_TO_RAD), cos(o * DEG_TO_RAD)) # (radians)
        decl = asin(sin(e0 * DEG_TO_RAD) * sin(o * DEG_TO_RAD)) # (radians)
    
        return ra, decl
    
    def hour_angle(dt, longit):
        # @input UTC time (datetime)
        # @input longitude (float, negative west of Greenwich)
        # @output hour angle, in degrees (float)
    
        # get gregorian time including fractional day
        y = dt.year
        m = dt.month
        d = dt.day + ((dt.second / 60.0 + dt.minute) / 60 + dt.hour) / 24.0 
    
        # get right ascention
        ra, _ = coords(y, m, d)
    
        # get julian day (AA ch7)
        if m <= 2:
            y -= 1
            m += 12
        a = y / 100
        b = 2 - a + a / 4
        jd = int(365.25 * (y + 4716)) + int(30.6 * (m + 1)) + d + b - 1524.5
    
        # get sidereal time at greenwich (AA ch12)
        t = (jd - 2451545.0) / 36525
        theta = 280.46061837 + 360.98564736629 * (jd - 2451545) \
                + .000387933 * t**2 - t**3 / 38710000
    
        # hour angle (AA ch13)
        ha = (theta + longit - ra / DEG_TO_RAD) % 360
    
        return ha
    
    def main():
        if len(sys.argv) != 4:
            print 'Usage: hour_angle.py [YYYY/MM/DD] [HH:MM:SS] [longitude]'
            sys.exit()
        else:
            dt = datetime.strptime(sys.argv[1] + ' ' + sys.argv[2], '%Y/%m/%d %H:%M:%S')
            longit = float(sys.argv[3])
        ha = hour_angle(dt, longit)
        # convert hour angle to timedelta from noon
        days = ha / 360
        if days > 0.5:
            days -= 0.5
        td = timedelta(days=days)
        # make solar time
        solar_time = datetime.combine(dt.date(), time(12)) + td
        print solar_time
    
    if __name__ == '__main__':
        main()
    
    0 讨论(0)
  • 2020-12-16 18:26

    Or if you want to go even shorter, you could use NOAA's low-accuracy equations:

    #!/usr/local/bin/python
    
    import sys
    from datetime import datetime, time, timedelta
    from math import pi, cos, sin
    
    def solar_time(dt, longit):
        return ha
    
    def main():
        if len(sys.argv) != 4:
            print 'Usage: hour_angle.py [YYYY/MM/DD] [HH:MM:SS] [longitude]'
            sys.exit()
        else:
            dt = datetime.strptime(sys.argv[1] + ' ' + sys.argv[2], '%Y/%m/%d %H:%M:%S')
            longit = float(sys.argv[3])
    
        gamma = 2 * pi / 365 * (dt.timetuple().tm_yday - 1 + float(dt.hour - 12) / 24)
        eqtime = 229.18 * (0.000075 + 0.001868 * cos(gamma) - 0.032077 * sin(gamma) \
                 - 0.014615 * cos(2 * gamma) - 0.040849 * sin(2 * gamma))
        decl = 0.006918 - 0.399912 * cos(gamma) + 0.070257 * sin(gamma) \
               - 0.006758 * cos(2 * gamma) + 0.000907 * sin(2 * gamma) \
               - 0.002697 * cos(3 * gamma) + 0.00148 * sin(3 * gamma)
        time_offset = eqtime + 4 * longit
        tst = dt.hour * 60 + dt.minute + dt.second / 60 + time_offset
        solar_time = datetime.combine(dt.date(), time(0)) + timedelta(minutes=tst)
        print solar_time
    
    if __name__ == '__main__':
        main()
    
    0 讨论(0)
  • 2020-12-16 18:26

    Using ephem's sidereal_time() method:

    import ephem # pip install pyephem (on Python 2)
                 # pip install ephem   (on Python 3)
    
    def solartime(observer, sun=ephem.Sun()):
        sun.compute(observer)
        # sidereal time == ra (right ascension) is the highest point (noon)
        hour_angle = observer.sidereal_time() - sun.ra
        return ephem.hours(hour_angle + ephem.hours('12:00')).norm  # norm for 24h
    

    Note: ephem.hours is a float number that represents an angle in radians and converts to/from a string as "hh:mm:ss.ff".

    For comparison, here's the "utc + longitude" formula:

    import math
    from datetime import timedelta
    
    def ul_time(observer):
        utc_dt = observer.date.datetime()
        longitude = observer.long
        return utc_dt + timedelta(hours=longitude / math.pi * 12)
    

    Example

    from datetime import datetime
    
    # "solar time" for some other cities
    for name in ['Los Angeles', 'New York', 'London',
                 'Paris', 'Moscow', 'Beijing', 'Tokyo']:
        city = ephem.city(name)
        print("%-11s %11s %s" % (name, solartime(city),
                                 ul_time(city).strftime('%T')))
    
    # set date, longitude manually
    o = ephem.Observer()
    o.date = datetime(2012, 4, 15, 1, 0, 2) # some utc time
    o.long = '00:00:00.0' # longitude (you could also use a float (radians) here)
    print("%s %s" % (solartime(o), ul_time(o).strftime('%T')))
    

    Output

    Los Angeles 14:59:34.11 14:44:30
    New York    17:56:31.27 17:41:27
    London      22:52:02.04 22:36:58
    Paris       23:01:56.56 22:46:53
    Moscow       1:23:00.33 01:07:57
    Beijing      6:38:09.53 06:23:06
    Tokyo        8:11:17.81 07:56:15
    1:00:00.10 01:00:01
    
    0 讨论(0)
  • 2020-12-16 18:30

    For a very simple function & very very approximated local time: Time variation goes from -12h to +12h and longitude goes from -180 to 180. Then:


    import datetime as dt
    
    def localTimeApprox(myDateTime, longitude):
       """Returns local hour approximation"""
       return myDateTime+dt.timedelta(hours=(longitude*12/180))
    

    Sample calling: localTimeApprox(dt.datetime(2014, 7, 9, 20, 00, 00), -75)

    Returns: datetime.datetime(2014, 7, 9, 15, 0)

    0 讨论(0)
  • 2020-12-16 18:32

    Trying again, with ephem. I included latitude and elevation as arguments, but they are not needed of course. You can just call them 0 for your purposes.

    #!/usr/local/bin/python
    
    import sys
    from datetime import datetime, time, timedelta
    import ephem
    
    def hour_angle(dt, longit, latit, elev):
        obs = ephem.Observer()
        obs.date = dt.strftime('%Y/%m/%d %H:%M:%S')
        obs.lon = longit
        obs.lat = latit
        obs.elevation = elev
        sun = ephem.Sun()
        sun.compute(obs)
        # get right ascention
        ra = ephem.degrees(sun.g_ra) - 2 * ephem.pi
    
        # get sidereal time at greenwich (AA ch12)
        jd = ephem.julian_date(dt)
        t = (jd - 2451545.0) / 36525
        theta = 280.46061837 + 360.98564736629 * (jd - 2451545) \
                + .000387933 * t**2 - t**3 / 38710000
    
        # hour angle (AA ch13)
        ha = (theta + longit - ra * 180 / ephem.pi) % 360
        return ha
    
    def main():
        if len(sys.argv) != 6:
            print 'Usage: hour_angle.py [YYYY/MM/DD] [HH:MM:SS] [longitude] [latitude] [elev]'
            sys.exit()
        else:
            dt = datetime.strptime(sys.argv[1] + ' ' + sys.argv[2], '%Y/%m/%d %H:%M:%S')
            longit = float(sys.argv[3])
            latit = float(sys.argv[4])
            elev = float(sys.argv[5])
    
        # get hour angle
        ha = hour_angle(dt, longit, latit, elev)
    
        # convert hour angle to timedelta from noon
        days = ha / 360
        if days > 0.5:
            days -= 0.5
        td = timedelta(days=days)
    
        # make solar time
        solar_time = datetime.combine(dt.date(), time(12)) + td
        print solar_time
    
    if __name__ == '__main__':
        main()
    
    0 讨论(0)
提交回复
热议问题