2014-01-16 05:23:56 +01:00
|
|
|
|
|
|
|
|
|
================
|
|
|
|
|
Dates and Time
|
|
|
|
|
================
|
|
|
|
|
|
2020-06-07 17:12:11 +02:00
|
|
|
|
.. currentmodule:: skyfield.timelib
|
2014-01-21 02:50:56 +01:00
|
|
|
|
|
2014-01-17 06:25:58 +01:00
|
|
|
|
Astronomers use several different numerical scales for measuring time.
|
2016-03-26 23:43:43 +01:00
|
|
|
|
Skyfield often has to use several timescales
|
|
|
|
|
even within a single computation.
|
2016-03-24 05:25:03 +01:00
|
|
|
|
So the :class:`Time` class
|
2014-01-17 06:25:58 +01:00
|
|
|
|
is designed to cache each new time scale
|
|
|
|
|
when a calculation first demands it.
|
|
|
|
|
Further demands for the same time scale
|
|
|
|
|
can then be satisfied without recomputing the value again.
|
|
|
|
|
|
2016-03-23 06:07:19 +01:00
|
|
|
|
Each time scale supported by :class:`Time`
|
2014-01-17 06:25:58 +01:00
|
|
|
|
is described in detail in one of the sections below.
|
|
|
|
|
The supported time scales are:
|
|
|
|
|
|
2016-03-25 16:29:25 +01:00
|
|
|
|
* ``t.utc`` — Coordinated Universal Time (“Greenwich Time”)
|
|
|
|
|
* ``t.tai`` — International Atomic Time
|
|
|
|
|
* ``t.tt`` — Terrestrial Time
|
2016-03-26 23:43:43 +01:00
|
|
|
|
* ``t.tdb`` — Barycentric Dynamical Time (the JPL’s *T*\ :sub:`eph`)
|
2016-03-25 16:29:25 +01:00
|
|
|
|
* ``t.ut1`` — Universal Time
|
2014-01-17 06:25:58 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
To specify a time,
|
|
|
|
|
first build a :class:`Timescale` object
|
2020-06-15 19:45:35 +02:00
|
|
|
|
by calling Skyfield’s ``load.timescale()`` routine.
|
2016-03-24 05:25:03 +01:00
|
|
|
|
This downloads several data files from international authorities —
|
|
|
|
|
the United States Naval Observatory
|
|
|
|
|
and the International Earth Rotation Service —
|
|
|
|
|
to make sure that Skyfield has current information
|
|
|
|
|
about both leap seconds and the orientation of the Earth.
|
|
|
|
|
(Both topics are covered in more detail below.)
|
|
|
|
|
|
|
|
|
|
Once you have a timescale object,
|
|
|
|
|
which Skyfield programmers conventionally name ``ts``,
|
|
|
|
|
you can use its methods to create times
|
|
|
|
|
specified using any of the time scales listed above:
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
2015-10-19 06:23:42 +02:00
|
|
|
|
.. testsetup::
|
|
|
|
|
|
|
|
|
|
__import__('skyfield.tests.fixes').tests.fixes.setup()
|
|
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
np.set_printoptions(suppress=True)
|
|
|
|
|
|
2014-01-16 06:48:13 +01:00
|
|
|
|
.. testcode::
|
|
|
|
|
|
2015-11-02 06:17:58 +01:00
|
|
|
|
# Building a date object
|
|
|
|
|
|
2016-03-21 02:39:15 +01:00
|
|
|
|
from skyfield.api import load
|
|
|
|
|
ts = load.timescale()
|
2016-03-24 05:25:03 +01:00
|
|
|
|
t = ts.utc(2014, 1, 18)
|
2014-01-19 14:53:44 +01:00
|
|
|
|
|
2019-07-22 15:02:09 +02:00
|
|
|
|
If your computer has difficulty downloading the official time scale files,
|
|
|
|
|
try :ref:`built-in-timescale-files`.
|
|
|
|
|
|
2014-01-19 14:53:44 +01:00
|
|
|
|
The possibilities that will be explored in the course of this page
|
|
|
|
|
are::
|
|
|
|
|
|
2017-02-27 04:38:03 +01:00
|
|
|
|
# All the ways you can create a Time object
|
|
|
|
|
# using a timescale:
|
2016-03-24 05:25:03 +01:00
|
|
|
|
|
2020-07-17 14:18:15 +02:00
|
|
|
|
t = ts.utc(year, month, day, hour, minute, second)
|
|
|
|
|
t = ts.utc(dt) # Python datetime.datetime object
|
2016-03-24 05:25:03 +01:00
|
|
|
|
|
2020-07-17 14:18:15 +02:00
|
|
|
|
t = ts.tai(year, month, day, hour, minute, second)
|
|
|
|
|
t = ts.tai_jd(float) # Julian date
|
2016-03-24 05:25:03 +01:00
|
|
|
|
|
2020-07-17 14:18:15 +02:00
|
|
|
|
t = ts.tt(year, month, day, hour, minute, second)
|
|
|
|
|
t = ts.tt_jd(float) # Julian date
|
2016-03-24 05:25:03 +01:00
|
|
|
|
|
2020-07-17 14:18:15 +02:00
|
|
|
|
t = ts.tdb(year, month, day, hour, minute, second)
|
|
|
|
|
t = ts.tdb_jd(float) # Julian date
|
2016-03-24 05:25:03 +01:00
|
|
|
|
|
2020-07-17 14:18:15 +02:00
|
|
|
|
t = ts.ut1(year, month, day, hour, minute, second)
|
|
|
|
|
t = ts.ut1_jd(float) # Julian date
|
2016-03-24 05:25:03 +01:00
|
|
|
|
|
|
|
|
|
Once you have constructed a :class:`Time` object,
|
|
|
|
|
you can provide it to any Skyfield routine that needs it.
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2016-03-21 02:39:15 +01:00
|
|
|
|
from skyfield.api import load
|
2016-02-23 18:47:19 +01:00
|
|
|
|
|
2015-08-16 04:55:57 +02:00
|
|
|
|
planets = load('de421.bsp')
|
2015-10-17 06:13:48 +02:00
|
|
|
|
earth = planets['earth']
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
2015-11-02 06:17:58 +01:00
|
|
|
|
# Building a date and using it with at()
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
ts = load.timescale()
|
|
|
|
|
t = ts.utc(2014, 1, 1)
|
|
|
|
|
print(earth.at(t).position.au)
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
[-0.17461758 0.88567056 0.38384886]
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
If you will need to use the same time value several times
|
|
|
|
|
then it is best to create the object once,
|
|
|
|
|
through a single method call to your timescale object,
|
|
|
|
|
and then use that single time repeatedly in your calculations.
|
2017-02-27 04:38:03 +01:00
|
|
|
|
Not only will you avoid asking Skyfield to repeatedly translate
|
2016-03-24 05:25:03 +01:00
|
|
|
|
the same time value between the different time scales,
|
2017-02-27 04:38:03 +01:00
|
|
|
|
but other expensive values that depend upon time
|
|
|
|
|
are also automatically cached on the date object.
|
2016-03-24 05:25:03 +01:00
|
|
|
|
(See the section on :ref:`date-cache` for more details.)
|
2014-01-17 06:25:58 +01:00
|
|
|
|
|
|
|
|
|
.. _building-dates:
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
2020-07-17 14:18:15 +02:00
|
|
|
|
Building and printing UTC
|
|
|
|
|
=========================
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
|
|
|
|
The ``utc`` parameter in the examples above
|
|
|
|
|
specifies Coordinated Universal Time (UTC),
|
|
|
|
|
the world clock known affectionately as “Greenwich Mean Time”
|
|
|
|
|
which is the basis for all of the world’s timezones.
|
|
|
|
|
|
2014-01-19 14:53:44 +01:00
|
|
|
|
If you are comfortable dealing directly with UTC,
|
2014-01-19 06:00:29 +01:00
|
|
|
|
you can simply set and retrieve it manually.
|
2016-03-24 05:25:03 +01:00
|
|
|
|
You can provide its constructor with just the year, month, and day,
|
|
|
|
|
or be more specific and give an hour, minute, and second.
|
|
|
|
|
And not only can you attach a fraction to the seconds,
|
|
|
|
|
but you can also freely use fractional days, hours, and minutes.
|
2017-02-27 04:38:03 +01:00
|
|
|
|
For example:
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
# Four ways to specify 2014 January 18 01:35:37.5
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
t1 = ts.utc(2014, 1, 18.06640625)
|
|
|
|
|
t2 = ts.utc(2014, 1, 18, 1.59375)
|
|
|
|
|
t3 = ts.utc(2014, 1, 18, 1, 35.625)
|
|
|
|
|
t4 = ts.utc(2014, 1, 18, 1, 35, 37.5)
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
assert t1 == t2 == t3 == t4 # True!
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
# Several ways to print a time as UTC.
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
2020-05-24 21:05:32 +02:00
|
|
|
|
print(tuple(t1.utc))
|
2020-07-17 14:18:15 +02:00
|
|
|
|
print(t1.utc_iso(' '))
|
2016-03-24 05:25:03 +01:00
|
|
|
|
print(t1.utc_jpl())
|
|
|
|
|
print(t1.utc_strftime('Date %Y-%m-%d and time %H:%M:%S'))
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
2014-01-26 22:26:22 +01:00
|
|
|
|
(2014, 1, 18, 1, 35, 37.5)
|
2020-07-17 14:18:15 +02:00
|
|
|
|
2014-01-18 01:35:38Z
|
2014-01-19 14:53:44 +01:00
|
|
|
|
A.D. 2014-Jan-18 01:35:37.5000 UT
|
2014-01-19 06:00:29 +01:00
|
|
|
|
Date 2014-01-18 and time 01:35:38
|
|
|
|
|
|
2020-05-24 21:05:32 +02:00
|
|
|
|
The 6 values returned by ``utc()``
|
|
|
|
|
can also be accessed as the attributes
|
|
|
|
|
``year``, ``month``, ``day``, ``hour``, ``minute``, and ``second``.
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
print(t1.utc.year, '/', t2.utc.month, '/', t2.utc.day)
|
|
|
|
|
print(t2.utc.hour, ':', t2.utc.minute, ':', t2.utc.second)
|
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
2014 / 1 / 18
|
|
|
|
|
1 : 35 : 37.5
|
|
|
|
|
|
2014-01-19 06:00:29 +01:00
|
|
|
|
And by scraping together the minimal support for UTC
|
|
|
|
|
that exists in the Python Standard Library,
|
2016-02-22 15:30:40 +01:00
|
|
|
|
Skyfield is able to offer a :func:`~skyfield.timelib.Timescale.now()` function
|
2014-11-23 22:23:25 +01:00
|
|
|
|
that reads your system clock
|
|
|
|
|
and returns the current time as a Julian date object
|
2014-01-19 06:00:29 +01:00
|
|
|
|
(assuming that your operating system clock is correct
|
2014-11-23 22:23:25 +01:00
|
|
|
|
and configured with the correct time zone):
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2016-03-21 02:39:15 +01:00
|
|
|
|
from skyfield.api import load
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
2015-11-02 06:17:58 +01:00
|
|
|
|
# Asking the current date and time
|
|
|
|
|
|
2016-03-21 02:39:15 +01:00
|
|
|
|
ts = load.timescale()
|
2016-03-24 05:25:03 +01:00
|
|
|
|
t = ts.now()
|
|
|
|
|
print(t.utc_jpl())
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
2015-10-19 06:23:42 +02:00
|
|
|
|
A.D. 2015-Oct-11 10:00:00.0000 UT
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
2020-07-17 14:18:15 +02:00
|
|
|
|
UTC and your timezone
|
|
|
|
|
=====================
|
|
|
|
|
|
|
|
|
|
To move beyond UTC and work with other world timezones,
|
|
|
|
|
you will need to install a time zone database
|
|
|
|
|
for your version of Python.
|
|
|
|
|
|
|
|
|
|
* Every version of Python that Skyfield supports
|
|
|
|
|
will work with the `pytz`_ package described in this section.
|
|
|
|
|
|
|
|
|
|
* Python 3.6 upgraded the Standard Library ``datetime`` type
|
|
|
|
|
so that the contortions of `pytz`_ are no longer necessary,
|
|
|
|
|
and instead recommends
|
|
|
|
|
`dateutil <https://dateutil.readthedocs.io/en/stable/>`_
|
|
|
|
|
for working with timezones.
|
|
|
|
|
Consult its documentation if you are interested in using it.
|
|
|
|
|
|
|
|
|
|
* Python 3.9 will offer a native
|
|
|
|
|
`zoneinfo <https://docs.python.org/3.9/library/zoneinfo.html>`_
|
|
|
|
|
module that for the first time brings timezone support
|
|
|
|
|
into the Python Standard Library.
|
|
|
|
|
|
|
|
|
|
But this documentation will focus on the approach
|
|
|
|
|
which works universally across all Python versions.
|
|
|
|
|
You can install the third-party `pytz`_ library
|
|
|
|
|
by listing it in the dependencies of your package,
|
|
|
|
|
or adding it to your project’s `requirements.txt`_ file,
|
2014-01-19 06:00:29 +01:00
|
|
|
|
or simply installing it manually::
|
|
|
|
|
|
|
|
|
|
pip install pytz
|
|
|
|
|
|
|
|
|
|
Once it is installed,
|
|
|
|
|
building Julian dates from local times is simple.
|
|
|
|
|
Instantiate a normal Python ``datetime``,
|
|
|
|
|
pass it to the ``localize()`` method of your time zone,
|
|
|
|
|
and pass the result to Skyfield:
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
2014-01-19 14:53:44 +01:00
|
|
|
|
from pytz import timezone
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
|
|
|
|
eastern = timezone('US/Eastern')
|
|
|
|
|
|
|
|
|
|
# Converting US Eastern Time to a Julian date.
|
|
|
|
|
|
|
|
|
|
d = datetime(2014, 1, 16, 1, 32, 9)
|
|
|
|
|
e = eastern.localize(d)
|
2020-07-17 15:41:48 +02:00
|
|
|
|
t = ts.from_datetime(e)
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
2014-01-19 14:53:44 +01:00
|
|
|
|
And if Skyfield returns a Julian date at the end of a calculation,
|
|
|
|
|
you can ask the Julian date object to build a ``datetime`` object
|
2016-03-24 05:25:03 +01:00
|
|
|
|
for either UTC or for your own timezone:
|
2014-01-19 14:53:44 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
# UTC datetime
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
dt = t.utc_datetime()
|
2014-05-29 23:51:40 +02:00
|
|
|
|
print('UTC: ' + str(dt))
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
|
|
|
|
# Converting back to an Eastern Time datetime.
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
dt = t.astimezone(eastern)
|
2014-05-29 23:51:40 +02:00
|
|
|
|
print('EST: ' + str(dt))
|
2014-01-19 06:00:29 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
UTC: 2014-01-16 06:32:09+00:00
|
|
|
|
|
EST: 2014-01-16 01:32:09-05:00
|
|
|
|
|
|
|
|
|
|
As we would expect,
|
|
|
|
|
1:32 AM in the Eastern time zone in January
|
|
|
|
|
is 6:32 AM local time in Greenwich, England,
|
|
|
|
|
five hours to the east across the Atlantic.
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
2016-03-23 06:07:19 +01:00
|
|
|
|
Note that Skyfield’s :meth:`~Time.astimezone()` method
|
2014-01-19 06:00:29 +01:00
|
|
|
|
will detect that you are using a ``pytz`` timezone
|
|
|
|
|
and automatically call its ``normalize()`` method for you —
|
|
|
|
|
which makes sure that daylight savings time is handled correctly —
|
|
|
|
|
to spare you from having to make the call yourself.
|
|
|
|
|
|
2016-03-23 06:07:19 +01:00
|
|
|
|
If you want a :class:`Time` to hold an entire array of dates,
|
2014-01-19 06:00:29 +01:00
|
|
|
|
as discussed below in :ref:`date-arrays`,
|
2014-01-19 07:48:08 +01:00
|
|
|
|
then you can provide a list of ``datetime`` objects
|
|
|
|
|
when building a Julian date.
|
|
|
|
|
The UTC methods will then return whole lists of values.
|
|
|
|
|
|
|
|
|
|
.. _leap-seconds:
|
|
|
|
|
|
|
|
|
|
UTC and leap seconds
|
|
|
|
|
====================
|
|
|
|
|
|
|
|
|
|
The rate of Earth’s rotation is gradually slowing down.
|
|
|
|
|
Since the UTC standard specifies a fixed length for the second,
|
2014-01-19 14:53:44 +01:00
|
|
|
|
promises a day of 24 hours, and limits an hour to 60 minutes,
|
2014-01-19 07:48:08 +01:00
|
|
|
|
the only way to stay within the rules
|
|
|
|
|
while keeping UTC synchronized with the Earth
|
|
|
|
|
is to occasionally add an extra leap second
|
|
|
|
|
to one of the year’s minutes.
|
|
|
|
|
|
|
|
|
|
The `International Earth Rotation Service <http://hpiers.obspm.fr/>`_
|
2014-01-19 14:53:44 +01:00
|
|
|
|
currently restricts itself to appending a leap second
|
|
|
|
|
to the last minute of June or the last minute of December.
|
2014-01-19 07:48:08 +01:00
|
|
|
|
When a leap second is inserted,
|
|
|
|
|
its minute counts 61 seconds numbered 00–60
|
|
|
|
|
instead of staying within the usual range 00–59.
|
2017-02-27 04:38:03 +01:00
|
|
|
|
One recent leap second was in June 2012:
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2015-11-02 06:17:58 +01:00
|
|
|
|
# Display 5 seconds around a leap second
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
five_seconds = [58, 59, 60, 61, 62]
|
|
|
|
|
t = ts.utc(2012, 6, 30, 23, 59, five_seconds)
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
for string in t.utc_jpl():
|
2014-05-29 23:51:40 +02:00
|
|
|
|
print(string)
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
2014-01-19 14:53:44 +01:00
|
|
|
|
A.D. 2012-Jun-30 23:59:58.0000 UT
|
|
|
|
|
A.D. 2012-Jun-30 23:59:59.0000 UT
|
|
|
|
|
A.D. 2012-Jun-30 23:59:60.0000 UT
|
|
|
|
|
A.D. 2012-Jul-01 00:00:00.0000 UT
|
|
|
|
|
A.D. 2012-Jul-01 00:00:01.0000 UT
|
|
|
|
|
|
|
|
|
|
Note that Skyfield has no problem with a calendar tuple
|
|
|
|
|
that has hours, minutes, or — as in this case —
|
|
|
|
|
seconds that are out of range.
|
2017-02-27 04:38:03 +01:00
|
|
|
|
When we provided a range of numbers 58 through 62 as seconds,
|
|
|
|
|
Skyfield added exactly the number of seconds we specified
|
2016-03-24 05:25:03 +01:00
|
|
|
|
to the end of June
|
|
|
|
|
and let the value overflow cleanly into the beginning of July.
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
|
|
|
|
Keep two consequences in mind when using UTC in your calculations.
|
|
|
|
|
|
|
|
|
|
First, expect an occasional jump or discrepancy
|
|
|
|
|
if you are striding forward through time
|
|
|
|
|
using the UTC minute, hour, or day.
|
|
|
|
|
A graph will show a planet moving slightly farther
|
2016-03-24 05:25:03 +01:00
|
|
|
|
during an hour that was lengthened by a leap second.
|
|
|
|
|
An Earth satellite’s velocity will seem higher
|
|
|
|
|
when you reach the minute that includes 61 seconds.
|
|
|
|
|
And so forth.
|
2014-01-19 07:48:08 +01:00
|
|
|
|
Problems like these are the reason
|
2017-02-27 04:38:03 +01:00
|
|
|
|
that the :class:`Time` class only uses UTC for input and output,
|
2014-01-19 07:48:08 +01:00
|
|
|
|
and insists on keeping time internally
|
|
|
|
|
using the uniform time scales discussed below in :ref:`tai-tt-tdb`.
|
|
|
|
|
|
|
|
|
|
Second, leap seconds disqualify the Python ``datetime``
|
2014-01-19 16:28:05 +01:00
|
|
|
|
from use as a general way to represent time
|
2014-01-19 07:48:08 +01:00
|
|
|
|
because it refuses to accept seconds greater than 59:
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
datetime(2012, 6, 30, 19, 59, 60)
|
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
...
|
|
|
|
|
ValueError: second must be in 0..59
|
|
|
|
|
|
2014-11-23 22:23:25 +01:00
|
|
|
|
That is why Skyfield offers a second version
|
|
|
|
|
of each method that returns a ``datetime``::
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
t.utc_datetime_and_leap_second()
|
|
|
|
|
t.astimezone_and_leap_second(tz)
|
2014-11-23 22:23:25 +01:00
|
|
|
|
|
|
|
|
|
These more accurate alternatives also return a ``leap_second``,
|
|
|
|
|
which usually has the value ``0`` but jumps to ``1``
|
2014-01-19 07:48:08 +01:00
|
|
|
|
when Skyfield is forced to represent a leap second
|
2014-11-23 22:23:25 +01:00
|
|
|
|
as a ``datetime`` with the incorrect time 23:59:59.
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2015-11-02 06:17:58 +01:00
|
|
|
|
# Asking for the leap_second flag to learn the whole story
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
dt, leap_second = t.astimezone_and_leap_second(eastern)
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
2014-01-19 16:28:05 +01:00
|
|
|
|
for dt_i, leap_second_i in zip(dt, leap_second):
|
2016-03-24 05:25:03 +01:00
|
|
|
|
print('{0} leap_second = {1}'.format(dt_i, leap_second_i))
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
2012-06-30 19:59:58-04:00 leap_second = 0
|
|
|
|
|
2012-06-30 19:59:59-04:00 leap_second = 0
|
|
|
|
|
2012-06-30 19:59:59-04:00 leap_second = 1
|
|
|
|
|
2012-06-30 20:00:00-04:00 leap_second = 0
|
|
|
|
|
2012-06-30 20:00:01-04:00 leap_second = 0
|
2014-01-19 07:48:08 +01:00
|
|
|
|
|
2014-01-19 14:53:44 +01:00
|
|
|
|
Using calendar tuples to represent UTC times is more elegant
|
2014-01-19 16:28:05 +01:00
|
|
|
|
than using Python ``datetime`` objects
|
2014-01-19 07:48:08 +01:00
|
|
|
|
because leap seconds can be represented accurately.
|
|
|
|
|
If your application cannot avoid using ``datetime`` objects,
|
|
|
|
|
then you will have to decide
|
|
|
|
|
whether to simply ignore the ``leap_second`` value
|
|
|
|
|
or to somehow output the leap second information.
|
|
|
|
|
|
2014-01-19 06:00:29 +01:00
|
|
|
|
.. _date-arrays:
|
|
|
|
|
|
2014-01-16 08:41:15 +01:00
|
|
|
|
Date arrays
|
|
|
|
|
===========
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
2017-02-27 04:38:03 +01:00
|
|
|
|
If you want to ask where a planet or satellite was
|
|
|
|
|
at a whole list of different times and dates,
|
|
|
|
|
then Skyfield will work most efficiently
|
|
|
|
|
if you build a single :class:`Time` object
|
2014-01-19 16:28:05 +01:00
|
|
|
|
that holds an entire array of dates,
|
2016-03-23 06:07:19 +01:00
|
|
|
|
instead of building many separate :class:`Time` objects.
|
2014-01-19 16:28:05 +01:00
|
|
|
|
There are three techniques for building arrays.
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
* Provide ``ts.utc()`` with a Python list of ``datetime`` objects.
|
2014-01-19 16:28:05 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
* Provide ``tai()`` or ``tt()`` or ``tdb()`` or ``ut1()``
|
|
|
|
|
with an entire NumPy array or Python list of floating point values.
|
2014-01-19 16:28:05 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
* When specifying year, month, day, hour, minute, and second,
|
|
|
|
|
make one of the values a list or array.
|
2014-01-19 16:28:05 +01:00
|
|
|
|
|
|
|
|
|
The last possibility is generally the one that is the most fun,
|
|
|
|
|
because its lets you vary whichever time unit you want
|
|
|
|
|
while holding the others steady.
|
|
|
|
|
And you are free to provide out-of-range values
|
|
|
|
|
and leave it to Skyfield to work out the correct result.
|
|
|
|
|
Here are some examples::
|
|
|
|
|
|
2020-06-16 14:07:01 +02:00
|
|
|
|
ts.utc(range(1900, 1950)) # Fifty years 1900–1949
|
2016-03-24 05:25:03 +01:00
|
|
|
|
ts.utc(1980, range(1, 25)) # Twenty-four months
|
|
|
|
|
ts.utc(2005, 5, [1, 10, 20]) # 1st, 10th, and 20th of May
|
2014-01-19 16:28:05 +01:00
|
|
|
|
|
|
|
|
|
# The ten seconds crossing the 1974 leap second
|
2016-03-24 05:25:03 +01:00
|
|
|
|
ts.utc(1975, 1, 1, 0, 0, range(-5, 5))
|
2014-01-19 16:28:05 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
The resulting :class:`Time` object will hold an array of times
|
|
|
|
|
instead of just a single scalar value.
|
2020-06-16 14:07:01 +02:00
|
|
|
|
As illustrated in the previous section (on leap seconds),
|
|
|
|
|
you can use a Python ``for`` to print each time separately:
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
t = ts.utc(2020, 6, 16, 7, range(4))
|
|
|
|
|
|
|
|
|
|
for ti in t:
|
|
|
|
|
print(ti.utc_strftime('%Y-%m-%d %H:%M'))
|
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
2020-06-16 07:00
|
|
|
|
|
2020-06-16 07:01
|
|
|
|
|
2020-06-16 07:02
|
|
|
|
|
2020-06-16 07:03
|
|
|
|
|
|
|
|
|
|
When you provide a time array as input to a Skyfield calculation,
|
|
|
|
|
the output array will have an extra dimension
|
|
|
|
|
that expands what would normally be a single result
|
2014-01-19 16:28:05 +01:00
|
|
|
|
into as many results as you provided dates.
|
2014-01-26 20:44:07 +01:00
|
|
|
|
We can compute the position of the Earth as an example:
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2014-01-19 16:28:05 +01:00
|
|
|
|
# Single Earth position
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
t = ts.utc(2014, 1, 1)
|
|
|
|
|
pos = earth.at(t).position.au
|
2014-06-11 20:51:35 +02:00
|
|
|
|
print(pos)
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
[-0.17461758 0.88567056 0.38384886]
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2014-01-19 16:28:05 +01:00
|
|
|
|
# Whole array of Earth positions
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
days = [1, 2, 3, 4]
|
2016-03-24 05:25:03 +01:00
|
|
|
|
t = ts.utc(2014, 1, days)
|
|
|
|
|
pos = earth.at(t).position.au
|
2014-05-29 23:51:40 +02:00
|
|
|
|
print(pos)
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
[[-0.17461758 -0.19179872 -0.20891924 -0.22597338]
|
|
|
|
|
[ 0.88567056 0.88265548 0.87936337 0.87579547]
|
|
|
|
|
[ 0.38384886 0.38254134 0.38111391 0.37956709]]
|
|
|
|
|
|
|
|
|
|
Note the shape of the resulting NumPy array.
|
2014-01-19 16:28:05 +01:00
|
|
|
|
If you unpack this array into three names,
|
2014-01-16 06:48:13 +01:00
|
|
|
|
then you get three four-element arrays
|
2014-01-19 16:28:05 +01:00
|
|
|
|
corresponding to the four dates.
|
|
|
|
|
These four-element arrays are ready to be submitted to `matplotlib`_
|
2020-06-16 14:07:01 +02:00
|
|
|
|
and other scientific Python tools:
|
|
|
|
|
|
|
|
|
|
.. testsetup::
|
|
|
|
|
|
|
|
|
|
def plot(*args): pass
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
2014-01-19 16:28:05 +01:00
|
|
|
|
x, y, z = pos # four values each
|
2020-06-16 14:07:01 +02:00
|
|
|
|
plot(x, y) # example matplotlib call
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
If you instead slice along the second axis,
|
|
|
|
|
then you can retrieve an individual position for a particular date —
|
2014-06-11 20:51:35 +02:00
|
|
|
|
and the first position is exactly what was returned above
|
2014-01-19 16:28:05 +01:00
|
|
|
|
when we computed the January 1st position by itself:
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2014-06-11 22:03:08 +02:00
|
|
|
|
print(pos[:,0])
|
2014-01-16 06:48:13 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
[-0.17461758 0.88567056 0.38384886]
|
|
|
|
|
|
2020-06-16 14:07:01 +02:00
|
|
|
|
You can combine a Python ``for`` loop with Python’s ``zip()`` builtin
|
2020-06-16 15:52:50 +02:00
|
|
|
|
to print each time alongside the corresponding coordinates.
|
|
|
|
|
There are two techniques,
|
|
|
|
|
one of which is less efficient and the other more efficient.
|
2020-06-16 14:07:01 +02:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2020-06-16 15:52:50 +02:00
|
|
|
|
# Less efficient: loop over `t`, forcing the creation of
|
|
|
|
|
# a separate `Time` object for each iteration of the loop.
|
|
|
|
|
|
2020-06-16 14:07:01 +02:00
|
|
|
|
for ti, xi, yi, zi in zip(t, x, y, z):
|
2020-06-16 15:52:50 +02:00
|
|
|
|
print('{} x = {:.2f} y = {:.2f} z = {:.2f}'.format(
|
|
|
|
|
ti.utc_strftime('%Y-%m-%d'), xi, yi, zi,
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
2014-01-01 x = -0.17 y = 0.89 z = 0.38
|
|
|
|
|
2014-01-02 x = -0.19 y = 0.88 z = 0.38
|
|
|
|
|
2014-01-03 x = -0.21 y = 0.88 z = 0.38
|
|
|
|
|
2014-01-04 x = -0.23 y = 0.88 z = 0.38
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
# More efficient: loop over the output of a `Time` method,
|
|
|
|
|
# which returns an array of the same length as `t`.
|
|
|
|
|
|
|
|
|
|
t_strings = t.utc_strftime('%Y-%m-%d')
|
|
|
|
|
|
|
|
|
|
for tstr, xi, yi, zi in zip(t_strings, x, y, z):
|
|
|
|
|
print('{} x = {:.2f} y = {:.2f} z = {:.2f}'.format(
|
|
|
|
|
tstr, xi, yi, zi,
|
|
|
|
|
))
|
2020-06-16 14:07:01 +02:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
2014-01-01 x = -0.17 y = 0.89 z = 0.38
|
|
|
|
|
2014-01-02 x = -0.19 y = 0.88 z = 0.38
|
|
|
|
|
2014-01-03 x = -0.21 y = 0.88 z = 0.38
|
|
|
|
|
2014-01-04 x = -0.23 y = 0.88 z = 0.38
|
|
|
|
|
|
2014-01-19 16:28:05 +01:00
|
|
|
|
Finally, converting an array Julian date back into a calendar tuple
|
|
|
|
|
results in the year, month, and all of the other values
|
|
|
|
|
being as deep as the array itself:
|
2014-01-16 05:23:56 +01:00
|
|
|
|
|
2014-01-19 16:28:05 +01:00
|
|
|
|
.. testcode::
|
|
|
|
|
|
2020-05-24 21:05:32 +02:00
|
|
|
|
print(t.utc)
|
2014-01-19 16:28:05 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
2020-05-24 21:05:32 +02:00
|
|
|
|
[[2014. 2014. 2014. 2014.]
|
|
|
|
|
[ 1. 1. 1. 1.]
|
|
|
|
|
[ 1. 2. 3. 4.]
|
|
|
|
|
[ 0. 0. 0. 0.]
|
|
|
|
|
[ 0. 0. 0. 0.]
|
|
|
|
|
[ 0. 0. 0. 0.]]
|
2014-01-16 08:41:15 +01:00
|
|
|
|
|
2014-01-19 16:28:05 +01:00
|
|
|
|
Again, simply slice across the second dimension of the array
|
|
|
|
|
to pull a particular calendar tuple out of the larger result:
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
print(t.utc[:,2])
|
2014-01-19 16:28:05 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
2014-01-16 08:41:15 +01:00
|
|
|
|
|
2018-04-16 00:46:29 +02:00
|
|
|
|
[2014. 1. 3. 0. 0. 0.]
|
2014-01-16 08:41:15 +01:00
|
|
|
|
|
2020-05-24 21:05:32 +02:00
|
|
|
|
The rows can be fetched not only by index
|
|
|
|
|
but also through the attribute names ``year``, ``month``, ``day``,
|
|
|
|
|
``hour``, ``minute``, and ``second``.
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
print(t.utc.year)
|
|
|
|
|
print(t.utc.month)
|
|
|
|
|
print(t.utc.day)
|
|
|
|
|
print(t.utc.hour)
|
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
[2014. 2014. 2014. 2014.]
|
|
|
|
|
[1. 1. 1. 1.]
|
|
|
|
|
[1. 2. 3. 4.]
|
|
|
|
|
[0. 0. 0. 0.]
|
|
|
|
|
|
2014-01-19 16:28:05 +01:00
|
|
|
|
.. _tai-tt-tdb:
|
2014-01-16 08:41:15 +01:00
|
|
|
|
|
2014-01-19 17:26:51 +01:00
|
|
|
|
Uniform time scales: TAI, TT, and TDB
|
|
|
|
|
=====================================
|
|
|
|
|
|
|
|
|
|
Date arithmetic becomes very simple
|
|
|
|
|
as we leave UTC behind and consider completely uniform time scales.
|
2016-06-08 06:55:24 +02:00
|
|
|
|
Days are always 24 hours, hours always 60 minutes,
|
|
|
|
|
and minutes always 60 seconds without any variation or exceptions.
|
2014-01-19 17:26:51 +01:00
|
|
|
|
Such time scales are not appropriate for your morning alarm clock
|
2017-02-27 04:38:03 +01:00
|
|
|
|
because they are never delayed or adjusted
|
2014-01-19 17:26:51 +01:00
|
|
|
|
to stay in sync with the slowing rotation of the earth.
|
|
|
|
|
But that is what makes them useful for astronomical calculation —
|
|
|
|
|
because physics keeps up its dance,
|
|
|
|
|
and the stars and planets move in their courses,
|
|
|
|
|
whether humanity pauses to observe a UTC leap second or not.
|
|
|
|
|
|
|
|
|
|
Because they make every day the same length,
|
|
|
|
|
uniform time scales can express dates
|
|
|
|
|
as a simple floating-point count of days elapsed.
|
|
|
|
|
To make all historical dates come out as positive numbers,
|
2017-02-27 04:38:03 +01:00
|
|
|
|
astronomers traditionally assign each date a “Julian day” number
|
2020-06-07 17:12:11 +02:00
|
|
|
|
that starts counting at 4713 BC January 1 in the old Julian calendar —
|
|
|
|
|
the same date as 4714 BC November 24 in our Gregorian calendar.
|
2017-02-27 04:38:03 +01:00
|
|
|
|
Following a tradition going back to the Greeks and Ptolemy,
|
|
|
|
|
the count starts at noon,
|
2014-01-19 17:26:51 +01:00
|
|
|
|
since the sun’s transit is an observable event
|
|
|
|
|
but the moment of midnight is not.
|
|
|
|
|
|
|
|
|
|
So twelve noon was the moment of Julian date zero:
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2015-11-02 06:17:58 +01:00
|
|
|
|
# When was Julian date zero?
|
|
|
|
|
|
2014-01-19 17:26:51 +01:00
|
|
|
|
bc_4714 = -4713
|
2016-03-24 05:25:03 +01:00
|
|
|
|
t = ts.tt(bc_4714, 11, 24, 12)
|
|
|
|
|
print(t.tt)
|
2014-01-19 17:26:51 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
0.0
|
|
|
|
|
|
|
|
|
|
Did you notice how negative years work?
|
|
|
|
|
People still counted by starting at one, not zero,
|
|
|
|
|
when the scholar Dionysius Exiguus created the eras BC and AD
|
|
|
|
|
in around the year AD 500.
|
2020-06-07 17:12:11 +02:00
|
|
|
|
So his scheme has 1 BC followed immediately by AD 1 without a break.
|
2014-01-19 17:26:51 +01:00
|
|
|
|
To avoid an off-by-one error,
|
|
|
|
|
astronomers usually ignore BC and count backwards through a year zero
|
|
|
|
|
and on into negative years.
|
2017-02-27 04:38:03 +01:00
|
|
|
|
So negative year *−n* is what might otherwise be called
|
|
|
|
|
either “*n+1* BC” or “*n+1* BCE” in a history textbook.
|
2014-01-19 17:26:51 +01:00
|
|
|
|
|
2017-02-27 04:38:03 +01:00
|
|
|
|
More than two million days have passed since 4714 BC,
|
2014-01-19 17:26:51 +01:00
|
|
|
|
so modern dates tend to be rather large numbers:
|
2014-01-16 08:41:15 +01:00
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2015-11-02 06:17:58 +01:00
|
|
|
|
# 2014 January 1 as a Julian Date
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
t = ts.utc(2014, 1, 1)
|
|
|
|
|
print('TAI = %r' % t.tai)
|
|
|
|
|
print('TT = %r' % t.tt)
|
|
|
|
|
print('TDB = %r' % t.tdb)
|
2014-01-16 08:41:15 +01:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
TAI = 2456658.5004050927
|
2020-05-10 19:19:47 +02:00
|
|
|
|
TT = 2456658.5007775924
|
2020-05-11 00:45:32 +02:00
|
|
|
|
TDB = 2456658.500777592
|
2014-01-19 17:26:51 +01:00
|
|
|
|
|
2014-06-11 20:51:35 +02:00
|
|
|
|
What are these three different uniform time scales?
|
2014-01-19 17:26:51 +01:00
|
|
|
|
|
|
|
|
|
International Atomic Time (TAI) is maintained
|
|
|
|
|
by the worldwide network of atomic clocks
|
|
|
|
|
referenced by researchers with a need for very accurate time.
|
|
|
|
|
The official leap second table
|
|
|
|
|
is actually a table of offsets between TAI and UTC.
|
|
|
|
|
At the end of June 2012, for example,
|
|
|
|
|
the TAI−UTC offset was changed from 34.0 to 35.0
|
2014-06-11 20:51:35 +02:00
|
|
|
|
which is what generated the leap second in UTC.
|
2014-01-19 17:26:51 +01:00
|
|
|
|
|
2014-01-26 20:44:07 +01:00
|
|
|
|
Terrestrial Time (TT) differs from TAI
|
2014-06-11 20:51:35 +02:00
|
|
|
|
only because astronomers
|
|
|
|
|
were already maintaining a uniform time scale of their own
|
2014-01-26 20:44:07 +01:00
|
|
|
|
before TAI was established,
|
2014-01-19 17:26:51 +01:00
|
|
|
|
using a slightly different starting point for the day.
|
|
|
|
|
For practical purposes, TT is simply TAI
|
|
|
|
|
plus exactly 32.184 seconds.
|
|
|
|
|
So it is now more than a minute ahead of UTC.
|
|
|
|
|
|
|
|
|
|
Barycentric Dynamical Time (TDB) runs at approximately the rate
|
2016-06-08 06:55:24 +02:00
|
|
|
|
that an atomic clock would run
|
|
|
|
|
if it were at rest with respect to the Solar System barycenter,
|
|
|
|
|
and therefore unaffected by the Earth’s motion.
|
2014-01-19 17:26:51 +01:00
|
|
|
|
The acceleration that Earth experiences in its orbit —
|
|
|
|
|
sometimes speeding up, sometimes slowing down —
|
|
|
|
|
varies the rate at which our atomic clocks
|
|
|
|
|
seem to run to an outside observer,
|
|
|
|
|
as predicted by Einstein’s theory of General Relativity.
|
|
|
|
|
So physical simulations of the Solar System tend to use TDB,
|
2016-03-26 23:43:43 +01:00
|
|
|
|
which is continuous with the *T*\ :sub:`eph` time scale
|
2014-01-19 17:26:51 +01:00
|
|
|
|
traditionally used for Solar System and spacecraft simulations
|
|
|
|
|
at the Jet Propulsion Laboratory.
|
2014-01-16 08:41:15 +01:00
|
|
|
|
|
2020-06-07 23:43:17 +02:00
|
|
|
|
UT1 and ∆T
|
2014-01-16 08:41:15 +01:00
|
|
|
|
==========
|
|
|
|
|
|
2014-06-11 20:51:35 +02:00
|
|
|
|
Finally, UT1 is the least uniform time scale of all
|
2014-01-19 18:23:18 +01:00
|
|
|
|
because its clock cannot be housed in a laboratory,
|
|
|
|
|
nor is its rate established by any human convention.
|
|
|
|
|
It is, rather, the clock
|
|
|
|
|
whose “hand” is the rotation of the Earth itself!
|
|
|
|
|
|
|
|
|
|
The UT1 time is derived from the direction
|
|
|
|
|
that the Earth happens to be pointing at any given moment.
|
2014-06-11 20:51:35 +02:00
|
|
|
|
And the Earth is a young world
|
2020-06-07 17:12:11 +02:00
|
|
|
|
with a still-molten iron core, a viscous mantle,
|
2014-06-11 20:51:35 +02:00
|
|
|
|
and continents that rise and fall
|
2020-06-07 17:12:11 +02:00
|
|
|
|
as each passing ice age weighs them down with ice and then melts away.
|
2014-01-19 18:23:18 +01:00
|
|
|
|
We think that we can predict, with high accuracy,
|
|
|
|
|
where the planets will be in their orbits
|
|
|
|
|
thousands of years from now.
|
2014-06-11 20:51:35 +02:00
|
|
|
|
But to predict the fluid dynamics of an elastic rotating ellipsoid
|
|
|
|
|
is, at the moment, beyond us.
|
2017-02-27 04:38:03 +01:00
|
|
|
|
We cannot, for example, run a simulation or formula
|
|
|
|
|
to predict leap seconds more than a few months ahead of time!
|
2014-06-11 20:51:35 +02:00
|
|
|
|
Instead, we simply have to watch with sensitive instruments
|
2014-01-19 18:23:18 +01:00
|
|
|
|
to see what the Earth will do next.
|
|
|
|
|
|
|
|
|
|
If you are interested in the Earth as a dynamic body,
|
|
|
|
|
visit the `Long-term Delta T
|
|
|
|
|
<http://www.usno.navy.mil/USNO/earth-orientation/eo-products/long-term>`_
|
|
|
|
|
page provided by the United States Naval Observatory.
|
|
|
|
|
You will find graphs and tables
|
|
|
|
|
showing how the length of Earth’s day
|
|
|
|
|
expands and contracts by milliseconds over the decades.
|
|
|
|
|
The accumulated error at any given moment
|
2020-06-07 23:43:17 +02:00
|
|
|
|
is provided as ∆T,
|
2014-01-19 18:23:18 +01:00
|
|
|
|
the evolving difference between TT and UT1
|
|
|
|
|
that dropped below zero in 1871 but then rose past it in 1902
|
2014-06-11 20:51:35 +02:00
|
|
|
|
and now stands at more than +67.2 seconds.
|
2014-01-19 18:23:18 +01:00
|
|
|
|
|
|
|
|
|
The task of governing leap seconds can be stated, then,
|
|
|
|
|
as the task of keeping the difference between TT and UTC
|
2020-06-07 23:43:17 +02:00
|
|
|
|
close to the natural value ∆T out in the wild.
|
2014-01-19 18:23:18 +01:00
|
|
|
|
The standards bodies promise, in fact,
|
2014-06-11 20:51:35 +02:00
|
|
|
|
that the difference between these two artificial time scales
|
2020-06-07 23:43:17 +02:00
|
|
|
|
will always be within 0.9 seconds of the observed ∆T value.
|
2014-01-19 18:23:18 +01:00
|
|
|
|
|
|
|
|
|
In calculations that do not involve Earth’s rotation,
|
2020-06-07 23:43:17 +02:00
|
|
|
|
∆T never arises.
|
2014-01-19 18:23:18 +01:00
|
|
|
|
The positions of planets,
|
|
|
|
|
the distance to the Moon,
|
|
|
|
|
and the movement of a comet or asteroid
|
2020-06-07 23:43:17 +02:00
|
|
|
|
all ignore ∆T completely.
|
|
|
|
|
When, then, does ∆T come into play?
|
2014-01-19 18:23:18 +01:00
|
|
|
|
|
2020-06-07 23:43:17 +02:00
|
|
|
|
* ∆T is used when you specify your geographic location
|
2020-06-07 17:12:11 +02:00
|
|
|
|
as a :class:`~skyfield.toposlib.Topos`
|
|
|
|
|
and Skyfield needs to compute its location at a given date and time.
|
2014-01-19 18:23:18 +01:00
|
|
|
|
|
2020-06-07 23:43:17 +02:00
|
|
|
|
* ∆T is needed to determine directions
|
2014-01-19 18:23:18 +01:00
|
|
|
|
like “up,” “north,” and “east” when you want Skyfield
|
|
|
|
|
to compute the altitude and azimuth of an object
|
|
|
|
|
in your local sky.
|
|
|
|
|
|
2020-06-07 23:43:17 +02:00
|
|
|
|
* ∆T determines the Earth orientation for Skyfield
|
2014-06-11 20:51:35 +02:00
|
|
|
|
when an Earth satellite position generated from TLE elements
|
2014-01-19 18:23:18 +01:00
|
|
|
|
gets translated into a full Solar System position.
|
|
|
|
|
|
2016-03-24 05:25:03 +01:00
|
|
|
|
When you create your ``ts`` timescale object
|
|
|
|
|
at the beginning of your program,
|
|
|
|
|
Skyfield downloads up-to-date ``deltat.data`` and ``deltat.preds`` files
|
|
|
|
|
(if they are not already downloaded)
|
|
|
|
|
from the United States Naval Observatory.
|
|
|
|
|
These provide sub-millisecond level measurements
|
|
|
|
|
of the direction that the Earth is pointing,
|
|
|
|
|
allowing Skyfield to make
|
|
|
|
|
|
|
|
|
|
When you ask about dates in the far future or past,
|
|
|
|
|
Skyfield will run off the end of its tables
|
|
|
|
|
and will instead use the formula of Morrison and Stephenson (2004)
|
|
|
|
|
to estimate when day and night might have occurred in that era.
|
2014-01-19 18:23:18 +01:00
|
|
|
|
|
2020-06-07 23:43:17 +02:00
|
|
|
|
Setting a Custom Value For ∆T
|
2017-02-27 04:38:03 +01:00
|
|
|
|
=============================
|
|
|
|
|
|
2020-06-07 23:43:17 +02:00
|
|
|
|
If you ever want to specify your own value for ∆T,
|
2017-02-27 04:38:03 +01:00
|
|
|
|
then provide a ``delta_t`` keyword argument
|
|
|
|
|
when creating your timescale:
|
|
|
|
|
|
2014-01-19 18:23:18 +01:00
|
|
|
|
.. testcode::
|
|
|
|
|
|
2016-03-21 02:39:15 +01:00
|
|
|
|
load.timescale(delta_t=67.2810).utc((2014, 1, 1))
|
2014-01-19 18:23:18 +01:00
|
|
|
|
|
2018-05-19 23:01:19 +02:00
|
|
|
|
.. _time-precision:
|
|
|
|
|
|
|
|
|
|
Time precision is around ~20.1 µs
|
|
|
|
|
=================================
|
|
|
|
|
|
|
|
|
|
Skyfield stores time vectors internally
|
|
|
|
|
as NumPy 64-bit floating point arrays of Julian times.
|
|
|
|
|
As explained in the United States Naval Observatory’s
|
|
|
|
|
`AA Technical Note 2011-02,
|
|
|
|
|
“The Error in the Double Precision Representation of Julian Dates,”
|
|
|
|
|
<http://aa.usno.navy.mil/software/novas/USNOAA-TN2011-02.pdf>`_
|
|
|
|
|
this provides fairly high precision:
|
|
|
|
|
|
|
|
|
|
“An evaluation of the error associated with representing Julian
|
|
|
|
|
dates in IEEE 754 double precision floating-point numbers
|
|
|
|
|
demonstrates that Julian dates near the current epoch can be
|
|
|
|
|
represented to a precision not worse than 20.1 microseconds.”
|
|
|
|
|
|
|
|
|
|
Skyfield’s own routines for turning time into strings
|
|
|
|
|
do careful enough rounding that you should never see effects that small.
|
|
|
|
|
For example, Skyfield renders the seconds of this time
|
|
|
|
|
attractively all the way down to 4 decimal places:
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
|
|
|
|
t = ts.utc(2014, 1, 18, 1, 35, 37)
|
|
|
|
|
print(t.utc_jpl())
|
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
|
|
|
|
A.D. 2014-Jan-18 01:35:37.0000 UT
|
|
|
|
|
|
|
|
|
|
It’s only if you accidentally let Python print out a raw floating point value
|
|
|
|
|
that you’ll see the limit of the precision:
|
|
|
|
|
|
|
|
|
|
.. testcode::
|
|
|
|
|
|
2020-05-24 21:05:32 +02:00
|
|
|
|
print(t.utc.hour, t.utc.minute, t.utc.second)
|
2018-05-19 23:01:19 +02:00
|
|
|
|
|
|
|
|
|
.. testoutput::
|
|
|
|
|
|
2020-05-24 21:05:32 +02:00
|
|
|
|
1 35 36.999999999991815
|
2018-05-19 23:01:19 +02:00
|
|
|
|
|
|
|
|
|
To avoid ugly output like this,
|
|
|
|
|
you should use Skyfield’s own time display methods
|
|
|
|
|
like :meth:`~skyfield.timelib.Time.utc_iso()`
|
|
|
|
|
and :meth:`~skyfield.timelib.Time.utc_jpl()`
|
|
|
|
|
or those of the ``datetime`` that Skyfield returns
|
|
|
|
|
from :meth:`~skyfield.timelib.Time.utc_datetime()`
|
|
|
|
|
when printing dates to the screen.
|
|
|
|
|
|
2014-01-17 06:25:58 +01:00
|
|
|
|
.. _date-cache:
|
|
|
|
|
|
2020-06-07 17:12:11 +02:00
|
|
|
|
Values cached on the Time object
|
|
|
|
|
================================
|
2014-01-17 06:25:58 +01:00
|
|
|
|
|
2016-03-23 06:07:19 +01:00
|
|
|
|
When you create a :class:`Time`
|
2014-01-19 23:00:33 +01:00
|
|
|
|
it goes ahead and computes its ``tt`` Terrestrial Time attribute
|
|
|
|
|
starting from whatever time argument you provide.
|
|
|
|
|
If you provide the ``utc`` parameter, for example,
|
2017-02-27 04:38:03 +01:00
|
|
|
|
then the date first computes and sets ``tai``
|
|
|
|
|
and then computes and sets ``tt``.
|
2014-01-19 23:00:33 +01:00
|
|
|
|
Each of the other time attributes only gets computed once,
|
|
|
|
|
the first time you access it.
|
|
|
|
|
|
|
|
|
|
The general rule is that attributes are only computed once,
|
|
|
|
|
and can be accessed again and again for free,
|
|
|
|
|
while methods never cache their results —
|
|
|
|
|
think of the ``()`` parentheses after a method name
|
|
|
|
|
as your reminder that “this will do a fresh computation every time.”
|
|
|
|
|
|
|
|
|
|
In addition to time scales,
|
2020-06-07 17:12:11 +02:00
|
|
|
|
each :class:`Time` object caches several other quantities
|
|
|
|
|
that are often needed in astronomy.
|
|
|
|
|
Skyfield only computes these attributes on-demand,
|
|
|
|
|
the first time the user tries to access them
|
|
|
|
|
or invokes a computation that needs their value:
|
2014-01-19 23:00:33 +01:00
|
|
|
|
|
2015-01-26 20:51:30 +01:00
|
|
|
|
``gmst``
|
2017-08-21 17:57:30 +02:00
|
|
|
|
Greenwich Mean Sidereal Time in hours,
|
2017-08-21 18:06:15 +02:00
|
|
|
|
in the range 0.0 ≤ ``gmst`` < 24.0.
|
2015-01-26 20:51:30 +01:00
|
|
|
|
|
|
|
|
|
``gast``
|
2017-08-21 17:57:30 +02:00
|
|
|
|
Greenwich Apparent Sidereal Time in hours,
|
2017-08-21 18:06:15 +02:00
|
|
|
|
in the range 0.0 ≤ ``gast`` < 24.0.
|
2015-01-26 20:51:30 +01:00
|
|
|
|
|
2020-06-07 17:12:11 +02:00
|
|
|
|
``M``, ``MT``
|
|
|
|
|
This 3×3 matrix and its inverse
|
|
|
|
|
perform the complete rotation between a vector in the ICRF
|
|
|
|
|
and a vector in the dynamical reference system of this Julian date.
|
2014-01-19 23:00:33 +01:00
|
|
|
|
|
2020-06-07 17:12:11 +02:00
|
|
|
|
``C``, ``CT``
|
|
|
|
|
This 3×3 matrix and its inverse
|
|
|
|
|
perform the complete rotation between a vector in the ICRF
|
2018-09-05 22:55:37 +02:00
|
|
|
|
and a vector in the celestial intermediate reference system (CIRS) of
|
|
|
|
|
this Julian date.
|
|
|
|
|
|
2017-02-27 04:38:03 +01:00
|
|
|
|
You will typically never need to access these matrices yourself,
|
2020-06-15 19:45:35 +02:00
|
|
|
|
as they are used automatically
|
|
|
|
|
by the :meth:`~skyfield.positionlib.ICRF.radec()`
|
2014-01-19 23:00:33 +01:00
|
|
|
|
method when you use its ``epoch=`` parameter
|
|
|
|
|
to ask for a right ascension and declination
|
2020-06-07 17:12:11 +02:00
|
|
|
|
in the dynamical reference system,
|
|
|
|
|
and when you ask a :class:`~skyfield.toposlib.Topos` object
|
|
|
|
|
for its position.
|
2014-01-17 06:25:58 +01:00
|
|
|
|
|
2014-01-16 06:48:13 +01:00
|
|
|
|
.. _matplotlib: http://matplotlib.org/
|
2014-01-19 06:00:29 +01:00
|
|
|
|
.. _pytz: http://pytz.sourceforge.net/
|
2014-06-11 20:38:11 +02:00
|
|
|
|
.. _requirements.txt: https://pip.pypa.io/en/latest/user_guide.html#requirements-files
|
2015-10-19 03:49:29 +02:00
|
|
|
|
|
2015-10-19 06:23:42 +02:00
|
|
|
|
.. testcleanup::
|
2015-10-19 03:49:29 +02:00
|
|
|
|
|
2015-10-19 06:23:42 +02:00
|
|
|
|
__import__('skyfield.tests.fixes').tests.fixes.teardown()
|