Hello community,
here is the log from the commit of package python-skyfield for openSUSE:Factory
checked in at 2020-09-27 11:50:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-skyfield (Old)
and /work/SRC/openSUSE:Factory/.python-skyfield.new.4249 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-skyfield"
Sun Sep 27 11:50:11 2020 rev:7 rq:837884 version:1.29
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-skyfield/python-skyfield.changes
2020-09-16 19:40:35.202883394 +0200
+++
/work/SRC/openSUSE:Factory/.python-skyfield.new.4249/python-skyfield.changes
2020-09-27 11:50:15.216074287 +0200
@@ -1,0 +2,32 @@
+Sat Sep 26 08:13:08 UTC 2020 - Benjamin Greiner <[email protected]>
+
+- Update to version 1.29
+ * Fix: the new Julian calendar feature was raising an
+ exception in the calendar methods like
+ `skyfield.timelib.Time.tt_calendar()` if the time
+ object was in fact an array of times. #450
+ * Fix: trying to iterate over a time object would raise an
+ exception if the time was created through
+ `~skyfield.timelib.Timescale.ut1()`.
+- Version 1.28
+ * Broken URL: Because the VizieR archive apparently decided
+ to uncompress their copy of the hip_main.dat.gz Hipparcos
+ catalog file, the old URL now returns a 404 error. As an
+ emergency fix, this version of Skyfield switches to their
+ uncompressed hip_main.dat. Hopefully they don’t compress
+ it again and break the new URL! A more permanent solution
+ is discussed at: #454
+ * To unblock this release, removed a few deprecated pre-1.0
+ experiments from April 2015 in skyfield.hipparcos and
+ skyfield.named_stars that broke because the Hipparcos
+ catalog is no longer compressed; hopefully no one was
+ using them.
+ * In a sweeping internal change, the
+ `~skyfield.timelib.Timescale` and
+ `~skyfield.timelib.Time` objects now offer support
+ for the Julian calendar that’s used by historians for
+ dates preceding the adoption of the Gregorian calendar in
+ 1582. See choice of calendars if you want to turn on
+ Julian dates in your application. #450
+
+-------------------------------------------------------------------
Old:
----
skyfield-1.27.tar.gz
New:
----
skyfield-1.29.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-skyfield.spec ++++++
--- /var/tmp/diff_new_pack.M4aOdM/_old 2020-09-27 11:50:16.416075580 +0200
+++ /var/tmp/diff_new_pack.M4aOdM/_new 2020-09-27 11:50:16.416075580 +0200
@@ -21,7 +21,7 @@
%define assayver 256.23c18c2
%define skip_python2 1
Name: python-skyfield
-Version: 1.27
+Version: 1.29
Release: 0
Summary: Elegant astronomy for Python
License: MIT
++++++ skyfield-1.27.tar.gz -> skyfield-1.29.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/PKG-INFO new/skyfield-1.29/PKG-INFO
--- old/skyfield-1.27/PKG-INFO 2020-09-15 20:39:15.000000000 +0200
+++ new/skyfield-1.29/PKG-INFO 2020-09-26 03:01:08.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: skyfield
-Version: 1.27
+Version: 1.29
Summary: Elegant astronomy for Python
Home-page: http://github.com/brandon-rhodes/python-skyfield/
Author: Brandon Rhodes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/__init__.py
new/skyfield-1.29/skyfield/__init__.py
--- old/skyfield-1.27/skyfield/__init__.py 2020-09-15 20:35:41.000000000
+0200
+++ new/skyfield-1.29/skyfield/__init__.py 2020-09-26 02:58:57.000000000
+0200
@@ -5,5 +5,5 @@
the source code, as well as the http://rhodesmill.org/skyfield/ site!
"""
-VERSION = (1, 27)
+VERSION = (1, 29)
__version__ = '.'.join(map(str, VERSION))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/api.py
new/skyfield-1.29/skyfield/api.py
--- old/skyfield-1.27/skyfield/api.py 2020-08-29 20:22:29.000000000 +0200
+++ new/skyfield-1.29/skyfield/api.py 2020-09-24 18:56:04.000000000 +0200
@@ -15,18 +15,23 @@
from .positionlib import position_from_radec, position_of_radec
from .starlib import Star
from .sgp4lib import EarthSatellite
-from .timelib import Time, Timescale, utc
+from .timelib import (
+ GREGORIAN_START, GREGORIAN_START_ENGLAND, Time, Timescale, utc
+)
from .toposlib import Topos
from .units import Angle, Distance
-from .named_stars import NamedStar
load = Loader('.')
-__all__ = ['Angle', 'B1950', 'Distance', 'EarthSatellite', 'Loader',
- 'NamedStar', 'PlanetaryConstants', 'Star', 'T0', 'Time',
- 'Timescale', 'Topos', 'datetime', 'load', 'load_constellation_map',
- 'load_file', 'position_from_radec', 'position_of_radec',
- 'utc', 'pi', 'tau']
+__all__ = [
+ 'Angle', 'B1950', 'Distance', 'EarthSatellite',
+ 'GREGORIAN_START', 'GREGORIAN_START_ENGLAND',
+ 'Loader', 'PlanetaryConstants', 'Star',
+ 'T0', 'Time', 'Timescale', 'Topos',
+ 'datetime', 'load', 'load_constellation_map',
+ 'load_file', 'position_from_radec', 'position_of_radec',
+ 'utc', 'pi', 'tau',
+]
# An attempt at friendliest-possible deprecations:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/data/hipparcos.py
new/skyfield-1.29/skyfield/data/hipparcos.py
--- old/skyfield-1.27/skyfield/data/hipparcos.py 2020-08-29
20:22:29.000000000 +0200
+++ new/skyfield-1.29/skyfield/data/hipparcos.py 2020-09-24
19:00:36.000000000 +0200
@@ -1,38 +1,20 @@
-import gzip
-from skyfield.functions import to_spherical
-from skyfield.starlib import Star
-from skyfield.timelib import T0
-from skyfield.units import Angle
+# This URL worked until September 2020:
-days = T0 - 2448349.0625
-URL = 'http://cdsarc.u-strasbg.fr/ftp/cats/I/239/hip_main.dat.gz'
-url = URL # old name, in case anyone used it
+#URL = 'http://cdsarc.u-strasbg.fr/ftp/cats/I/239/hip_main.dat.gz'
+
+# Then someone at VizieR apparently ran `gunzip` on the file, breaking
+# the existing URL. The fastest fix is for us to switch to:
-def parse(line):
- "DEPRECATED; see :func:`~skyfield.data.hipparcos.load_dataframe() instead."
- # See ftp://cdsarc.u-strasbg.fr/cats/I/239/ReadMe
- star = Star(
- ra=Angle(degrees=float(line[51:63])),
- dec=Angle(degrees=float(line[64:76])),
- ra_mas_per_year=float(line[87:95]),
- dec_mas_per_year=float(line[96:104]),
- parallax_mas=float(line[79:86]),
- names=[('HIP', int(line[8:14]))],
- )
- star._position_au += star._velocity_au_per_d * days
- distance, dec, ra = to_spherical(star._position_au)
- star.ra = Angle(radians=ra, preference='hours')
- star.dec = Angle(radians=dec)
- return star
-
-def load(match_function):
- "DEPRECATED; see :func:`~skyfield.data.hipparcos.load_dataframe() instead."
- from skyfield import api
-
- with api.load.open(url) as f:
- for line in gzip.GzipFile(fileobj=f):
- if match_function(line):
- yield parse(line)
+URL = 'https://cdsarc.u-strasbg.fr/ftp/cats/I/239/hip_main.dat'
+
+# But what if someone runs `gzip` on the file again? Then the new URL
+# will break like the old one did. It appears that VizieR makes no
+# guarantee that raw catalog URLs are stable, and that we need to switch
+# to one of their catalog generation URLs, which produce text in a new
+# format that Skyfield will have to learn. Discussion at:
+# https://github.com/skyfielders/python-skyfield/issues/454
+
+url = URL # old name, in case anyone used it
PANDAS_MESSAGE = """Skyfield needs Pandas to load the Hipparcos catalog
@@ -56,7 +38,7 @@
'CPD', '(V-I)red', 'SpType', 'r_SpType',
)
-def load_dataframe(fobj, compression='gzip'):
+def load_dataframe(fobj):
"""Given an open file for `hip_main.dat.gz`, return a parsed dataframe.
If your copy of ``hip_main.dat`` has already been unzipped, pass the
@@ -68,8 +50,13 @@
except ImportError:
raise ImportError(PANDAS_MESSAGE)
+ fobj.seek(0)
+ magic = fobj.read(2)
+ compression = 'gzip' if (magic == b'\x1f\x8b') else None
+ fobj.seek(0)
+
df = read_csv(
- fobj, sep='|', compression=compression, names=_COLUMN_NAMES,
+ fobj, sep='|', names=_COLUMN_NAMES, compression=compression,
usecols=['HIP', 'Vmag', 'RAdeg', 'DEdeg', 'Plx', 'pmRA', 'pmDE'],
na_values=[' ', ' ', ' ', ' '],
)
@@ -82,13 +69,3 @@
epoch_year = 1991.25,
)
return df.set_index('hip')
-
-def get(which):
- "DEPRECATED; see :func:`~skyfield.data.hipparcos.load_dataframe() instead."
- if isinstance(which, str):
- pattern = ('H| %6s' % which).encode('ascii')
- for star in load(lambda line: line.startswith(pattern)):
- return star
- else:
- patterns = set(id.encode('ascii').rjust(6) for id in which)
- return list(load(lambda line: line[8:14] in patterns))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/documentation/api-time.rst
new/skyfield-1.29/skyfield/documentation/api-time.rst
--- old/skyfield-1.27/skyfield/documentation/api-time.rst 2020-09-11
10:40:22.000000000 +0200
+++ new/skyfield-1.29/skyfield/documentation/api-time.rst 2020-09-24
17:55:31.000000000 +0200
@@ -8,6 +8,26 @@
and of how you can convert time to and from familiar time scales
like UTC and the worldwide time zones that are adapted from it.
+.. _calendar date:
+
+Calendar date
+=============
+
+1. When building a `Time` from a calendar date,
+ you can not only designate a single moment by its time and date,
+ but you can also build a `Time` representing a whole array of moments
+ by supplying a Python list or NumPy array
+ for either the year, month, day, hour, minute, or second.
+ See `date-arrays`.
+
+2. By default,
+ Skyfield uses the modern Gregorian calendar for all dates —
+ even dates before the Gregorian calendar was introduced in 1582.
+
+3. You can instead opt to use the old Julian calendar for ancient dates,
+ which is the most common practice among historians.
+ See `choice of calendars`.
+
Timescale, for building and converting times
============================================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/documentation/api.rst
new/skyfield-1.29/skyfield/documentation/api.rst
--- old/skyfield-1.27/skyfield/documentation/api.rst 2020-09-11
10:48:18.000000000 +0200
+++ new/skyfield-1.29/skyfield/documentation/api.rst 2020-09-24
17:34:58.000000000 +0200
@@ -39,34 +39,22 @@
Loader.timescale
Loader.tle_file
+.. _api-Timescale:
+
Time scales
===========
.. currentmodule:: skyfield.timelib
-A Skyfield `Timescale` object is typically built
-at the beginning of each program:
+A script will typically start by building a single Skyfield `Timescale`
+to use for all date and time conversions:
.. testcode::
from skyfield import api
ts = api.load.timescale()
-It downloads and parses the data tables necessary
-to correctly convert between Universal Time
-and the more stable time scales used by astronomers.
-
-If you want to skip downloading up-to-date time scale files,
-you can run:
-
-.. testcode::
-
- ts = api.load.timescale()
-
-This can avoid problems connecting
-to the servers from which the official files are distributed.
-Note that the time scale files distributed with
-any given version of Skyfield will fall gradually out of date.
+Its methods are:
.. autosummary::
@@ -85,6 +73,8 @@
Timescale.ut1_jd
Timescale.from_astropy
+.. _api-Time:
+
Time objects
============
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/documentation/time.rst
new/skyfield-1.29/skyfield/documentation/time.rst
--- old/skyfield-1.27/skyfield/documentation/time.rst 2020-08-27
16:38:03.000000000 +0200
+++ new/skyfield-1.29/skyfield/documentation/time.rst 2020-09-24
18:00:35.000000000 +0200
@@ -5,15 +5,13 @@
.. currentmodule:: skyfield.timelib
-Astronomers use several numerical scales to measure time.
-Skyfield often has to use more than one of them within a single computation!
-The `Time` class is used to represent a single moment in time,
-or an array of such moments.
-It keeps track of all the different ways the same moment
-might be designated in different time scales.
-It performs this conversion using data tables
-that are stored in the `Timescale` object
-that was used to create it:
+Astronomers use a variety of different scales to measure time.
+Skyfield often has to use several timescales within a single computation!
+The `Time` class is how Skyfield represents either a single moment in time
+or a whole array of moments,
+and keeps track of all of the different designations
+assigned to that moment
+by the various standard time scales:
.. testsetup::
@@ -24,74 +22,129 @@
.. testcode::
- # Building a time object
-
from skyfield.api import load
- ts = load.timescale() # Timescale object
- t = ts.utc(2014, 1, 18) # Time object
+ ts = load.timescale()
+ t = ts.tt(2000, 1, 1, 12, 0)
- print(t.tai)
- print(t.tt)
- print(t.tdb)
+ print('TT date and time: ', t.tt_strftime())
+ print('TAI date and time:', t.tai_strftime())
+ print('UTC date and time:', t.utc_strftime())
+ print('TDB Julian date: {:.10f}'.format(t.tdb))
+ print('Julian century: {:.1f}'.format(t.J))
.. testoutput::
- 2456675.5004050927
- 2456675.5007775924
- 2456675.5007775975
+ TT date and time: 2000-01-01 12:00:00 TT
+ TAI date and time: 2000-01-01 11:59:28 TAI
+ UTC date and time: 2000-01-01 11:58:56 UTC
+ TDB Julian date: 2451544.9999999991
+ Julian century: 2000.0
+The `Timescale` object returned by ``load.timescale()``
+manages the conversions between different time scales
+and is also how the programmer builds `Time` objects for specific dates.
Most applications create only one `Timescale` object,
-which is conventionally named ``ts``,
+which Skyfield programmers conventionally name ``ts``,
and use it to build all of their times.
-Each `Time` object only computes a given timescale
-the first time it’s asked.
-On subsequent requests for the same timescale,
-it returns the value that it cached the first time,
-without recomputing it.
-This has an important consequence:
-if your program will need to do several computations for the same time,
-be sure to use the same `Time` object for all of them.
-Otherwise your separate time objects
-will have to compute the same time scales all over again.
-See the :ref:`date-cache` section below for more details.
-
-Each time scale supported by :class:`Time`
-is described in detail in one of the sections below.
-The supported time scales are:
-
-* ``t.utc`` — Coordinated Universal Time (“Greenwich Time”)
-* ``t.ut1`` — Universal Time
-* ``t.tai`` — International Atomic Time
-* ``t.tt`` and ``t.J`` — Terrestrial Time
-* ``t.tdb`` — Barycentric Dynamical Time (the JPL’s *T*\ :sub:`eph`)
-
-You can build a `Time` from several different source timescales.
-Here’s a summary.
-
-::
-
- # All the ways you can create a Time object:
-
- t = ts.utc(year, month, day, hour, minute, second)
- t = ts.from_datetime(dt)
- t = ts.from_datetimes([dt1, dt2, ...])
- t = ts.now()
-
- t = ts.tai(year, month, day, hour, minute, second)
- t = ts.tai_jd(floating_point_Julian_date)
+For quick reference,
+here are the supported timescales:
- t = ts.tt(year, month, day, hour, minute, second)
- t = ts.tt_jd(floating_point_Julian_date)
- t = ts.J(floating_point_Julian_year)
-
- t = ts.tdb(year, month, day, hour, minute, second)
- t = ts.tdb_jd(floating_point_Julian_date)
-
- t = ts.ut1(year, month, day, hour, minute, second)
- t = ts.ut1_jd(floating_point_Julian_date)
-
-The meaning of each timescale is discussed in the sections below.
+* UTC — Coordinated Universal Time (“Greenwich Time”)
+* UT1 — Universal Time
+* TAI — International Atomic Time
+* TT — Terrestrial Time
+* TDB — Barycentric Dynamical Time (the JPL’s *T*\ :sub:`eph`)
+
+And here are links to the API documentation for time scales and times:
+
+* :ref:`api-Timescale`
+* :ref:`api-Time`
+
+.. _choice of calendars:
+
+Ancient and modern dates
+========================
+
+Skyfield normally uses the modern Gregorian calendar,
+even for dates in history before the Gregorian calendar’s adoption in 1582.
+This “proleptic” use of Gregorian dates
+makes date calculations simple,
+is compatible with Python’s ``datetime``,
+and is also the behavior of the United States Naval Observatory library
+on which many Skyfield routines were originally based.
+
+But the Gregorian calendar is awkward
+for historians and students of ancient astronomy,
+because the calendar in actual use before 1582
+was the old Julian calendar
+established by Julius Caesar’s calendar reform in 45 BC.
+The two calendars agree over the century
+between the leap day of AD 200 and the leap day of AD 300.
+But because the Julian calendar is not quite synchronized with the seasons,
+its dates run ahead of the Gregorian calendar before that century
+and run behind the Gregorian calendar after it.
+
+If you would like Skyfield
+to switch to the Julian calendar for historical dates —
+both when interpreting the dates you input,
+and when producing calendar dates as output —
+simply give your ``Timescale`` object
+the Julian date on which you would like the calendar to switch.
+
+.. testcode::
+
+ from skyfield.api import GREGORIAN_START
+
+ ts.julian_calendar_cutoff = GREGORIAN_START
+
+ t = ts.tt_jd(range(2299159, 2299163))
+ for s in t.tt_strftime():
+ print(s)
+
+.. testoutput::
+
+ 1582-10-03 12:00:00 TT
+ 1582-10-04 12:00:00 TT
+ 1582-10-15 12:00:00 TT
+ 1582-10-16 12:00:00 TT
+
+As you can see from these four successive days in history,
+Pope Gregory had the calendar jump
+from the Julian calendar date 1582 October 4
+to the Gregorian calendar date October 15 the next day
+in order to bring the date of Easter back into sync with the equinox.
+Skyfield provides two constants for popular cutoff dates:
+
+* ``GREGORIAN_START`` — Julian day 2299161,
+ on which the new Gregorian calendar went into effect in Rome.
+
+* ``GREGORIAN_START_ENGLAND`` — Julian day 2361222,
+ on which the new Gregorian calendar went into effect in England in 1752
+ (the reform having initially been rejected by the English bishops,
+ “Seeing that the Bishop of Rome is Antichrist,
+ therefore we may not communicate with him in any thing”).
+
+You are free to choose your own cutoff Julian date
+if you are studying astronomy records from a country
+that adopted the Gregorian calendar on some other date.
+Russia, for example, did not adopt it until the twentieth century.
+The default value, that always uses Gregorian dates, is ``None``:
+
+.. testcode::
+
+ ts.julian_calendar_cutoff = None
+
+Note that even the Julian calendar becomes anachronistic
+before its adoption in 45 BC,
+so all dates generated by Skyfield are “proleptic” before that date.
+And, of course, the Julian calendar
+was local to the civilization that ringed the Mediterranean.
+If you are interested in relating astronomical events
+to more ancient Roman calendars,
+or the calendars of other civilizations,
+try searching for a third-party Python package
+that supports the calendar you are interested in.
.. _building-dates:
@@ -378,37 +431,42 @@
===========
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
-that holds an entire array of dates,
-instead of building many separate :class:`Time` objects.
-There are three techniques for building arrays.
-
-* Provide ``ts.utc()`` with a Python list of ``datetime`` objects.
-
-* Provide ``tai()`` or ``tt()`` or ``tdb()`` or ``ut1()``
- with an entire NumPy array or Python list of floating point values.
+across a whole series of times and dates,
+then Skyfield will work most efficiently if,
+instead of building many separate :class:`Time` objects,
+you build a single :class:`Time` object that holds the entire array of dates.
+
+There are three techniques for building a `Time` array.
+
+* Provide :meth:`~Timescale.tai()` or :meth:`~Timescale.tt()`
+ or :meth:`~Timescale.tdb()` or :meth:`~Timescale.ut1()`
+ with a Python list or NumPy array of numbers
+ for one of the six components of the calendar date
+ (year, month, day, hour, minute, or second).
+
+* Provide :meth:`~Timescale.tai_jd()` or :meth:`~Timescale.tt_jd()`
+ or :meth:`~Timescale.tdb_jd()` or :meth:`~Timescale.ut1_jd()`
+ with a list or NumPy array of floating point numbers.
-* When specifying year, month, day, hour, minute, and second,
- make one of the values a list or array.
+* Provide :meth:`~Timescale.from_datetimes()`
+ with a Python list of ``datetime`` objects.
-The last possibility is generally the one that is the most fun,
+The first 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
+while holding the others constant.
+You are free to provide out-of-range values
and leave it to Skyfield to work out the correct result.
Here are some examples::
ts.utc(range(1900, 1950)) # Fifty years 1900–1949
- ts.utc(1980, range(1, 25)) # Twenty-four months
- ts.utc(2005, 5, [1, 10, 20]) # 1st, 10th, and 20th of May
+ ts.utc(1980, range(1, 25)) # 24 months of 1980 and 1981
+ ts.utc(2005, 5, [1, 11, 21]) # 1st, 11th, and 21st of May
- # The ten seconds crossing the 1974 leap second
+ # Negative values work too! Here are the
+ # ten seconds crossing the 1974 leap second.
ts.utc(1975, 1, 1, 0, 0, range(-5, 5))
-The resulting :class:`Time` object will hold an array of times
-instead of just a single scalar value.
+The resulting :class:`Time` object will hold an array of times.
As illustrated in the previous section (on leap seconds),
you can use a Python ``for`` to print each time separately:
@@ -416,8 +474,8 @@
t = ts.utc(2020, 6, 16, 7, range(4))
- for ti in t:
- print(ti.utc_strftime('%Y-%m-%d %H:%M'))
+ for s in t.utc_strftime('%Y-%m-%d %H:%M'):
+ print(s)
.. testoutput::
@@ -532,9 +590,9 @@
2014-01-03 x = -0.21 y = 0.88 z = 0.38
2014-01-04 x = -0.23 y = 0.88 z = 0.38
-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:
+Finally, converting an array `Time` back into a calendar tuple
+results in the year, month, day, hour, minute, and second
+each having the same dimension as the array itself:
.. testcode::
@@ -549,7 +607,7 @@
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
-Again, simply slice across the second dimension of the array
+Simply slice across the second dimension of the array
to pull a particular calendar tuple out of the larger result:
.. testcode::
@@ -560,7 +618,8 @@
[2014. 1. 3. 0. 0. 0.]
-The rows can be fetched not only by index
+Slicing in the other direction,
+the rows can be fetched not only by index
but also through the attribute names ``year``, ``month``, ``day``,
``hour``, ``minute``, and ``second``.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/named_stars.py
new/skyfield-1.29/skyfield/named_stars.py
--- old/skyfield-1.27/skyfield/named_stars.py 2020-08-29 20:22:29.000000000
+0200
+++ new/skyfield-1.29/skyfield/named_stars.py 2020-09-24 19:35:59.000000000
+0200
@@ -1,12 +1,9 @@
-# TODO: Deprecate; maybe even remove, since it's not documented?
-"""
-Convenience functions for users to get a Star instance using a small database
-of named stars.
-"""
-from .data import hipparcos
+# This list was seeded from:
+# https://en.wikipedia.org/wiki/List_of_stars_in_the_Hipparcos_Catalogue
+
+# Recent discussion:
+# https://github.com/skyfielders/python-skyfield/issues/304
-#This list was seeded from
-#https://en.wikipedia.org/wiki/List_of_stars_in_the_Hipparcos_Catalogue
named_star_dict= {
'Achernar': 7588,
'Acrux': 60718,
@@ -128,11 +125,3 @@
'Wei': 82396,
'Wezen': 34444
}
-
-def NamedStar(name):
- """DEPRECATED: See stars.rst for how to load a star catalog."""
- try:
- hid = named_star_dict[name]
- return hipparcos.get(str(hid))
- except KeyError:
- raise ValueError("No star named {0} known to skyfield.".format(name))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/sgp4lib.py
new/skyfield-1.29/skyfield/sgp4lib.py
--- old/skyfield-1.27/skyfield/sgp4lib.py 2020-09-11 10:47:16.000000000
+0200
+++ new/skyfield-1.29/skyfield/sgp4lib.py 2020-09-17 17:48:24.000000000
+0200
@@ -157,7 +157,8 @@
"""
sat = self.model
- jd = t._utc_float()
+ whole, fraction, is_leap_second = t._utc_float(0.0)
+ jd = whole + fraction
if getattr(jd, 'shape', None):
e, r, v = sat.sgp4_array(jd, zeros_like(jd))
messages = [SGP4_ERRORS[error] if error else None for error in e]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/tests/test_against_novas.py
new/skyfield-1.29/skyfield/tests/test_against_novas.py
--- old/skyfield-1.27/skyfield/tests/test_against_novas.py 2020-09-11
10:40:33.000000000 +0200
+++ new/skyfield-1.29/skyfield/tests/test_against_novas.py 2020-09-24
19:24:30.000000000 +0200
@@ -6,7 +6,7 @@
from skyfield.api import Topos, load
from skyfield.constants import AU_KM, AU_M
from skyfield.data import hipparcos
-from skyfield.functions import length_of
+from skyfield.functions import BytesIO, length_of
from .fixes import low_precision_ERA
OLD_AU_KM = 149597870.691 # TODO: load from de405
@@ -3105,46 +3105,41 @@
compare(az.degrees, (151.19707488767745, 338.13295291812307,
156.2971102404744, 191.29497427201525), 0.0005 * arcsecond)
def test_hipparcos_conversion0(earth):
- line = 'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
- star = hipparcos.parse(line)
- compare(star.ra.hours, 2.530301023497941, 0.001 * ra_arcsecond)
- compare(star.dec.degrees, 89.26410950742938, 0.001 * arcsecond)
+ line = b'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
+ df = hipparcos.load_dataframe(BytesIO(line))
+ star = starlib.Star.from_dataframe(df.iloc[0])
ra, dec, distance =
earth.at(load.timescale().tt_jd(2440423.345833333)).observe(star).radec()
compare(ra.hours, 2.5283697000528966, 0.00001 * ra_arcsecond)
compare(dec.degrees, 89.26420852419295, 0.00001 * arcsecond)
def test_hipparcos_conversion1(earth):
- line = 'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
- star = hipparcos.parse(line)
- compare(star.ra.hours, 2.530301023497941, 0.001 * ra_arcsecond)
- compare(star.dec.degrees, 89.26410950742938, 0.001 * arcsecond)
+ line = b'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
+ df = hipparcos.load_dataframe(BytesIO(line))
+ star = starlib.Star.from_dataframe(df.iloc[0])
ra, dec, distance =
earth.at(load.timescale().tt_jd(2448031.5)).observe(star).radec()
compare(ra.hours, 2.529691010447949, 0.00001 * ra_arcsecond)
compare(dec.degrees, 89.26413900274704, 0.00001 * arcsecond)
def test_hipparcos_conversion2(earth):
- line = 'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
- star = hipparcos.parse(line)
- compare(star.ra.hours, 2.530301023497941, 0.001 * ra_arcsecond)
- compare(star.dec.degrees, 89.26410950742938, 0.001 * arcsecond)
+ line = b'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
+ df = hipparcos.load_dataframe(BytesIO(line))
+ star = starlib.Star.from_dataframe(df.iloc[0])
ra, dec, distance =
earth.at(load.timescale().tt_jd(2451545.0)).observe(star).radec()
compare(ra.hours, 2.5302921836971946, 0.00001 * ra_arcsecond)
compare(dec.degrees, 89.26411033462212, 0.00001 * arcsecond)
def test_hipparcos_conversion3(earth):
- line = 'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
- star = hipparcos.parse(line)
- compare(star.ra.hours, 2.530301023497941, 0.001 * ra_arcsecond)
- compare(star.dec.degrees, 89.26410950742938, 0.001 * arcsecond)
+ line = b'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
+ df = hipparcos.load_dataframe(BytesIO(line))
+ star = starlib.Star.from_dataframe(df.iloc[0])
ra, dec, distance =
earth.at(load.timescale().tt_jd(2456164.5)).observe(star).radec()
compare(ra.hours, 2.5311170753257395, 0.00001 * ra_arcsecond)
compare(dec.degrees, 89.26406913848278, 0.00001 * arcsecond)
def test_hipparcos_conversion4(earth):
- line = 'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
- star = hipparcos.parse(line)
- compare(star.ra.hours, 2.530301023497941, 0.001 * ra_arcsecond)
- compare(star.dec.degrees, 89.26410950742938, 0.001 * arcsecond)
+ line = b'H| 11767| |02 31 47.08|+89 15 50.9|
1.97|1|H|037.94614689|+89.26413805| | 7.56| 44.22| -11.74| 0.39| 0.45|
0.48| 0.47| 0.55|-0.16| 0.05| 0.27|-0.01| 0.08| 0.05| 0.04|-0.12|-0.09|-0.36|
1| 1.22| 11767| 2.756|0.003| 2.067|0.003| | 0.636|0.003|T|0.70|0.00|L| |
2.1077|0.0021|0.014|102| | 2.09| 2.13| 3.97|P|1|A|02319+8915|I| 1| 1| | | |
| | | | | |S| |P| 8890|B+88 8 | |
|0.68|F7:Ib-IIv SB|G\n'
+ df = hipparcos.load_dataframe(BytesIO(line))
+ star = starlib.Star.from_dataframe(df.iloc[0])
ra, dec, distance = earth.at(load.timescale().tt_jd([2440423.345833333,
2448031.5, 2451545.0, 2456164.5])).observe(star).radec()
compare(ra.hours, (2.5283697000528966, 2.529691010447949,
2.5302921836971946, 2.5311170753257395), 0.00001 * ra_arcsecond)
compare(dec.degrees, (89.26420852419295, 89.26413900274704,
89.26411033462212, 89.26406913848278), 0.00001 * arcsecond)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/tests/test_api.py
new/skyfield-1.29/skyfield/tests/test_api.py
--- old/skyfield-1.27/skyfield/tests/test_api.py 2020-08-30
18:58:27.000000000 +0200
+++ new/skyfield-1.29/skyfield/tests/test_api.py 2020-09-24
18:55:30.000000000 +0200
@@ -126,14 +126,6 @@
assert str(p.from_altaz(alt=a, az=a).distance()) == '0.1 au'
assert str(p.from_altaz(alt=a, az=a, distance=d).distance()) == '0.234 au'
-def test_named_star_throws_valueerror():
- with assert_raises(ValueError, 'No star named foo known to skyfield'):
- api.NamedStar('foo')
-
-def test_named_star_returns_star():
- s = api.NamedStar('Polaris')
- assert isinstance(s, api.Star)
-
def test_github_81(ts):
t = ts.utc(1980, 1, 1)
assert t == t
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/skyfield-1.27/skyfield/tests/test_earth_satellites.py
new/skyfield-1.29/skyfield/tests/test_earth_satellites.py
--- old/skyfield-1.27/skyfield/tests/test_earth_satellites.py 2020-09-13
13:33:26.000000000 +0200
+++ new/skyfield-1.29/skyfield/tests/test_earth_satellites.py 2020-09-17
17:49:26.000000000 +0200
@@ -106,7 +106,8 @@
jd_epoch = sat.model.jdsatepoch + sat.model.jdsatepochF
three_days_later = jd_epoch + 3.0
- offset = ts.tt(jd=three_days_later)._utc_float() - three_days_later
+ whole, fraction, is_leap_second =
ts.tt(jd=three_days_later)._utc_float(0.0)
+ offset = whole + fraction - three_days_later
t = ts.tt(jd=three_days_later - offset)
# First, a crucial sanity check (which is, technically, a test of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/tests/test_strs_and_reprs.py
new/skyfield-1.29/skyfield/tests/test_strs_and_reprs.py
--- old/skyfield-1.27/skyfield/tests/test_strs_and_reprs.py 2020-09-11
09:04:48.000000000 +0200
+++ new/skyfield-1.29/skyfield/tests/test_strs_and_reprs.py 2020-09-16
01:15:55.000000000 +0200
@@ -25,7 +25,7 @@
""")
assert repr(e) == expected
-def test_satellite_with_name(eph):
+def test_satellite_with_name():
s = EarthSatellite(lines[1], lines[2], lines[0])
expected = dedent("""\
ISS (ZARYA) catalog #25544 epoch 2013-11-26 13:57:03 UTC
@@ -36,7 +36,7 @@
""")
assert repr(s) == expected
-def test_satellite_without_name(eph):
+def test_satellite_without_name():
s = EarthSatellite(lines[1], lines[2])
expected = dedent("""\
catalog #25544 epoch 2013-11-26 13:57:03 UTC
@@ -47,18 +47,21 @@
""")
assert repr(s) == expected
-def test_topos(eph):
+def test_topos():
t = Topos(latitude_degrees=42.2, longitude_degrees=-88.1)
expected = dedent("""\
Earth latitude 42deg 12' 00.0" N longitude -88deg 06' 00.0" E
""")
assert str(t) == expected
- # TODO
expected = dedent("""\
<Topos Earth latitude 42deg 12' 00.0" N longitude -88deg 06' 00.0" E>
""")
assert repr(t) == expected
+ t.target_name = 'Custom name'
+ assert str(t) == 'Custom name'
+ assert repr(t) == "<Topos Custom name>"
+
def test_jpl_vector_sum(eph):
e = eph['earth']
expected = dedent("""\
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/tests/test_timelib.py
new/skyfield-1.29/skyfield/tests/test_timelib.py
--- old/skyfield-1.27/skyfield/tests/test_timelib.py 2020-09-14
02:54:44.000000000 +0200
+++ new/skyfield-1.29/skyfield/tests/test_timelib.py 2020-09-26
00:51:10.000000000 +0200
@@ -1,38 +1,43 @@
import datetime as dt_module
+import numbers
import numpy as np
import sys
from assay import assert_raises
from pytz import timezone
from skyfield import api
from skyfield.constants import DAY_S, T0
-from skyfield.timelib import utc, calendar_tuple, julian_date
+from skyfield.timelib import (
+ GREGORIAN_START, GREGORIAN_START_ENGLAND,
+ calendar_tuple, compute_calendar_date, julian_date, julian_day, utc,
+)
from datetime import datetime
one_second = 1.0 / DAY_S
epsilon = one_second * 42.0e-6 # 20.1e-6 is theoretical best precision
-time_parameter = ['tai', 'tt', 'tdb', 'ut1']
+continuous_timescale = ['tai', 'tt', 'tdb', 'ut1']
+time_scale_name = ['utc', 'tai', 'tt', 'tdb', 'ut1']
time_value = [(1973, 1, 18, 1, 35, 37.5), 2441700.56640625]
def ts():
yield api.load.timescale()
-def test_time_creation_methods(ts, time_parameter, time_value):
- method = getattr(ts, time_parameter)
+def test_time_creation_methods(ts, continuous_timescale, time_value):
+ method = getattr(ts, continuous_timescale)
if isinstance(time_value, tuple):
t = method(*time_value)
else:
t = method(jd=time_value) # TODO: deprecate
- assert getattr(t, time_parameter) == 2441700.56640625
+ assert getattr(t, continuous_timescale) == 2441700.56640625
# Also go ahead and test the calendar and formatting operations.
- tup = getattr(t, time_parameter + '_calendar')()
+ tup = getattr(t, continuous_timescale + '_calendar')()
assert tup == (1973, 1, 18, 1, 35, 37.5)
- strftime = getattr(t, time_parameter + '_strftime')
+ strftime = getattr(t, continuous_timescale + '_strftime')
string = strftime()
- assert string == '1973-01-18 01:35:38 ' + time_parameter.upper()
+ assert string == '1973-01-18 01:35:38 ' + continuous_timescale.upper()
if sys.version_info <= (3,):
return # we do not currently support %f under Python 2
@@ -40,6 +45,11 @@
string = strftime('%S.%f')
assert string == '37.500000'
+def test_is_time_iterable(ts, time_scale_name):
+ t = getattr(ts, time_scale_name)(2020, 9, (25, 26))
+ for item in t:
+ pass
+
def test_strftime_on_prehistoric_dates(ts):
if sys.version_info <= (3,):
return # Python 2 time.strftime() complains about negative years
@@ -98,7 +108,6 @@
assert c[:5] == (2020, 6, 7, 2, 2)
assert '%.10f' % c[5] == '12.0123456789'
-time_scale_name = ['utc', 'tai', 'tt', 'tdb']
time_params_with_array = [
((2018, 2019, 2020), 3, 25, 13, 1, 10),
(2018, (3, 4, 5), 25, 13, 1, 10),
@@ -216,6 +225,10 @@
assert dt == tz.localize(datetime(1969, 7, 20, 16, 18, 0, 0))
assert leap_second == 0
+def test_toordinal(ts):
+ t = ts.utc(1973, 12, 31, 11, 59, 60)
+ assert t.toordinal() == 720623.5
+
def test_utc_datetime(ts):
t = ts.utc(1969, 7, 20, 20, 18, 42.186479)
dt = t.utc_datetime()
@@ -400,9 +413,6 @@
assert repr(ts.tt_jd([1, 2, 3, 4])) == '<Time tt=[1.0 ... 4.0] len=4>'
def test_jd_calendar():
-
- import numbers
-
# Check a specific instance (using UNIX epoch here, though that's
# an arbitrary choice)
jd_unix = 2440587.5
@@ -438,6 +448,121 @@
# Check reversal of array
assert (julian_date(*cal_array) == jd_array).all()
+def test_raw_julian_gregorian_cutover():
+ gregory = 2299161
+ assert compute_calendar_date(gregory - 2, gregory) == (1582, 10, 3)
+ assert compute_calendar_date(gregory - 1, gregory) == (1582, 10, 4)
+ assert compute_calendar_date(gregory + 0, gregory) == (1582, 10, 15)
+ assert compute_calendar_date(gregory + 1, gregory) == (1582, 10, 16)
+
+ jd = np.arange(gregory - 2, gregory + 2)
+ assert [list(a) for a in compute_calendar_date(jd, gregory)] == [
+ [1582, 1582, 1582, 1582],
+ [10, 10, 10, 10],
+ [3, 4, 15, 16],
+ ]
+
+ assert julian_day(1582, 10, 3, gregory) == (gregory - 2)
+ assert julian_day(1582, 10, 4, gregory) == (gregory - 1)
+ assert julian_day(1582, 10, 15, gregory) == (gregory + 0)
+ assert julian_day(1582, 10, 16, gregory) == (gregory + 1)
+
+ days = [3, 4, 15, 16]
+ assert list(julian_day(1582, 10, np.array(days), gregory)) == [
+ 2299159, 2299160, 2299161, 2299162,
+ ]
+
+def test_constructor_julian_gregorian_cutover(ts, time_scale_name):
+ if sys.version_info <= (3,):
+ return # Python 2 time.strftime() complains about the year 1582
+
+ def jd(y, m, d):
+ t = getattr(ts, time_scale_name)(y, m, d)
+ if time_scale_name == 'utc':
+ return sum(t._utc_float(0.0))
+ return getattr(t, time_scale_name)
+
+ ts = api.load.timescale()
+
+ assert jd(1582, 10, 4) == 2299149.5
+ assert jd(1582, 10, 15) == 2299160.5
+ assert jd(1752, 9, 2) == 2361209.5
+ assert jd(1752, 9, 14) == 2361221.5
+
+ ts.julian_calendar_cutoff = GREGORIAN_START
+
+ assert jd(1582, 10, 4) == 2299159.5
+ assert jd(1582, 10, 15) == 2299160.5
+ assert jd(1752, 9, 2) == 2361209.5
+ assert jd(1752, 9, 14) == 2361221.5
+
+ ts.julian_calendar_cutoff = GREGORIAN_START_ENGLAND
+
+ assert jd(1582, 10, 4) == 2299159.5
+ assert jd(1582, 10, 15) == 2299170.5
+ assert jd(1752, 9, 2) == 2361220.5
+ assert jd(1752, 9, 14) == 2361221.5
+
+def test_calendar_tuple_julian_gregorian_cutover(ts, time_scale_name):
+ if sys.version_info <= (3,):
+ return # Python 2 time.strftime() complains about the year 1582
+
+ def ymd(jd):
+ t = ts.tt_jd(jd, 0.1)
+ if time_scale_name == 'utc':
+ return t.utc[:3]
+ return getattr(t, time_scale_name + '_calendar')()[:3]
+
+ ts = api.load.timescale()
+
+ assert ymd(2299149.5) == (1582, 10, 4)
+ assert ymd(2299160.5) == (1582, 10, 15)
+ assert ymd(2361209.5) == (1752, 9, 2)
+ assert ymd(2361221.5) == (1752, 9, 14)
+
+ ts.julian_calendar_cutoff = GREGORIAN_START
+
+ assert ymd(2299159.5) == (1582, 10, 4)
+ assert ymd(2299160.5) == (1582, 10, 15)
+ assert ymd(2361209.5) == (1752, 9, 2)
+ assert ymd(2361221.5) == (1752, 9, 14)
+
+ ts.julian_calendar_cutoff = GREGORIAN_START_ENGLAND
+
+ assert ymd(2299159.5) == (1582, 10, 4)
+ assert ymd(2299170.5) == (1582, 10, 15)
+ assert ymd(2361220.5) == (1752, 9, 2)
+ assert ymd(2361221.5) == (1752, 9, 14)
+
+def test_strftime_julian_gregorian_cutover(ts, time_scale_name):
+ if sys.version_info <= (3,):
+ return # Python 2 time.strftime() complains about the year 1582
+
+ def ymd(jd):
+ t = ts.tt_jd(jd, 0.1)
+ return getattr(t, time_scale_name + '_strftime')('%Y %m %d')
+
+ ts = api.load.timescale()
+
+ assert ymd(2299149.5) == '1582 10 04'
+ assert ymd(2299160.5) == '1582 10 15'
+ assert ymd(2361209.5) == '1752 09 02'
+ assert ymd(2361221.5) == '1752 09 14'
+
+ ts.julian_calendar_cutoff = GREGORIAN_START
+
+ assert ymd(2299159.5) == '1582 10 04'
+ assert ymd(2299160.5) == '1582 10 15'
+ assert ymd(2361209.5) == '1752 09 02'
+ assert ymd(2361221.5) == '1752 09 14'
+
+ ts.julian_calendar_cutoff = GREGORIAN_START_ENGLAND
+
+ assert ymd(2299159.5) == '1582 10 04'
+ assert ymd(2299170.5) == '1582 10 15'
+ assert ymd(2361220.5) == '1752 09 02'
+ assert ymd(2361221.5) == '1752 09 14'
+
def test_time_equality(ts):
t0 = ts.tt_jd(2459008.5, 0.125)
t1 = ts.tt_jd(2459008.0, 0.625)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/timelib.py
new/skyfield-1.29/skyfield/timelib.py
--- old/skyfield-1.27/skyfield/timelib.py 2020-09-15 00:48:15.000000000
+0200
+++ new/skyfield-1.29/skyfield/timelib.py 2020-09-26 00:51:39.000000000
+0200
@@ -18,6 +18,9 @@
)
from .precessionlib import compute_precession
+GREGORIAN_START = 2299161
+GREGORIAN_START_ENGLAND = 2361222
+
CalendarTuple = namedtuple('CalendarTuple', 'year month day hour minute
second')
class CalendarArray(ndarray):
@@ -87,6 +90,7 @@
self._leap_reverse_dates = leap_dates + leap_offsets / DAY_S
self.J2000 = Time(self, float_(T0))
self.B1950 = Time(self, float_(B1950))
+ self.julian_calendar_cutoff = None
def now(self):
"""Return the current date and time as a `Time` object.
@@ -106,35 +110,21 @@
zone object as its ``tzinfo`` attribute instead of ``None``.
"""
- jd, fr = _utc_datetime_to_tai(
- self.leap_dates, self.leap_offsets, datetime)
- t = Time(self, jd, fr + tt_minus_tai)
- t.tai_fraction = fr
- return t
+ return self._utc(_datetime_to_utc_tuple(datetime))
def from_datetimes(self, datetime_list):
- """Return a `Time` for a Python ``datetime`` list.
+ """Return a `Time` for a list of Python ``datetime``\ s.
The ``datetime`` objects must each be “timezone-aware”: they
must each have a time zone object as their ``tzinfo`` attribute
instead of ``None``.
"""
- pairs = [_utc_datetime_to_tai(self.leap_dates, self.leap_offsets, d)
- for d in datetime_list]
- jd, fr = zip(*pairs)
- t = Time(self, _to_array(jd), fr + tt_minus_tai)
- t.tai_fraction = fr
- return t
+ tuples = (_datetime_to_utc_tuple(d) for d in datetime_list)
+ return self._utc(array(value) for value in zip(*tuples))
def utc(self, year, month=1, day=1, hour=0, minute=0, second=0.0):
- """Build a `Time` from a UTC calendar date.
-
- Specify the date as a numeric year, month, day, hour, minute,
- and second. Any argument may be an array in which case the
- return value is a ``Time`` representing a whole array of times.
-
- """
+ """Build a `Time` from a UTC `calendar date`."""
# TODO: someday deprecate passing datetime objects here, as
# there are now separate constructors for them.
if isinstance(year, datetime):
@@ -144,41 +134,89 @@
if hasattr(year, '__len__') and isinstance(year[0], datetime):
return self.from_datetimes(year)
- tai1, tai2 = _utc_to_tai(
- self.leap_dates, self.leap_offsets, _to_array(year),
- _to_array(month), _to_array(day), _to_array(hour),
- _to_array(minute), _to_array(second),
- )
- t = Time(self, tai1, tai2 + tt_minus_tai)
- t.tai_fraction = tai2
+ tup = year, month, day, hour, minute, second
+ return self._utc(tup)
+
+ def _utc(self, tup):
+ year, month, day, hour, minute, second = tup
+ whole, fraction = self._jd(year, month, day, hour, minute, 0.0)
+ i = searchsorted(self.leap_dates, whole + fraction, 'right')
+ fraction += (self.leap_offsets[i] + second) / DAY_S
+ whole, fraction = _reconcile(whole, fraction) # second could be array
+ t = Time(self, whole, fraction + tt_minus_tai)
+ t.tai_fraction = fraction
return t
- def tai(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
- jd=None):
- """Build a `Time` from a TAI proleptic Gregorian date.
+ def _jd(self, year, month, day, hour, minute, second):
+ a = _to_array
+ cutoff = self.julian_calendar_cutoff
+ whole = julian_day(a(year), a(month), a(day), cutoff) - 0.5
+ fraction = (a(second) + a(minute) * 60.0 + a(hour) * 3600.0) / DAY_S
+ return _reconcile(whole, fraction)
- Supply the International Atomic Time (TAI) as a proleptic
- Gregorian calendar date:
+ def _cal(self, whole, fraction):
+ return calendar_tuple(whole, fraction, self.julian_calendar_cutoff)
- >>> t = ts.tai(2014, 1, 18, 1, 35, 37.5)
- >>> t.tai
- 2456675.56640625
- >>> t.tai_calendar()
- (2014, 1, 18, 1, 35, 37.5)
+ def _strftime(self, format, jd, fraction, seconds_bump=None):
+ # Python forces an unhappy choice upon us: either use the faster
+ # time.strftime() and lose support for '%f', or use the slower
+ # datetime.strftime() and crash if years are negative. We take the
+ # first option, but then patch '%f' support back in by secretly
+ # passing the microseconds string as the time zone name. After all,
+ # the routines supported by this function never use time zones.
+ # What could go wrong?
+
+ ms = _format_uses_milliseconds(format)
+
+ if ms:
+ fraction = fraction + 1e-16 # encourage .0 to not turn into
.999999
+ elif _format_uses_seconds(format):
+ fraction = fraction + _half_second
+ elif _format_uses_minutes(format):
+ fraction = fraction + _half_minute
+
+ year, month, day, hour, minute, second = self._cal(jd, fraction)
+ z = year * 0
+
+ # TODO: will this always produce the same whole number that
+ # calendar_tuple() produces internally? Or should we make a private
+ # version of calendar_tuple() that returns it to us for use here?
+ weekday = (fraction + 0.5 + _to_array(jd)).astype(int) % 7
+
+ if ms:
+ format = format[:ms.start()] + '%Z' + format[ms.end():]
+ second = (second * 1e6).astype(int)
+ second, usec = divmod(second, 1000000)
+ if seconds_bump is not None:
+ second += seconds_bump
+ if getattr(jd, 'ndim', 0):
+ u = ['%06d' % u for u in usec]
+ tup = year, month, day, hour, minute, second, weekday, z, z, u
+ return [strftime(format, struct_time(t)) for t in zip(*tup)]
+ u = '%06d' % usec
+ tup = year, month, day, hour, minute, second, weekday, z, z, u
+ return strftime(format, struct_time(tup))
+ else:
+ second = second.astype(int)
+ if seconds_bump is not None:
+ second += seconds_bump
+ tup = year, month, day, hour, minute, second, weekday, z, z
+ if getattr(jd, 'ndim', 0):
+ return [strftime(format, item) for item in zip(*tup)]
+ return strftime(format, tup)
- """
+ def tai(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
+ jd=None):
+ """Build a `Time` from an International Atomic Time `calendar date`."""
if jd is not None:
return self.tai_jd(jd) # deprecate someday
- a = _to_array
- whole = julian_day(a(year), a(month), a(day)) - 0.5
- fraction = (a(second) + a(minute) * 60.0 + a(hour) * 3600.0) / DAY_S
- whole, fraction = _reconcile(whole, fraction)
+ whole, fraction = self._jd(year, month, day, hour, minute, second)
t = Time(self, whole, fraction + tt_minus_tai)
t.tai_fraction = fraction
return t
def tai_jd(self, jd, fraction=0.0):
- """Build a `Time` from a TAI Julian date."""
+ """Build a `Time` from an International Atomic Time Julian date."""
whole, fraction2 = divmod(_to_array(jd), 1.0)
fraction2 += fraction
t = Time(self, whole, fraction2 + tt_minus_tai)
@@ -187,34 +225,20 @@
def tt(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
jd=None):
- """Build a `Time` from a TT calendar date.
-
- Supply the Terrestrial Time (TT) as a proleptic Gregorian
- calendar date:
-
- >>> t = ts.tt(2014, 1, 18, 1, 35, 37.5)
- >>> t.tt
- 2456675.56640625
- >>> t.tt_calendar()
- (2014, 1, 18, 1, 35, 37.5)
-
- """
+ """Build a `Time` from a Terrestrial Time `calendar date`."""
if jd is not None:
return self.tt_jd(jd) # deprecate someday
- a = _to_array
- whole = julian_day(a(year), a(month), a(day)) - 0.5
- fraction = (a(second) + a(minute) * 60.0 + a(hour) * 3600.0) / DAY_S
- whole, fraction = _reconcile(whole, fraction)
+ whole, fraction = self._jd(year, month, day, hour, minute, second)
return Time(self, whole, fraction)
def tt_jd(self, jd, fraction=0.0):
- """Build a `Time` from a TT Julian date."""
+ """Build a `Time` from a Terrestrial Time Julian date."""
whole, fraction2 = divmod(_to_array(jd), 1.0)
fraction2 += fraction
return Time(self, whole, fraction2)
def J(self, year):
- """Build a `Time` from a TT Julian year or array of years.
+ """Build a `Time` from a Terrestrial Time Julian year or array.
Julian years are convenient uniform periods of exactly 365.25
days of Terrestrial Time, centered on 2000 January 1 12h TT =
@@ -226,29 +250,15 @@
def tdb(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
jd=None):
- """Build a `Time` from a TDB calendar date.
-
- Supply the Barycentric Dynamical Time (TDB) as a proleptic
- Gregorian calendar date:
-
- >>> t = ts.tdb(2014, 1, 18, 1, 35, 37.5)
- >>> t.tdb
- 2456675.56640625
-
- """
+ """Build a `Time` from a Barycentric Dynamical Time `calendar date`."""
if jd is not None:
- tdb = jd
- else:
- tdb = julian_date(
- _to_array(year), _to_array(month), _to_array(day),
- _to_array(hour), _to_array(minute), _to_array(second),
- )
- tdb = _to_array(tdb)
- t = Time(self, tdb, - tdb_minus_tt(tdb) / DAY_S)
- return t
+ return self.tdb_jd(jd) # deprecate someday
+ whole, fraction = self._jd(year, month, day, hour, minute, second)
+ jd = whole + fraction # TODO: why do tests break if we pass separately
+ return Time(self, jd, - tdb_minus_tt(jd) / DAY_S)
def tdb_jd(self, jd, fraction=0.0):
- """Build `Time` from a Barycentric Dynamical Time (TDB) Julian date."""
+ """Build `Time` from a Barycentric Dynamical Time Julian date."""
whole, fraction2 = divmod(jd, 1.0)
fraction2 += fraction
t = Time(self, whole, fraction2 - tdb_minus_tt(jd, fraction) / DAY_S)
@@ -257,27 +267,14 @@
def ut1(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
jd=None):
- """Build a `Time` from a UT1 calendar date.
-
- Supply the Universal Time (UT1) as a proleptic Gregorian
- calendar date:
-
- >>> t = ts.ut1(2014, 1, 18, 1, 35, 37.5)
- >>> t.ut1
- 2456675.56640625
-
- """
- if jd is not None:
- ut1 = jd
- else:
- ut1 = julian_date(
- _to_array(year), _to_array(month), _to_array(day),
- _to_array(hour), _to_array(minute), _to_array(second),
- )
- return self.ut1_jd(ut1)
+ """Build a `Time` from a UT1 Universal Time `calendar date`."""
+ if jd is None: # TODO: deprecate the jd parameter to this method
+ whole, fraction = self._jd(year, month, day, hour, minute, second)
+ jd = whole + fraction # TODO: can we pass high precision on?
+ return self.ut1_jd(jd)
def ut1_jd(self, jd):
- """Build a `Time` from a UT1 Julian date."""
+ """Build a `Time` from a UT1 Universal Time Julian date."""
ut1 = _to_array(jd)
# Estimate TT = UT1, to get a rough Delta T estimate.
@@ -295,7 +292,7 @@
# and look for the notebook "error-in-timescale-ut1.ipynb".
delta_t_approx /= DAY_S
t = Time(self, ut1, delta_t_approx)
- t.ut1_fraction = 0.0
+ t.ut1_fraction = 0.0 * ut1
return t
def from_astropy(self, t):
@@ -344,6 +341,8 @@
# otherwise, a `for` loop over it will not raise an error.
t = Time(self.ts, self.whole[index], self.tt_fraction[index])
for name in 'tai_fraction', 'tdb_fraction', 'ut1_fraction':
+ # TODO: drat, I suspect this forces the creation of all of
+ # the fractions whether we need them or not.
value = getattr(self, name, None)
if value is not None:
setattr(t, name, value[index])
@@ -403,7 +402,8 @@
directly as a coordinate for a plot.
"""
- return self._utc_float() - 1721424.5
+ whole, fraction, is_leap_second = self._utc_float(0.0)
+ return whole - 1721424.5 + fraction
def utc_datetime(self):
"""Convert to a Python ``datetime`` in UTC.
@@ -437,14 +437,13 @@
"""
year, month, day, hour, minute, second = self._utc_tuple(
_half_microsecond)
- second, fraction = divmod(second, 1.0)
- second = second.astype(int)
+ micro = (second * 1e6).astype(int)
+ second, micro = divmod(micro, 1000000)
leap_second = second // 60
second -= leap_second # fit within limited bounds of Python datetime
- micro = (fraction * 1e6).astype(int)
if self.shape:
- utcs = [utc] * self.shape[0]
- argsets = zip(year, month, day, hour, minute, second, micro, utcs)
+ zone = [utc] * self.shape[0]
+ argsets = zip(year, month, day, hour, minute, second, micro, zone)
dt = array([datetime(*args) for args in argsets])
else:
dt = datetime(year, month, day, hour, minute, second, micro, utc)
@@ -532,12 +531,8 @@
Otherwise the value is truncated rather than rounded.
"""
- ts = self.ts
- tai = self.tai
- i = searchsorted(ts._leap_reverse_dates, tai, 'right')
- fraction = self.tai_fraction - ts.leap_offsets[i] / DAY_S
- is_leap_second = fraction + self.whole - ts.leap_dates[i-1] < 0
- return _strftime(format, self.whole, fraction, is_leap_second)
+ whole, fraction, is_leap_second = self._utc_float(0.0)
+ return self.ts._strftime(format, self.whole, fraction, is_leap_second)
def _utc_tuple(self, offset=0.0):
"""Return UTC as (year, month, day, hour, minute, second.fraction).
@@ -551,60 +546,54 @@
throw away the seconds; and so forth.
"""
- ts = self.ts
- tai = self.tai + offset
- i = searchsorted(ts._leap_reverse_dates, tai, 'right')
- year, month, day, hour, minute, second = calendar_tuple(
- self.whole,
- offset - ts.leap_offsets[i] / DAY_S + self.tai_fraction,
- )
- j = tai - ts.leap_offsets[i] / DAY_S
- is_leap_second = j < ts.leap_dates[i-1]
+ jd, fraction, is_leap_second = self._utc_float(offset)
+ year, month, day, hour, minute, second = self.ts._cal(jd, fraction)
second += is_leap_second
return year, month, day, hour.astype(int), minute.astype(int), second
- def _utc_float(self):
- """Return UTC as a floating point Julian date."""
- tai = self.tai
+ def _utc_float(self, offset):
ts = self.ts
- i = searchsorted(ts._leap_reverse_dates, tai, 'right')
- return tai - ts.leap_offsets[i] / DAY_S
+ i = searchsorted(ts._leap_reverse_dates, self.tai + offset, 'right')
+ whole = self.whole
+ fraction = offset - ts.leap_offsets[i] / DAY_S + self.tai_fraction
+ is_leap_second = (whole + fraction) < ts.leap_dates[i-1]
+ return whole, fraction, is_leap_second
# Calendar tuples.
def tai_calendar(self):
- """Return TAI as Gregorian (year, month, day, hour, minute, second)."""
- return calendar_tuple(self.whole, self.tai_fraction)
+ """TAI as a (year, month, day, hour, minute, second) `calendar
date`."""
+ return self.ts._cal(self.whole, self.tai_fraction)
def tt_calendar(self):
- """Return TT as Gregorian (year, month, day, hour, minute, second)."""
- return calendar_tuple(self.whole, self.tt_fraction)
+ """TT as a (year, month, day, hour, minute, second) `calendar date`."""
+ return self.ts._cal(self.whole, self.tt_fraction)
def tdb_calendar(self):
- """Return TDB as Gregorian (year, month, day, hour, minute, second)."""
- return calendar_tuple(self.whole, self.tdb_fraction)
+ """TDB as a (year, month, day, hour, minute, second) `calendar
date`."""
+ return self.ts._cal(self.whole, self.tdb_fraction)
def ut1_calendar(self):
- """Return UT1 as Gregorian (year, month, day, hour, minute, second)."""
- return calendar_tuple(self.whole, self.ut1_fraction)
+ """UT1 as a (year, month, day, hour, minute, second) `calendar
date`."""
+ return self.ts._cal(self.whole, self.ut1_fraction)
# Date formatting.
def tai_strftime(self, format='%Y-%m-%d %H:%M:%S TAI'):
"""Format TAI with a datetime strftime() format string."""
- return _strftime(format, self.whole, self.tai_fraction)
+ return self.ts._strftime(format, self.whole, self.tai_fraction)
def tt_strftime(self, format='%Y-%m-%d %H:%M:%S TT'):
"""Format TT with a datetime strftime() format string."""
- return _strftime(format, self.whole, self.tt_fraction)
+ return self.ts._strftime(format, self.whole, self.tt_fraction)
def tdb_strftime(self, format='%Y-%m-%d %H:%M:%S TDB'):
"""Format TDB with a datetime strftime() format string."""
- return _strftime(format, self.whole, self.tdb_fraction)
+ return self.ts._strftime(format, self.whole, self.tdb_fraction)
def ut1_strftime(self, format='%Y-%m-%d %H:%M:%S UT1'):
"""Format UT1 with a datetime strftime() format string."""
- return _strftime(format, self.whole, self.ut1_fraction)
+ return self.ts._strftime(format, self.whole, self.ut1_fraction)
# Convenient caching of several expensive functions of time.
@@ -713,6 +702,7 @@
def dut1(self):
ts = self.ts
i = searchsorted(ts._leap_reverse_dates, self.tai, 'right')
+ print(32.184, ts.leap_offsets[i], self.delta_t)
return 32.184 + ts.leap_offsets[i] - self.delta_t
@reify
@@ -792,14 +782,24 @@
@reify
def NT(self): return rollaxis(self.N, 1)
-def julian_day(year, month=1, day=1):
- """Given a proleptic Gregorian calendar date, return a Julian day int."""
- janfeb = month < 3
- return (day
- + 1461 * (year + 4800 - janfeb) // 4
- + 367 * (month - 2 + janfeb * 12) // 12
- - 3 * ((year + 4900 - janfeb) // 100) // 4
- - 32075)
+def julian_day(year, month=1, day=1, julian_before=None):
+ """Given a calendar date, return a Julian day integer.
+
+ Uses the proleptic Gregorian calendar unless ``julian_before`` is
+ set to a specific Julian day, in which case the Julian calendar is
+ used for dates older than that.
+
+ """
+ # See the Explanatory Supplement to the Astronomical Almanac 15.11.
+ janfeb = month <= 2
+ g = year + 4716 - janfeb
+ f = (month + 9) % 12
+ e = 1461 * g // 4 + day - 1402
+ J = e + (153 * f + 2) // 5
+
+ mask = 1 if (julian_before is None) else (J >= julian_before)
+ J += (38 - (g + 184) // 100 * 3 // 4) * mask
+ return J
def julian_date(year, month=1, day=1, hour=0, minute=0, second=0.0):
"""Given a proleptic Gregorian calendar date and time, build a Julian date.
@@ -818,34 +818,14 @@
def compute_calendar_date(jd_integer, julian_before=None):
"""Convert Julian day ``jd_integer`` into a calendar (year, month, day).
- By default, this returns a proleptic Gregorian date. If you instead
- want to use the Julian calendar for old dates, provide a Julian day
- ``julian_before`` on which the calendar should cut over. One
- popular choice is the date on which Pope Gregory adopted the new
- calendar in October 1582:
-
- >>> gregory = 2299161
- >>> for jd in range(gregory - 2, gregory + 2):
- ... print(compute_calendar_date(jd, gregory))
- (1582, 10, 3)
- (1582, 10, 4)
- (1582, 10, 15)
- (1582, 10, 16)
-
- Another is the date on which England switched in September 1752:
-
- >>> england = 2361222
- >>> for jd in range(england - 2, england + 2):
- ... print(compute_calendar_date(jd, england))
- (1752, 9, 1)
- (1752, 9, 2)
- (1752, 9, 14)
- (1752, 9, 15)
+ Uses the proleptic Gregorian calendar unless ``julian_before`` is
+ set to a specific Julian day, in which case the Julian calendar is
+ used for dates older than that.
"""
use_gregorian = (julian_before is None) or (jd_integer >= julian_before)
- # Adapted from the Astronomical Almanac 15.11.1.
+ # See the Explanatory Supplement to the Astronomical Almanac 15.11.
f = jd_integer + 1401
f += use_gregorian * ((4 * jd_integer + 274277) // 146097 * 3 // 4 - 38)
e = 4 * f + 3
@@ -858,13 +838,13 @@
calendar_date = compute_calendar_date # old name, in case anyone used it
-def calendar_tuple(jd_float, fraction=0.0):
+def calendar_tuple(jd_float, fraction=0.0, julian_before=None):
"""Return a (year, month, day, hour, minute, second.fraction) tuple."""
jd_float = _to_array(jd_float)
whole1, fraction1 = divmod(jd_float, 1.0)
whole2, fraction = divmod(fraction1 + fraction + 0.5, 1.0)
whole = (whole1 + whole2).astype(int)
- year, month, day = compute_calendar_date(whole)
+ year, month, day = compute_calendar_date(whole, julian_before)
second = fraction * 86400.0
minute, second = divmod(second, 60.0)
minute = minute.astype(int)
@@ -964,71 +944,14 @@
_format_uses_seconds = re.compile(r'%[-_0^#EO]*[STXc]').search
_format_uses_minutes = re.compile(r'%[-_0^#EO]*[MR]').search
-def _strftime(format, jd, fraction, seconds_bump=None):
- # Python forces an unhappy choice upon us: either use the faster
- # time.strftime() and lose support for '%f', or use the slower
- # datetime.strftime() and crash if years are negative. We take the
- # first option, but then patch '%f' support back in by secretly
- # passing the microseconds string as the time zone name. After all,
- # the routines supported by this function never use time zones.
- # What could go wrong?
-
- ms = _format_uses_milliseconds(format)
-
- if ms:
- fraction = fraction + 1e-16 # encourage .0 to not turn into .999999
- elif _format_uses_seconds(format):
- fraction = fraction + _half_second
- elif _format_uses_minutes(format):
- fraction = fraction + _half_minute
-
- year, month, day, hour, minute, second = calendar_tuple(jd, fraction)
- z = year * 0
-
- # TODO: will this always produce the same whole number that
- # calendar_tuple() produces internally? Or should we make a private
- # version of calendar_tuple() that returns it to us for use here?
- weekday = (fraction + 0.5 + _to_array(jd)).astype(int) % 7
-
- if ms:
- format = format[:ms.start()] + '%Z' + format[ms.end():]
- second = (second * 1e6).astype(int)
- second, usec = divmod(second, 1000000)
- if seconds_bump is not None:
- second += seconds_bump
- if getattr(jd, 'ndim', 0):
- usec = ['%06d' % u for u in usec]
- tup = year, month, day, hour, minute, second, weekday, z, z, usec
- return [strftime(format, struct_time(item)) for item in zip(*tup)]
- usec = '%06d' % usec
- return strftime(format, struct_time(
- (year, month, day, hour, minute, second, weekday, z, z, usec)
- ))
- else:
- second = second.astype(int)
- if seconds_bump is not None:
- second += seconds_bump
- tup = year, month, day, hour, minute, second, weekday, z, z
- if getattr(jd, 'ndim', 0):
- return [strftime(format, item) for item in zip(*tup)]
- return strftime(format, tup)
-
-def _utc_datetime_to_tai(leap_dates, leap_offsets, dt):
- if dt.tzinfo is None:
+def _datetime_to_utc_tuple(dt):
+ z = dt.tzinfo
+ if z is None:
raise ValueError(_naive_complaint)
- if dt.tzinfo is not utc:
+ if z is not utc:
dt = dt.astimezone(utc)
- return _utc_to_tai(leap_dates, leap_offsets,
- dt.year, dt.month, dt.day,
- dt.hour, dt.minute, dt.second + dt.microsecond * 1e-6)
-
-def _utc_to_tai(leap_dates, leap_offsets,
- year, month, day, hour, minute, second):
- j = julian_day(year, month, day) - 0.5
- i = searchsorted(leap_dates, j, 'right')
- seconds = leap_offsets[i] + second + minute * 60.0 + hour * 3600.0
- j, seconds = _reconcile(j, seconds)
- return j, seconds / DAY_S
+ return (dt.year, dt.month, dt.day,
+ dt.hour, dt.minute, dt.second + dt.microsecond / 1e6)
_JulianDate_deprecation_message = """Skyfield no longer supports direct\
instantiation of JulianDate objects (which are now called Time objects)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/skyfield-1.27/skyfield/toposlib.py
new/skyfield-1.29/skyfield/toposlib.py
--- old/skyfield-1.27/skyfield/toposlib.py 2020-09-03 23:46:40.000000000
+0200
+++ new/skyfield-1.29/skyfield/toposlib.py 2020-09-16 01:15:34.000000000
+0200
@@ -4,6 +4,7 @@
from .constants import ASEC2RAD, tau
from .earthlib import refract, terra
from .functions import mxmxm, mxv, rot_x, rot_y, rot_z
+from .descriptorlib import reify
from .units import Distance, Angle, _interpret_ltude
from .vectorlib import VectorFunction
@@ -72,7 +73,7 @@
# reference that delays garbage collection.)
return self
- @property
+ @reify # not @property, so users have the option of overwriting it
def target_name(self):
m = self.elevation.m
e = ' elevation {0:.0f} m'.format(m) if m else ''