Python中的日出和日落时间

22

我目前正在开展一个项目,当涉及到光的某些活动时,该项目将通知用户。我已经完成了与光有关的部分。因此,我需要找到一种在Python中检索日出和日落时间的有效方法,因为整个脚本都是用Python编写的。我知道其他语言有几个库,但我想知道在Python中最方便的方法是什么。

我认为它将看起来很像这样:

if(sunrise<T<sunset) and (light<threshold):
    notifyUser()

我会感激任何帮助,祝你好运。


请注意,分号是多余的,更好的写法是 if (sunrise < T < sunset) and (light < threshold): - Wayne Werner
没错。我只是想给出一个我正在尝试编写伪代码的想法。感谢您的反馈! - bkaankuguoglu
6个回答

26

看看astral。这是他们文档中略微修改的示例:

>>> from astral import Astral
>>> city_name = 'London'
>>> a = Astral()
>>> a.solar_depression = 'civil'
>>> city = a[city_name]
>>> sun = city.sun(date=datetime.date(2009, 4, 22), local=True)

>>> if (sun['sunrise'] < T < sun['sunset']) and (light < threshold):
>>>    notifyUser()

如果您使用类似于这个例子的东西,请记得将提供的city_name和日期更改为city.sun

1
那么,Astral以哪种格式返回这些时间?例如,在定义了以下内容之后: now = datetime.datetime.now(); 我可以使用以下代码吗? if (sun['sunrise'] < now < sun['sunset']) and (light < threshold): notifyUser() - bkaankuguoglu
这里使用了一个预设日期。如果日期是从时间序列的DateTime索引中获取的呢? - k.ko3n

13
您也可以尝试使用 suntime。以下是其文档中的示例:
import datetime
from suntime import Sun, SunTimeException

latitude = 51.21
longitude = 21.01

sun = Sun(latitude, longitude)

# Get today's sunrise and sunset in UTC
today_sr = sun.get_sunrise_time()
today_ss = sun.get_sunset_time()
print('Today at Warsaw the sun raised at {} and get down at {} UTC'.
      format(today_sr.strftime('%H:%M'), today_ss.strftime('%H:%M')))

# On a special date in your machine's local time zone
abd = datetime.date(2014, 10, 3)
abd_sr = sun.get_local_sunrise_time(abd)
abd_ss = sun.get_local_sunset_time(abd)
print('On {} the sun at Warsaw raised at {} and get down at {}.'.
      format(abd, abd_sr.strftime('%H:%M'), abd_ss.strftime('%H:%M')))

11

我比较了几个包(suntimesuntimessunrisetastral)并比较了它们返回的日出和日落时间。所有这些包都返回本地时间,但您也可以轻松获取UTC时间。 这是结果:

from datetime import date, datetime, timezone, timedelta
import pytz
import time
from suntime import Sun, SunTimeException
from suntimes import SunTimes
import sunriset
import astral, astral.sun

latitude = 52.0691667
longitude = 19.4805556
altitude = 0
tz_poland = pytz.timezone('Europe/Warsaw')
tz_name = 'Europe/Warsaw'
for_date = date(2021, 4, 6)
print('====== suntime ======')
abd = for_date
sun = Sun(latitude, longitude)
today_sr = sun.get_sunrise_time()
today_ss = sun.get_sunset_time()
print(today_sr.astimezone(tz_poland))
print(today_ss.astimezone(tz_poland))
print('====== suntimes ======')
sun2 = SunTimes(longitude=longitude, latitude=latitude, altitude=altitude)
day = datetime(for_date.year, for_date.month, for_date.day)
print(sun2.risewhere(day, tz_name))
print(sun2.setwhere(day, tz_name))
print('====== sunriset ======')
local = datetime.now()
utc = datetime.utcnow()
local_tz = float(((local - utc).days * 86400 + round((local - utc).seconds, -1))/3600)
number_of_years = 1
start_date = for_date
df = sunriset.to_pandas(start_date, latitude, longitude, 2, number_of_years)
for index, row in df.iterrows():
    print(row['Sunrise'])
    print(row['Sunset'])
    break
print('====== astral ======')
l = astral.LocationInfo('Custom Name', 'My Region', tz_name, latitude, longitude)
s = astral.sun.sun(l.observer, date=for_date)
print(s['sunrise'].astimezone(tz_poland))
print(s['sunset'].astimezone(tz_poland))

返回值:

====== suntime ======
2021-04-06 06:05:00+02:00
2021-04-06 19:16:00+02:00
====== suntimes ======
2021-04-06 06:04:34.000553+02:00
2021-04-06 19:17:16.000613+02:00
====== sunriset ======
0 days 06:02:52.093465
0 days 19:16:49.892350
====== astral ======
2021-04-06 06:03:39.792229+02:00
2021-04-06 19:17:01.188463+02:00

请注意,只有suntimes软件包支持海拔高度。

有人想为处理多个日期时间数据添加 %%timeit 基准测试吗? - Muhammad Yasirroni
基准测试已在我的答案上完成。 - Muhammad Yasirroni

10

上面的Astral包示例似乎已经过时。

下面是针对圣何塞,加利福尼亚机场位置的Astral v2.2示例。

首先,设置观察者的位置:

from astral import LocationInfo
loc = LocationInfo(name='SJC', region='CA, USA', timezone='America/Los_Angeles',
                   latitude=37.3713439, longitude=-121.944675)
print(loc)
# LocationInfo(name='SJC', region='CA, USA', timezone='America/Los_Angeles',
#   latitude=37.3713439, longitude=-121.944675)
print(loc.observer)
# Observer(latitude=37.3713439, longitude=-121.944675, elevation=0.0)

注意:

  • 只有纬度、经度和时区是重要的。
  • 名称和地区只是标签。

接下来,计算所需日期(例如下面的2021-01-15)观察者的太阳信息:

import datetime
from astral.sun import sun
s = sun(loc.observer, date=datetime.date(2021, 1, 15), tzinfo=loc.timezone)
for key in ['dawn', 'dusk', 'noon', 'sunrise', 'sunset']:
    print(f'{key:10s}:', s[key])

输出:

dawn      : 2021-01-15 06:52:04.342105-08:00
dusk      : 2021-01-15 17:42:59.471441-08:00
noon      : 2021-01-15 12:17:05-08:00
sunrise   : 2021-01-15 07:21:04.877697-08:00
sunset    : 2021-01-15 17:13:58.467348-08:00

注:

  • 如果未指定时区,则默认为UTC。
  • 中午是太阳正午 - 太阳穿过观测者所在位置的子午线时的时间。
  • AstralNOAA公布的Excel表格的一种实现。

3

进一步改进 @Wojciech Jakubas 的 答案,这是基准(省略时区):

# boilerplate
from datetime import date, datetime, timezone, timedelta
import pytz
import time
from suntime import Sun, SunTimeException
from suntimes import SunTimes
import sunriset
import astral, astral.sun

latitude = 6.2088
longitude = 106.8456
altitude = 0
tz_name = 'Asia/Jakarta'
for_date = date(2021, 4, 6)

%%timeit
# print('====== suntime ======')
sun = Sun(latitude, longitude)
today_sr = sun.get_sunrise_time(for_date)
today_ss = sun.get_sunset_time(for_date)
# 20.3 µs ± 90.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%%timeit
# print('====== suntimes ======')
sun2 = SunTimes(longitude=longitude, latitude=latitude, altitude=altitude)
today_sr = sun2.riseutc(for_date)
today_ss = sun2.setutc(for_date)
# 83 µs ± 261 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%%timeit
# print('====== sunriset ======')
df = sunriset.to_pandas(for_date, latitude, longitude, 0, 1)
for index, row in df.iterrows():
    today_sr = row['Sunrise']
    today_ss = row['Sunset']
    break
# 28.7 ms ± 126 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
# print('====== astral ======')
l = astral.LocationInfo('Custom Name', 'My Region', tz_name, latitude, longitude)
today_sr = astral.sun.sunrise(l.observer, date=for_date)
today_ss = astral.sun.sunset(l.observer, date=for_date)
# 64.3 µs ± 292 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

要转换为本地 时区 (UTC+X),请使用:

# with known region name
tz_name = 'Asia/Jakarta'
tz = pytz.timezone(tz_name)
today_sr.astimezone(tz)

# with known time different
time_different = 7
tz = datetime.timezone(datetime.timedelta(hours=time_different))
today_sr.astimezone(tz)

结论:

  1. 使用suntime(注意它使用LGPL 许可证)。
  2. 如果您真的需要一年的数据并且不关心速度,请尝试sunriset,因为它支持一年的周期。
  3. sunriset需要手动从源代码中编辑以解决单个时间问题。
  4. sunriset输出为timedelta,因此不能直接使用.astimezone进行转换。

0

这也可以使用 skyfield 库来完成。以下是一个短示例,使用当前版本(1.39),并参考 文档 改编:

from skyfield import almanac, api

ts = api.load.timescale()
eph = api.load('de421.bsp')

# Location
bluffton = api.wgs84.latlon(+40.8939, -83.8917)

# Times between which to look for sunrises and sunsets
t0 = ts.utc(2018, 9, 12, 4)
t1 = ts.utc(2018, 9, 13, 4)

times, is_sunrise = almanac.find_discrete(t0, t1, almanac.sunrise_sunset(eph, bluffton))

time将包含t0t1之间的日出/日落时间,相应的is_sunrise值为1表示日出,0表示日落。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接