pull/7866/merge
Lorenzo Cappelletti 2024-03-16 08:42:57 +08:00 zatwierdzone przez GitHub
commit c00de795e4
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
3 zmienionych plików z 1043 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,716 @@
:mod:`datetime` -- basic date and time types
============================================
.. module:: datetime
:synopsis: basic date and time types
|see_cpython_module| :mod:`python:datetime`.
This module supplies classes for manipulating dates, times, and deltas.
:class:`datetime` objects may be categorized as “aware” or “naive”
depending on whether or not they include timezone information.
An aware object can locate itself relative to other aware objects. An
*aware* object represents a specific moment in time that is not open to
interpretation.
A *naive* object does not contain enough information to unambiguously
locate itself relative to other :class:`datetime` objects. Whether a naive
object represents Coordinated Universal Time (UTC), local time, or time
in some other timezone is purely up to the program, just like it is up
to the program whether a particular number represents metres, miles, or
mass. Naive objects are easy to understand and to work with, at the cost
of ignoring some aspects of reality.
For applications requiring aware objects, :class:`datetime` objects have
an optional time zone information attribute, *tzinfo*, that can be set to
an instance of a :class:`timezone` class. These objects capture
information about the offset from UTC time and the time zone name.
The following classes are provided:
* :class:`timedelta`
* :class:`timezone`
* :class:`datetime`
Python's classes `date()
<https://docs.python.org/3/library/datetime.html#date-objects>`_ and `time()
<https://docs.python.org/3/library/datetime.html#time-objects>`_
are mimicked with a class :class:`datetime` set to midnight, and a class
:class:`timedelta` in the range [0-24h).
:class:`timedelta` Objects
--------------------------
A :class:`timedelta` object represents a duration, the difference between two
dates or times. With respect to the Python module
`datetime <https://docs.python.org/3/library/datetime.html>`_,
this implementation is constrained as follows:
* Minimum resolution is *1 nanosecond*, instead of *1 microsecond*.
* Masimum delta spans over ±106751 days (±2\ :sup:`63` nanoseconds or
±292 years) instead of ±999999999 days.
Class attributes
^^^^^^^^^^^^^^^^
.. attribute:: timedelta.MINYEAR
The year of :attr:`timedelta.min`, i.e.
``timedelta.min.tuple()[1] // (365 * 24 * 60 * 60 * 10**9) == -292``.
.. attribute:: timedelta.MAXYEAR
The year of :attr:`timedelta.max`, i.e.
``timedelta.max.tuple()[1] // (365 * 24 * 60 * 60 * 10**9) == 292``.
.. attribute:: timedelta.min
The most negative :class:`timedelta` object,
``timedelta(nanoseconds=-2**63)``.
.. attribute:: timedelta.max
The most positive :class:`timedelta` object,
``timedelta(nanoseconds=2**63 - 1)``.
.. attribute:: timedelta.resolution
The smallest possible difference between non-equal :class:`timedelta`
objects, ``timedelta(nanoseconds=1)``.
.. attribute:: timedelta.nanoseconds
The internal time delta representation as 64-bit integer.
Class methods
^^^^^^^^^^^^^
.. class:: timedelta(hours=0, minutes=0, seconds=0, days=0, weeks=0,
milliseconds=0, microseconds=0, nanoseconds=0)
All arguments are optional and default to ``0``. Arguments may be integers
or floats, and may be positive or negative. Only nanoseconds are stored
internally. Arguments are converted to those units:
* A microsecond is converted to 1000 nanoseconds.
* A millisecond is converted to 10\ :sup:`6` nanoseconds.
* A minute is converted to 60 seconds.
* An hour is converted to 3600 seconds.
* A week is converted to 7 days.
If no argument is a float, the conversion and normalization processes are
exact (no information is lost).
.. method:: timedelta.total_seconds()
Return a float representing the total number of seconds contained in the duration.
.. method:: timedelta.__add__(other)
Return a :class:`timedelta` which represents the sum of two durations.
.. method:: timedelta.__sub__(other)
Return a :class:`timedelta` which represents the difference between
two durations.
.. method:: timedelta.__mul__(other)
Return a delta multiplied by an integer or float. The result is rounded to
the nearest nanosecond using round-half-to-even.
.. method:: timedelta.__truediv__(other)
When *other* is a float or an integer, returns a delta divided by *other*.
The result is rounded to the nearest multiple of timedelta.resolution
using round-half-to-even.
When *other* is a delta, division of overall duration by interval unit
*other*. Returns a float object.
.. method:: timedelta.__floordiv__(other)
The floor is computed and the remainder (if any) is thrown away. When
*other* is a delta, an integer is returned.
.. method:: timedelta.__mod__(other)
The remainder is computed as a :class:`timedelta` object.
.. method:: timedelta.__divmod__(other)
Computes the quotient and the remainder: ``q = td1.__floordiv__(td2)`` and
``r = td1.__mod__(td2)``. ``q`` is an integer and ``r`` is a :class:`timedelta`
object.
.. method:: timedelta.__neg__()
Equivalent to ``td1.__mul__(-1)``.
.. method:: timedelta.__eq__(other)
Equivalent to ``td1.total_seconds() == td2.total_seconds()``.
.. method:: timedelta.__le__(other)
Equivalent to ``td1.total_seconds() <= td2.total_seconds()``.
.. method:: timedelta.__lt__(other)
Equivalent to ``td1.total_seconds() < td2.total_seconds()``.
.. method:: timedelta.__ge__(other)
Equivalent to ``td1.total_seconds() >= td2.total_seconds()``.
.. method:: timedelta.__gt__(other)
Equivalent to ``td1.total_seconds() > td2.total_seconds()``.
.. method:: timedelta.__bool__()
Return ``False`` when duration is ``0``.
.. method:: timedelta.__abs__()
Return a positive delta.
.. method:: timedelta.isoformat()
This method mimics Python's `isoformat()
<https://docs.python.org/3/library/datetime.html#datetime.time.isoformat>`_
for *time* objects by returning a string in the format ``HH:MM:SS``, where
``HH``, ``MM``, and ``SS`` are two digits of the time delta's hours,
minutes and seconds, respectively, since midnight. This is only if value
is within the range [0-24h).
For other values, it returns the format ``±Dd HH:MM:SS``, where ``±`` is
the sign of the delta and ``D`` its number of days. This is *not* ISO
compliant, but provides a complete representation.
If the fractional part of :meth:`timedelta.total_seconds()` is not 0,
``.ffffff`` is appended.
.. method:: timedelta.tuple(sign_pos='')
Return the tuple ``(sign, days, hours, minutes, seconds, nanoseconds)``,
where ``sign`` is ``-`` if delta is negative, *sign_pos* otherwise.
Examples of usage
^^^^^^^^^^^^^^^^^
An example of normalization::
import datetime.timedelta
# Components of another_year add up to exactly 365 days
year = timedelta(days=365)
another_year = timedelta(weeks=40, days=84, hours=23, minutes=50, seconds=600)
print(year == another_year) # True
print(year.total_seconds()) # 31536000.0
Examples of timedelta arithmetic::
import datetime.timedelta
year = timedelta(days=365)
ten_years = year * 10
print(ten_years) # 3650d 00:00:00
nine_years = ten_years - year
print(nine_years) # 3285d 00:00:00
three_years = nine_years // 3
print(three_years) # 1095d 00:00:00
:class:`timezone` Objects
-------------------------
The :class:`timezone` class represents a timezone defined by a fixed
offset from UTC. Define a subclass of :class:`timezone` to capture
information about a particular time zone.
An instance of :class:`timezone` can be passed to the constructors for
:class:`datetime`. The latter objects view their attributes as being in
local time, and the :class:`timezone` object supports methods revealing
offset of local time from UTC, the name of the time zone, and DST offset,
all relative to a date-time object passed to them.
Methods to customize
^^^^^^^^^^^^^^^^^^^^
A subclass of :class:`timezone` may need to override the following methods.
Exactly which methods are needed depends on the uses made of aware
:class:`datetime` objects. If in doubt, simply implement all of them.
.. method:: timezone.utcoffset(dt)
Return offset of local time from UTC, as a :class:`timedelta` object
that is positive east of UTC. If local time is west of UTC, this should
be negative.
This represents the *total* offset from UTC; for example, if a
:class:`timezone` object represents both time zone and DST adjustments,
:meth:`timezone.utcoffset` should return their sum. If the UTC offset
isnt known, return ``None``. Else the value returned must be a
:class:`timedelta` object strictly between ``timedelta(hours=-24)`` and
``timedelta(hours=24)`` (the magnitude of the offset must be less than one
day). Most implementations of :meth:`timezone.utcoffset` will probably
look like one of these two:
return CONSTANT # fixed-offset class
return CONSTANT + self.dst(dt) # daylight-aware class
If :meth:`timezone.utcoffset` does not return ``None``, :meth:`timezone.dst`
should not return None either.
The default implementation of :meth:`timezone.utcoffset` returns the sum
of time zone and DST adjustments, if available.
.. method:: timezone.dst(dt)
Return the daylight saving time (DST) adjustment, as a :class:`timedelta`
object or ``None`` if DST information isnt known.
Return ``timedelta(0)`` if DST is not in effect. If DST is in effect, return
the offset as a :class:`timedelta` object (see :meth:`timezone.utcoffset`
for details). Note that DST offset, if applicable, has already been added
to the UTC offset returned by :meth:`timezone.utcoffset`, so theres no
need to consult :meth:`timezone.dst` unless youre interested in obtaining
DST info separately.
Most implementations of :meth:`timezone.dst` will probably look like one
of these two::
def dst(self, dt):
# a fixed-offset class: doesn't account for DST
return timedelta(0)
or::
def dst(self, dt):
# Code to set dston and dstoff to the time zone's DST
# transition times based on the input *dt*'s year, and
# expressed in standard local time.
dt_ = dt.replace(tzinfo=None)
if dt_ >= dston and dt_ < dstoff:
return timedelta(hours=1)
else:
return timedelta(0)
The default implementation of :meth:`timezone.dst` returns ``None``.
.. method:: timezone.tzname(dt)
Return the time zone name corresponding to the :class:`datetime` object
*dt*, as a string. Nothing about string names is defined by the
:class:`datetime` module, and theres no requirement that it mean anything
in particular. For example, “GMT”, “UTC”, “-500”, “-5:00”, “EDT”,
“US/Eastern”, “America/New York” are all valid replies. Return ``None`` if
a string name isnt known. Note that this is a method rather than a fixed
string primarily because some :class:`timezone` subclasses will wish to
return different names depending on the specific value of *dt* passed,
especially if the :class:`timezone` class is accounting for daylight time.
The default implementation of :meth:`timezone.tzname` returns the fixed
value specified when the :class:`timezone` instance is constructed.
If *name* is not provided in the constructor, the name returned by
``tzname()`` is generated from the value of the ``offset`` as follows.
If *offset* is ``timedelta(0)``, the name is “UTC”, otherwise it returns
the string provided by :meth:`timezone.isoformat` method.
These methods are called by a :class:`datetime` object, in response to their
methods of the same names. A :class:`datetime` object passes *self* as *dt*
argument.
Class attributes
^^^^^^^^^^^^^^^^
.. attribute:: timezone.utc
The UTC timezone, ``timezone(timedelta(0))``.
Class methods
^^^^^^^^^^^^^
.. class:: timezone(offset, name=None)
The *offset* argument must be specified as a :class:`timedelta`
object representing the difference between the local time and UTC.
It must be strictly between ``timedelta(hours=-24)`` and
``timedelta(hours=24)``, otherwise :exc:`ValueError` is raised.
The *name* argument is optional. If specified it must be a string
that will be used as the value returned by the :meth:`datetime.tzname`
method.
.. method:: timezone.isoformat(dt)
Return a string in the format ``UTC±HH:MM``, where ``±`` is the sign of
*offset* from UTC, ``HH`` and ``MM`` are two digits of offset's hours and
offset's minutes respectively. If *offset* is ``timedelta(0)``, “UTC”
is returned.
If *utc* is ``False``, this method always returns ``±HH:MM``.
*dt* is needed in determining the right offset; it can be ``None``.
Examples of usage
^^^^^^^^^^^^^^^^^
`Central European Time <https://en.wikipedia.org/wiki/Summer_time_in_Europe>`_
(CET), used in most parts of Europe and a few North African countries, is a
standard time which is 1 hour ahead of Coordinated Universal Time (UTC).
As of 2011, all member states of the European Union observe summer time;
those that during the winter use CET use Central European Summer Time (CEST)
(or: UTC+02:00, daylight saving time) in summer (from last Sunday of March
to last Sunday of October). ::
import datetime
class Cet(datetime.timezone):
def __init__(self):
super().__init__(datetime.timedelta(hours=1), "CET")
def dst(self, dt):
return datetime.timedelta(hours=1) if self.isdst(dt) else datetime.timedelta(0)
def tzname(self, dt):
return 'CEST' if self.isdst(dt) else 'CET'
def isdst(self, dt):
if dt is None:
return False
year, month, day, hour, minute, second, tz = dt.tuple()
if not 2000 <= year < 2100:
raise ValueError
if 3 < month < 10:
return True
if month == 3:
beg = 31 - (5*year//4 + 4) % 7 # last Sunday of March
if day < beg: return False
if day > beg: return True
return hour >= 3
if month == 10:
end = 31 - (5*year//4 + 1) % 7 # last Sunday of October
if day < end: return True
if day > end: return False
return hour < 3
return False
tz = Cet()
print(tz.isoformat(datetime(2011, 1, 1))) # UTC+01:00
print(tz.tzname (datetime(2011, 1, 1))) # CET
print(tz.isoformat(datetime(2011, 8, 1))) # UTC+02:00
print(tz.tzname (datetime(2011, 8, 1))) # CEST
The two formulas above are a simplification of the general
`Gauss's algorithm (disparate variation)
<https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Disparate_variation>`_
for calculating the week day of any given date::
def week_day (year, month, day, verbose=False):
if month <= 2:
raise ValueError
Y = year
y = Y%100
c = Y//100
m = month - 2
d = day
W = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
k1 = d + int(2.6*m - 0.2) - 507*c//4
k = k1 % 7
w = (5*year//4 + k) % 7
print(f'w(year) = (5*year//4 + {k}) % 7 # {100*c} <= year < {(c+1)*100}')
if verbose:
print()
print(f'year = {year}, month = {month}, day = {day}')
print( 'w(y) = (d + int(2.6*m - 0.2) + y + y//4 + c//4 - 2*c) % 7')
print( 'w(y) = (d + int(2.6*m - 0.2) + 5*y//4 - 7*c//4) % 7')
print()
print( 'w(year) = (d + int(2.6*m - 0.2) + 5*year//4 - 5*c*100//4 - 7*c//4) % 7')
print( 'w(year) = (d + int(2.6*m - 0.2) + 5*year//4 - 507*c//4) % 7')
print(f'w(year) = ({d} + int(2.6*{m} - 0.2) + 5*year//4 - 507*{c}//4) % 7')
print(f'w(year) = (5*year//4 + ({k1}) % 7) % 7')
print(f'w(year) = (5*year//4 + {k}) % 7')
print(f'w({year}) = {w} ({W[w]})')
which produces the following output for March and October::
>>> week_day(2000, 3, 31)
w(year) = (5*year//4 + 4) % 7 # 2000 <= year < 2100
>>> week_day(2000, 10, 31)
w(year) = (5*year//4 + 1) % 7 # 2000 <= year < 2100
:class:`datetime` Objects
-------------------------
A :class:`datetime` object is a single object containing all the information
for specifying an absolute date and time point.
:class:`datetime` assumes the current Gregorian calendar extended in both
directions, past and future. January 1 of year 1 is called day number 1,
January 2 of year 1 is called day number 2, and so on.
:class:`datetime` assumes there are exactly 3600*24 seconds in every day and
subject to adjustment via a :class:`timezone` object.
Constructors
^^^^^^^^^^^^
.. class:: datetime(self, year, month, day, hour=0, minute=0, second=0, nanosecond=0, tzinfo=None)
The *year*, *month* and *day* arguments are required. *tzinfo* may be
``None``, or an instance of a :class:`timezone` class. The remaining
arguments must be integers in the following ranges:
* ``MINYEAR <= year <= MAXYEAR``,
* ``1 <= month <= 12``,
* ``1 <= day <= number of days in the given month and year``,
* ``0 <= hour < 24``,
* ``0 <= minute < 60``,
* ``0 <= second < 60``,
* ``0 <= nanosecond < 999_999_999``,
If an argument outside those ranges is given, :exc:`ValueError` is raised.
.. function:: fromisoformat(date_string)
Return a :class:`datetime` corresponding to a *date_string* in the format
emitted by :meth:`datetime.isoformat`.
Specifically, this function supports strings in the format::
YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]]
where ``*`` can match any single character.
.. function:: fromordinal(n)
Return the :class:`datetime` corresponding to the proleptic Gregorian
ordinal, where January 1 of year 1 has ordinal 1. :exc:`ValueError` is
raised unless ``1 <= ordinal <= datetime.max.toordinal()``. The hour,
minute and second of the result are all 0, and *tzinfo* is ``None``.
.. function:: combine(date, time, tzinfo)
Return a new :class:`datetime` object whose date components are equal to
the given *date* objects (see :meth:`datetime.date`), and whose time
components are equal to the given *time* objects (see
:meth:`datetime.time`). If the *tzinfo* argument is provided, its value
is used to set the *tzinfo* attribute of the result, otherwise the
*tzinfo* attribute of the *date* argument is used.
Class attributes
^^^^^^^^^^^^^^^^
.. attribute:: datetime.MINYEAR
The smallest year number allowed in a :class:`datetime` object.
:attr:`datetime.MINYEAR` is ``1``.
.. attribute:: datetime.MAXYEAR
The largest year number allowed in a :class:`datetime` object.
:attr:`datetime.MAXYEAR` is ``9999``.
.. attribute:: datetime.EPOCH
:class:`datetime` object representing the time epoch of 2000-01-01 00:00:00
UTC (same as :mod:`time`'s epoch).
.. attribute:: datetime.timezone
The object passed as the *tzinfo* argument to the :class:`datetime`
constructor, or ``None`` if none was passed.
Class methods
^^^^^^^^^^^^^
.. method:: datetime.__add__(other)
In the expression ``datetime2 = datetime1.__add__(timedelta)``, ``datetime2``
is a duration of ``timedelta`` removed from ``datetime1``, moving forward
in time if ``timedelta > 0``, or backward if ``timedelta < 0``. The result
has the same :class:`timezone` attribute as the input ``datetime1``, and
``datetime2 - datetime1 == timedelta`` after.
Note that no time zone adjustments are done even if the input is an aware
object.
.. method:: datetime.__sub__(other)
If *other* is an instance of :class:`timedelta`, the expression
``datetime2 = datetime1.__sub__(timedelta)`` computes the ``datetime2`` such
that ``datetime2 + timedelta == datetime1``. As for addition, the result has
the same :class:`timezone` attribute as the input ``datetime1``, and no time
zone adjustments are done even if the input is aware.
If *other* is an instance of :class:`datetime`, subtraction
``timedelta = datetime2.__sub__(datetime1)`` is defined only if both operands
are naive, or if both are aware. If one is aware and the other is naive,
:exc:`TypeError` is raised.
If both are naive, or both are aware and have the same :class:`timezone`
attribute, the :class:`timezone` attributes are ignored, and the result
is a :class:`timedelta` object *t* such that ``datetime2 + t == datetime1``.
No time zone adjustments are done in this case.
If both are aware and have different :class:`timezone` attributes, ``a-b``
acts as if *a* and *b* were first converted to naive UTC datetimes first.
.. method:: datetime.__lt__(other)
Equivalent to ``dt1.toordinal() < dt2.toordinal()``.
.. method:: datetime.__le__(other)
Equivalent to ``dt1.toordinal() <= dt2.toordinal()``.
.. method:: datetime.__eq__(other)
Equivalent to ``dt1.toordinal() == dt2.toordinal()``.
.. method:: datetime.__ge__(other)
Equivalent to ``dt1.toordinal() >= dt2.toordinal()``.
.. method:: datetime.__gt__(other)
Equivalent to ``dt1.toordinal() > dt2.toordinal()``.
.. method:: datetime.utcoffset()
If *tzinfo* is ``None``, returns ``None``, else returns a
:class:`timedelta` object with magnitude less than one day.
.. method:: datetime.replace(year=None, month=None, day=None, hour=None, minute=None, second=None, nanosecond=None, tzinfo=True)
Return a :class:`datetime` with the same attributes, except for those
attributes given new values by whichever keyword arguments are specified.
Note that ``tzinfo=None`` can be specified to create a naive
:class:`datetime` from an aware :class:`datetime` with no conversion of
date and time data.
.. method:: datetime.astimezone(tz)
Return a :class:`datetime` object with new *tzinfo* attribute
*tz*, adjusting the date and time data so the result is the same UTC time
as *self*, but in *tz*s local time. *self* must be aware.
If you merely want to attach a :class:`timezone` object *tz* to a
:class:`datetime` *dt* without adjustment of date and time data, use
``dt.replace(tzinfo=tz)``. If you merely want to remove the :class:`timezone`
object from an aware :class:`datetime` *dt* without conversion of date and
time data, use ``dt.replace(tzinfo=None)``.
.. method:: datetime.isoformat(sep='T')
Return a string representing the date and time in ISO 8601 format
``YYYY-MM-DDTHH:MM:SS``. If :meth:`datetime.utcoffset` does not return
``None``, a string is appended, giving the UTC offset:
``YYYY-MM-DDTHH:MM:SS+HH:MM``.
.. method:: datetime.date()
Return a :class:`datetime` instance whose date and time zone components
are equal to the input object and time is set to midnight.
.. method:: datetime.time()
Return a :class:`timedelta` instance whose time components are equal to
the input object.
.. method:: datetime.toordinal()
Return the proleptic Gregorian ordinal of the date.
.. method:: datetime.isoweekday()
Return the day of the week as an integer, where Monday is 1 and Sunday
is 7. For example, ``date(2002, 12, 4).isoweekday() == 3``, a Wednesday.
.. method:: datetime.tuple()
Return the tuple ``(year, month, day, hour, minute, second, nanosecond, tzinfo)``.
Examples of usage
^^^^^^^^^^^^^^^^^
Examples of working with :class:`datetime` objects::
from datetime import timedelta, timezone, datetime, fromisoformat
print(datetime(2005, 7, 14, 12, 30)) # 2005-07-14 12:30:00
dt = fromisoformat('2006-11-21 16:30+01:00')
print(dt.add(timedelta(hours=23))) # 2006-11-22 15:30:00+01:00
tz1 = timezone(timedelta(hours=4, minutes=30))
print(tz1) # UTC+04:30
dt = datetime(1900, 11, 21, 3, 30, tzinfo=tz1)
print(dt) # 1900-11-21 03:30:00+04:30
print(dt.astimezone(timezone.utc)) # 1900-11-20 23:00:00+00:00

Wyświetl plik

@ -62,6 +62,7 @@ library.
builtins.rst
cmath.rst
collections.rst
datetime.rst
errno.rst
gc.rst
gzip.rst
@ -78,6 +79,7 @@ library.
socket.rst
ssl.rst
struct.rst
suntime.rst
sys.rst
time.rst
zlib.rst

Wyświetl plik

@ -0,0 +1,325 @@
:mod:`suntime` -- sunrise and sunset time
=========================================
.. module:: suntime
:synopsis: sunrise and sunset time
This library provides an approximated calculation of sunrise and sunset time.
It contains two classes, :class:`Sundatetime` and :class:`Suntime`, which
differ from the input and output data types.
Class :class:`Sundatetime`
--------------------------
The following class makes use of module :mod:`datetime` for expressing input
date and output time.
.. class:: Sundatetime(latitude, longitude, altitude=0)
Arguments *latitude* and *longitude* are floats representing the
coordinates of a place on Earth. *altitude* is an integer number for
observer's elevation in meters.
.. method:: Sundatetime.calc_sunrise_sunset(date)
Calculate the sunrise and sunset for the given date. *date* must be an
*aware* `datetime.datetime` object in the range [2000-01-01; 2100-01-01).
Time information is ignored, whereas time zone ``tzinfo`` is used to
provide meaningful output. The results are cached in :data:`sunrise` and
:data:`sunset` .
.. method:: Sundatetime.is_daytime(now)
Sundatetime.is_nighttime(now)
Argument *now* is an *aware* `datetime.datetime` object representing a
point in time. A boolean value is returned whether Sun is above/below the
horizon or not, ``None`` when data are meaningless.
.. method:: Sundatetime.is_sunrise(now)
Sundatetime.is_sunset(now)
Argument *now* is an *aware* `datetime.datetime` object representing a
point in time. A boolean value is returned whether *now* matches
sunrise/sunset time or not, ``None`` when data are meaningless.
The following instance variables are accessible:
.. data:: Sundatetime.latitude
Sundatetime.longitude
Float numbers for coordinates on Earth.
.. data:: Sundatetime.altitude
Elevation in meters for observations above the sea horizon. It corrects
for both apparent dip and terrestrial refraction.
.. data:: Sundatetime.sunrise
Sundatetime.sunset
They hold ``None`` when an instance is created, an *aware*
`datetime.datetime` after :meth:`calc_sunrise_sunset` is called.
.. note::
:data:`sunrise` may occur before 00:00 and :data:`sunset` after 23:59
on calculated *date*. See :ref:`unexpected-results`.
Class :class:`Suntime`
----------------------
The following class makes use of plain integers for expressing input date and
output time.
.. class:: Suntime(latitude, longitude, altitude=0, timezone=0)
Arguments *latitude* and *longitude* are floats representing the
coordinates of a place on Earth. *altitude* is an integer number for
observer's elevation in meters. *timezone* is an integer holding the
timezone offset from UTC in minutes. The results are cached in
:data:`sunrise` and :data:`sunset`.
.. method:: Suntime.calc_sunrise_sunset(year, month, day, dst=0)
Calculate the sunrise and sunset for the given year, month and day.
*year* must be in the range [2000; 2100). *dst* is an integer holding the
offset in minute (usually 60) that accounts for Daylight Saving Time.
.. method:: Suntime.is_daytime(now)
Suntime.is_nighttime(now)
Argument *now* is an integer holding the number of minutes since midnight.
A boolean value is returned whether Sun is above/below the horizon or not,
``None`` when data are meaningless.
.. method:: Suntime.is_sunrise(now)
Suntime.is_sunset(now)
Argument *now* is an an integer holding the number of minutes since midnight.
A boolean value is returned whether *now* matches sunrise/sunset time or not,
``None`` when data are meaningless.
The following instance variables are accessible:
.. data:: Suntime.latitude
Suntime.longitude
Float numbers for coordinates on Earth.
.. data:: Suntime.altitude
Elevation in meters for observations above the sea horizon. It corrects
for both apparent dip and terrestrial refraction.
.. data:: Suntime.sunrise
Suntime.sunset
It holds ``None`` when an instance is created, an integer for the
difference in minutes since 00:00 after :meth:`calc_sunrise_sunset`
is called.
.. note::
``sunrise`` may be negative and ``sunset`` may be greater than 1439
(24 hours). See :ref:`unexpected-results`.
.. _unexpected-results:
Unexpected results
------------------
:class:`Sundatetime` may return unexpected results: :data:`Sundatetime.sunrise`
may come before 00:00 and/or :data:`Sundatetime.sunset` may come after 23:59.
Similarly, :class:`Suntime` may return a negative :data:`Suntime.sunrise`
and/or a :data:`Suntime.sunset` greater or equal to 1440 (24 hours).
Assuming ``date`` is the current date and ``now`` is the current time,
the conditions which may lead to unexpected results are:
* *incorrect time zone*: *date*'s time zone is not consistent with provided
*longitude*. Suitable values for *timezone* and *dst* should be provided.
See example #3 (Novosibirsk) below.
* *Sun is up all day*: close to the poles, Sun never sets in summer/winter
time. For this dates, ``is_daytime()`` holds true for the whole ``date``.
Note that the following holds true: ``sunrise ≤ now < sunset``.
* *Sun is down all day*: close to the poles, Sun never raises in summer/winter
time. For this dates, ``is_nighttime()`` holds true for the whole ``date``.
Note that the following holds false: ``sunrise < sunset``.
* *Sun sets after midnight*: close to the poles, Sun may sets after
23:59. In this case, ``is_daytime()`` and ``is_nighttime()`` behave as
expected. The following condition is true: ``sunrise ≤ now ≤ 23:59 < sunset``.
Examples of usage
-----------------
A typical use case is::
import datetime, suntime, time
class Cet(datetime.timezone):
# See `datetime` documentation
# initialization
CET = Cet()
Rome = suntime.Sundatetime(42.5966460, 12.4360233)
Rome.calc_sunrise_sunset(datetime.datetime(2000, 1, 1, tzinfo=CET))
# main loop (every minute or more)
now = datetime.datetime(*time.localtime()[:5], tzinfo=CET)
if (now.date() > Rome.sunset.date()):
Rome.calc_sunrise_sunset(now)
Rome.is_daytime(now)
The following script shows sunrise and sunset time for several places and dates::
# place: latitude longitude
pl1 = ( 42.5966460, 12.4360233) # Rome
pl2 = ( 51.1627938,-122.9593616) # Vancouver
pl3 = (-33.9252192, 18.4240762) # CapeTown
pl4 = ( 55.1574890, 82.8547661) # Novosibirsk
pl5 = ( 78.6560170, 16.3447384) # Pyramiden
pl6 = pl5
pl7 = (-77.7817838, 166.4561470) # McMurdo
pl8 = pl7
# date: YY MM DD sunrise sunset
dt1 = (2000, 1, 1) # 7:37 16:49 - https://www.timeanddate.com/sun/italy/rome?month=1&year=2000
dt2 = (2014, 10, 3) # 7:15 18:46 - https://www.timeanddate.com/sun/canada/vancouver?month=10&year=2014
dt3 = (2016, 12, 21) # 5:32 19:57 - https://www.timeanddate.com/sun/south-africa/cape-town?month=12&year=2016
dt4 = (2021, 4, 24) # 6:04 20:50 - https://www.timeanddate.com/sun/russia/novosibirsk?month=4&year=2021
dt5 = (2040, 8, 25) # up all day - https://www.timeanddate.com/sun/@2729216?month=8&year=2033
dt6 = (2040, 8, 26) # 00:09
# 1:45 23:41 - https://www.timeanddate.com/sun/@2729216?month=8&year=2040
dt7 = (2033, 8, 10) # down all day - https://www.timeanddate.com/sun/antarctica/mcmurdo?month=8&year=2033
dt8 = (2033, 10, 21) # 3:00 24:13 - https://www.timeanddate.com/sun/antarctica/mcmurdo?month=10&year=2033
# timezone offsets and DSTs (in hours)
tz1 = ( 1, 0)
tz2 = (-8, 1)
tz3 = ( 2, 0)
tz4 = ( 0, 0) # wrong; it generates negative hour because actual timezone is (7, 0)
tz5 = ( 1, 1)
tz6 = ( 1, 1)
tz7 = (13,-1)
tz8 = (13, 0)
The following snippet of code makes use of class :class:`Sundatetime`::
from suntime import Sundatetime
from datetime import datetime, timedelta, timezone
class Tz(timezone):
def __init__(self, hours, dst=0):
super().__init__(timedelta(hours=hours))
self._dst = dst
def dst(self, dt):
return timedelta(hours=self._dst) if self.isdst(dt) else timedelta(0)
def isdst(self, dt):
return self._dst != 0
print('Rome:')
sd1 = Sundatetime(*pl1)
sd1.calc_sunrise_sunset(datetime(*dt1, tzinfo=Tz(*tz1)))
print('>', sd1.sunrise) # 2000-01-01 07:40:00+01:00
print('>', sd1.sunset ) # 2000-01-01 16:47:00+01:00
print('Vancouver:')
sd2 = Sundatetime(*pl2)
sd2.calc_sunrise_sunset(datetime(*dt2, tzinfo=Tz(*tz2)))
print('>', sd2.sunrise) # 2014-10-03 07:16:00-08:00
print('>', sd2.sunset ) # 2014-10-03 18:46:00-08:00
print('Cape Town:')
sd3 = Sundatetime(*pl3)
sd3.calc_sunrise_sunset(datetime(*dt3, tzinfo=Tz(*tz3)))
print('>', sd3.sunrise) # 2016-12-21 05:32:00+02:00
print('>', sd3.sunset ) # 2016-12-21 19:57:00+02:00
print('Novosibirsk:')
sd4 = Sundatetime(*pl4)
sd4.calc_sunrise_sunset(datetime(*dt4, tzinfo=Tz(*tz4)))
print('>', sd4.sunrise) # 2021-04-23 23:04:00+00:00
print('>', sd4.sunset ) # 2021-04-24 13:49:00+00:00
print('Pyramiden:')
sd5 = Sundatetime(*pl5)
sd5.calc_sunrise_sunset(datetime(*dt5, tzinfo=Tz(*tz5)))
print('>', sd5.sunrise) # 2040-08-24 12:57:00+02:00
print('>', sd5.sunset ) # 2040-08-26 12:57:00+02:00
print('Pyramiden:')
sd6 = Sundatetime(*pl6)
sd6.calc_sunrise_sunset(datetime(*dt6, tzinfo=Tz(*tz6)))
print('>', sd6.sunrise) # 2040-08-26 01:35:00+02:00
print('>', sd6.sunset ) # 2040-08-27 00:18:00+02:00
print('McMurdo:')
sd7 = Sundatetime(*pl7)
sd7.calc_sunrise_sunset(datetime(*dt7, tzinfo=Tz(*tz7)))
print('>', sd7.sunrise) # 2033-08-11 13:00:00+12:00
print('>', sd7.sunset ) # 2033-08-09 13:00:00+12:00
print('McMurdo:')
sd8 = Sundatetime(*pl8)
sd8.calc_sunrise_sunset(datetime(*dt8, tzinfo=Tz(*tz8)))
print('>', sd8.sunrise) # 2033-10-21 03:06:00+13:00
print('>', sd8.sunset ) # 2033-10-22 00:12:00+13:00
If :mod:`datetime` module is not available, the same input data can feed class
:class:`Suntime`::
from suntime import Suntime
st1 = Suntime(*pl1, timezone=tz1[0]*60)
st1.calc_sunrise_sunset(*dt1, dst=tz1[1]*60)
print('Rome:')
print('>', divmod(st1.sunrise, 60)) # (7, 40)
print('>', divmod(st1.sunset , 60)) # (16, 47)
st2 = Suntime(*pl2, timezone=tz2[0]*60)
st2.calc_sunrise_sunset(*dt2, dst=tz2[1]*60)
print('Vancouver:')
print('>', divmod(st2.sunrise, 60)) # (7, 16)
print('>', divmod(st2.sunset , 60)) # (18, 46)
st3 = Suntime(*pl3, timezone=tz3[0]*60)
st3.calc_sunrise_sunset(*dt3, dst=tz3[1]*60)
print('Cape Town:')
print('>', divmod(st3.sunrise, 60)) # (5, 32)
print('>', divmod(st3.sunset , 60)) # (19, 57)
st4 = Suntime(*pl4, timezone=tz4[0]*60)
st4.calc_sunrise_sunset(*dt4, dst=tz4[1]*60)
print('Novosibirsk:')
print('>', divmod(st4.sunrise, 60)) # (-1, 4)
print('>', divmod(st4.sunset , 60)) # (13, 49)
st5 = Suntime(*pl5, timezone=tz5[0]*60)
st5.calc_sunrise_sunset(*dt5, dst=tz5[1]*60)
print('Pyramiden:')
print('>', divmod(st5.sunrise, 60)) # (-12, 57)
print('>', divmod(st5.sunset , 60)) # (36, 57)
st6 = Suntime(*pl6, timezone=tz6[0]*60)
st6.calc_sunrise_sunset(*dt6, dst=tz6[1]*60)
print('Pyramiden:')
print('>', divmod(st6.sunrise, 60)) # (1, 35)
print('>', divmod(st6.sunset , 60)) # (24, 18)
st7 = Suntime(*pl7, timezone=tz7[0]*60)
st7.calc_sunrise_sunset(*dt7, dst=tz7[1]*60)
print('McMurdo:')
print('>', divmod(st7.sunrise, 60)) # (37, 0)
print('>', divmod(st7.sunset , 60)) # (-11, 0)
st8 = Suntime(*pl8, timezone=tz8[0]*60)
st8.calc_sunrise_sunset(*dt8, dst=tz8[1]*60)
print('McMurdo:')
print('>', divmod(st8.sunrise, 60)) # (3, 6)
print('>', divmod(st8.sunset , 60)) # (24, 12)