Skip to content

Commit

Permalink
Refactor so byear and decimalyear share validation
Browse files Browse the repository at this point in the history
  • Loading branch information
mhvk committed Apr 5, 2023
1 parent 3c303f4 commit b223d80
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 26 deletions.
64 changes: 39 additions & 25 deletions astropy/time/formats.py
Expand Up @@ -52,6 +52,7 @@
"TimeDatetime64",
"TimeYMDHMS",
"TimeNumeric",
"TimeNumericNoQuantity",
"TimeDeltaNumeric",
]

Expand Down Expand Up @@ -121,6 +122,8 @@ class TimeFormat:
----------
val1 : numpy ndarray, list, number, str, or bytes
Values to initialize the time or times. Bytes are decoded as ascii.
Quantities with time units are allowed for formats where the
interpretation is unambiguous.
val2 : numpy ndarray, list, or number; optional
Value(s) to initialize the time or times. Only used for numerical
input, to help preserve precision.
Expand Down Expand Up @@ -545,6 +548,7 @@ def to_value(self, jd1=None, jd2=None, parent=None, out_subfmt=None):
class TimeJD(TimeNumeric):
"""
Julian Date time format.
This represents the number of days since the beginning of
the Julian Period.
For example, 2451544.5 in JD is midnight on January 1, 2000.
Expand All @@ -560,6 +564,7 @@ def set_jds(self, val1, val2):
class TimeMJD(TimeNumeric):
"""
Modified Julian Date time format.
This represents the number of days since midnight on November 17, 1858.
For example, 51544.0 in MJD is midnight on January 1, 2000.
"""
Expand All @@ -580,25 +585,39 @@ def to_value(self, **kwargs):
value = property(to_value)


class TimeDecimalYear(TimeNumeric):
"""
Time as a decimal year, with integer values corresponding to midnight
of the first day of each year. For example 2000.5 corresponds to the
ISO time '2000-07-02 00:00:00'.
class TimeNumericNoQuantity(TimeNumeric):
"""
Numerical time format with a non-SI scale of time.
name = "decimalyear"
For these, quantities are not allowed, as the assumption that
one year always contains a fixed number of days does not hold.
"""

def _check_val_type(self, val1, val2):
if hasattr(val1, "to") and hasattr(val1, "unit") and val1.unit is not None:
if hasattr(val1, "to") and getattr(val1, "unit", None) is not None:
raise ValueError(

Check warning on line 598 in astropy/time/formats.py

View check run for this annotation

Codecov / codecov/patch

astropy/time/formats.py#L598

Added line #L598 was not covered by tests
"cannot use Quantities for 'decimalyear' format, as the unit of year "
"is defined as 365.25 days, while the length of the year is variable "
"in the 'decimalyear' format. Use float instead."
f"cannot use Quantities for {self.name!r} format, as the unit of year "
"is defined as 365.25 days, while the length of year is variable "
"in this format. Use float instead."
)
# if val2 is a Quantity, super() will raise a TypeError.
return super()._check_val_type(val1, val2)


class TimeDecimalYear(TimeNumericNoQuantity):
"""
Time as a decimal year, with integer values corresponding to midnight
of the first day of each year.
For example 2000.5 corresponds to the ISO time '2000-07-02 00:00:00'.
Since for this format the length of the year varies between 365 and
366 days, it is not possible to use Quantity input, in which a year
is always 365.25 days.
"""

name = "decimalyear"

def set_jds(self, val1, val2):
self._check_scale(self._scale) # Validate scale.

Expand Down Expand Up @@ -657,7 +676,7 @@ def to_value(self, **kwargs):
class TimeFromEpoch(TimeNumeric):
"""
Base class for times that represent the interval from a particular
epoch as a floating point multiple of a unit time interval (e.g. seconds
epoch as a numerical multiple of a unit time interval (e.g. seconds
or days).
"""

Expand Down Expand Up @@ -1962,7 +1981,7 @@ def value(self):

class TimeEpochDate(TimeNumeric):
"""
Base class for support floating point Besselian and Julian epoch dates.
Base class for support of Besselian and Julian epoch dates.
"""

_default_scale = "tt" # As of astropy 3.2, this is no longer 'utc'.
Expand All @@ -1981,26 +2000,21 @@ def to_value(self, **kwargs):
value = property(to_value)


class TimeBesselianEpoch(TimeEpochDate):
"""Besselian Epoch year as floating point value(s) like 1950.0."""
class TimeBesselianEpoch(TimeEpochDate, TimeNumericNoQuantity):
"""Besselian Epoch year as value(s) like 1950.0.
Since for this format the length of the year varies, input needs to
be floating point; it is not possible to use Quantity input, for
which a year always equals 365.25 days.
"""

name = "byear"
epoch_to_jd = "epb2jd"
jd_to_epoch = "epb"

def _check_val_type(self, val1, val2):
"""Input value validation, typically overridden by derived classes."""
if hasattr(val1, "to") and hasattr(val1, "unit") and val1.unit is not None:
raise ValueError(
"Cannot use Quantities for 'byear' format, as the interpretation "
"would be ambiguous. Use float with Besselian year instead."
)
# FIXME: is val2 really okay here?
return super()._check_val_type(val1, val2)


class TimeJulianEpoch(TimeEpochDate):
"""Julian Epoch year as floating point value(s) like 2000.0."""
"""Julian Epoch year as value(s) like 2000.0."""

name = "jyear"
unit = erfa.DJY # 365.25, the Julian year, for conversion to quantities
Expand Down
2 changes: 1 addition & 1 deletion docs/time/index.rst
Expand Up @@ -1449,7 +1449,7 @@ To use |Quantity| objects with units of time::
Traceback (most recent call last):
...
ValueError: Input values did not match the format class byear:
ValueError: Cannot use Quantities for 'byear' format, as the interpretation would be ambiguous. Use float with Besselian year instead.
ValueError: cannot use Quantities for 'byear' format, as the unit of year is defined as 365.25 days, while the length of year is variable in this format. Use float instead.

>>> TimeDelta(10.*u.yr) # With a quantity, no format is required
<TimeDelta object: scale='None' format='jd' value=3652.5>
Expand Down

0 comments on commit b223d80

Please sign in to comment.