-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added LOCAL time scale #7122
Added LOCAL time scale #7122
Conversation
Hi there @vermashresth 👋 - thanks for the pull request! I'm just a friendly 🤖 that checks for issues related to the changelog and making sure that this pull request is milestoned and labeled correctly. This is mainly intended for the maintainers, so if you are not a maintainer you can ignore this, and a maintainer will let you know if any action is required on your part 😃. Everything looks good from my point of view! 👍 If there are any issues with this message, please report them here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR. This looks good, modulo some fairly minor initial comments. Beyond that:
- Obviously you need to figure out why the tests failed (sorry, don't have time to look into it myself now). For this, if you haven't done so already, do set up your own development environment.
- Once things are working, we'd need some tests with the
local
timescale. This should include transformations to different formats (which may need some thought for those that have a reference epoch; failing those is an option, but it should be done gracefully). - And once that is done, some added documentation and an entry in
CHANGES.rst
- Finally, but this can be a separate PR, we should use this for
io.fits
!
astropy/time/core.py
Outdated
@@ -30,11 +30,14 @@ | |||
from .formats import TimeFromEpoch # pylint: disable=W0611 | |||
|
|||
|
|||
__all__ = ['Time', 'TimeDelta', 'TIME_SCALES', 'TIME_DELTA_SCALES', | |||
__all__ = ['Time', 'TimeDelta', 'TIME_SCALES','TIME_TYPES', 'TIME_DELTA_SCALES', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to export TIME_TYPES
astropy/time/core.py
Outdated
# remove attributes that are invalidated by changing time | ||
for attr in ('_delta_ut1_utc', '_delta_tdb_tt'): | ||
if hasattr(out, attr): | ||
delattr(out, attr) | ||
|
||
else: # T - T | ||
|
||
# the scales should be compatible (e.g., cannot convert TDB to LOCAL) | ||
if(self.scale is not None and self.scale not in other.SCALES or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For Time
, it should not be possible for self.scale
or other.scale
to be None
. Also, the sets are symmetric, so one only needs to check that other.scale in self.SCALES
.
astropy/time/tests/test_basic.py
Outdated
@@ -11,7 +11,7 @@ | |||
|
|||
from ...tests.helper import catch_warnings | |||
from ...utils import isiterable | |||
from .. import Time, ScaleValueError, TIME_SCALES, TimeString, TimezoneInfo | |||
from .. import Time, ScaleValueError, TIME_SCALES, TIME_TYPES, TimeString, TimezoneInfo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted, I think TIME_TYPES
should not be exported (since TIME_DELTA_TYPES
is not either). Also, I think the tests are clearer if they do not use TIME_TYPES['tt']
but rather STANDARD_TIME_SCALES
. So,
from ..core import STANDARD_TIME_SCALES
astropy/time/tests/test_basic.py
Outdated
@@ -317,10 +317,10 @@ def test_all_transforms(self): | |||
except reversibility [#2074]""" | |||
lat = 19.48125 | |||
lon = -155.933222 | |||
for scale1 in TIME_SCALES: | |||
for scale1 in TIME_TYPES['tt']: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use STANDARD_TIME_SCALES
here and below
astropy/time/tests/test_delta.py
Outdated
@@ -7,7 +7,7 @@ | |||
import pytest | |||
|
|||
from .. import (Time, TimeDelta, OperandTypeError, ScaleValueError, | |||
TIME_SCALES, TIME_DELTA_SCALES) | |||
TIME_SCALES,TIME_TYPES, TIME_DELTA_SCALES) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above.
@vermashresth - now see your comment in #6487 - Above, I suggested importing
and similarly for For the table tests, the failures are related to a worry I expressed above: for formats with a reference time, it is assumed that the time passed in can be transformed to the scale of that reference time. For "local", this is not possible, and hence the format conversion fails. I think in principle, it may be possible to get those conversions to work, but as a first go, I think it is fine to edit the table test to work with |
Thanks for the review! @mhvk |
9e90960
to
4528e91
Compare
Hi! @mhvk |
Hi @mhvk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very good overall, so I've started to get to pep8 nitpicks... But more importantly, could you add a few notes to the documentation? Both of for Time
itself and for the related fits stuff.
Also, it seems time to ask @taldcroft to have a bit of a look.
astropy/time/core.py
Outdated
out._set_scale(other.scale if other.scale is not None | ||
else 'tai') | ||
if other.scale is not None and self.scale not in TIME_TYPES[other.scale]: | ||
raise TypeError("Cannot subtract Time and TimeDelta instances with scales '{0}' and '{1}'".format(self.scale, other.scale)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very long line. Could you break it up?
astropy/time/core.py
Outdated
# remove attributes that are invalidated by changing time | ||
for attr in ('_delta_ut1_utc', '_delta_tdb_tt'): | ||
if hasattr(out, attr): | ||
delattr(out, attr) | ||
|
||
else: # T - T | ||
|
||
# the scales should be compatible (e.g., cannot convert TDB to LOCAL) | ||
if(other.scale not in self.SCALES): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No reason for the parentheses, nor really for the black line (since there is a comment)
astropy/time/core.py
Outdated
@@ -1386,8 +1396,11 @@ def __add__(self, other): | |||
if other.scale not in (out.scale, None): | |||
other = getattr(other, out.scale) | |||
else: | |||
out._set_scale(other.scale if other.scale is not None else 'tai') | |||
|
|||
if other.scale is not None and self.scale not in TIME_TYPES[other.scale]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment on the flow; just start with if other.scale is None
(as above).
astropy/time/core.py
Outdated
else 'tai') | ||
if other.scale is not None and self.scale not in TIME_TYPES[other.scale]: | ||
raise TypeError("Cannot subtract Time and TimeDelta instances with scales '{0}' and '{1}'".format(self.scale, other.scale)) | ||
else: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the logic is a bit odd here with an implicitly nested if. I would do
if other.scale is None:
out._set_scale('tai')
else:
if self.scale not in TIME_TYPES[other.scale]:
raise ...
self._set_scale(other.scale)
astropy/time/formats.py
Outdated
tm = getattr(parent, self.epoch_scale) | ||
except Exception as err: | ||
raise ScaleValueError("Cannot convert from '{0}' epoch scale '{1}'" | ||
"to specified scale '{2}', got error:\n{3}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please align properly.
astropy/time/tests/test_basic.py
Outdated
ScalevalueError | ||
""" | ||
t = Time('2006-01-15 21:24:37.5', scale='local') | ||
assert t.jd == 2453751.3921006946 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worry this may (start to) fail due to rounding errors on some platforms. Maybe
assert_allclose(t.jd, 2453751.3921006946, atol=0.001/3600./24., rtol=0.)
astropy/time/tests/test_basic.py
Outdated
t = Time('2006-01-15 21:24:37.5', scale='local') | ||
assert t.jd == 2453751.3921006946 | ||
assert t.mjd == 53750.892100694444 | ||
assert t.decimalyear == 2006.0408002758752 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the year, atol
would need to be further divided by 365.
astropy/time/tests/test_delta.py
Outdated
I.e., time differences of two times should have the scale of the | ||
first time. | ||
|
||
There are no local timescales for which this does not work. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a silly statement, since there is only one!
astropy/time/tests/test_delta.py
Outdated
assert t1_recover_t2_scale.scale == scale2 | ||
t1_recover = getattr(t1_recover_t2_scale, scale1) | ||
assert allclose_jd(t1_recover.jd, t1.jd) | ||
t2_recover_t1_scale = t1 - dt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, also add a test with a TimeDelta(..., scale=None)
as well as just subtracting a quantity with time units.
@@ -264,7 +264,7 @@ both" [#]_. See also [#]_ and [#]_. | |||
:: | |||
|
|||
>>> Time.SCALES | |||
('tai', 'tcb', 'tcg', 'tdb', 'tt', 'ut1', 'utc') | |||
('tai', 'tcb', 'tcg', 'tdb', 'tt', 'ut1', 'utc', 'local') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also adjust the text so that it becomes clear what 'local' is, maybe including the table below.
Hi @mhvk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vermashresth - I made another review, mostly nitpicks and tiny details, and a bit more on the documentation. As noted inline, I don't think we have to change the graph of the scale transformations, but we should note that local
is excluded from it.
astropy/table/tests/test_mixin.py
Outdated
@pytest.mark.parametrize('table_types', (Table, QTable)) | ||
def test_io_time_write_fits_local(tmpdir, table_types): | ||
""" | ||
Test that table with Time mixin columns can be written by io.fits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the docstring was just copy-pasted from the function above. Could you update it? (i.e., state that this tests the local
time scale, and describe why it cannot be included above)?
astropy/time/tests/test_delta.py
Outdated
@@ -268,15 +269,15 @@ def test_delta_scales_definition(self): | |||
TimeDelta([0., 1., 10.], format='sec', scale='utc') | |||
|
|||
@pytest.mark.parametrize(('scale1', 'scale2'), | |||
list(itertools.product(TIME_SCALES, TIME_SCALES))) | |||
def test_scales_for_time_minus_time(self, scale1, scale2): | |||
list(itertools.product(STANDARD_TIME_SCALES, STANDARD_TIME_SCALES))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: could you break the line? It is rather long now.
astropy/time/tests/test_delta.py
Outdated
@@ -297,6 +298,45 @@ def test_scales_for_time_minus_time(self, scale1, scale2): | |||
t2_recover = getattr(t2_recover_t1_scale, scale2) | |||
assert allclose_jd(t2_recover.jd, t2.jd) | |||
|
|||
def test_local_scales_for_time_minus_time(self): | |||
"""T(X) - T2(Y) -- does T(X) - T2(Y).X and return dT(X) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please again update the docstring to reflect what this test does.
astropy/time/tests/test_delta.py
Outdated
Also tests that subtracting two time scales, one of which is | ||
different from local time scale should raise TypeError. | ||
""" | ||
scale1 = 'local' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than copy the test from above, can we just write 'local'? That makes the test easier to understand, and we're not iterating over different scales here.
astropy/time/tests/test_delta.py
Outdated
# now check with delta time; also check reversibility | ||
t1_recover_t2_scale = t2 + dt | ||
assert t1_recover_t2_scale.scale == scale2 | ||
t1_recover = getattr(t1_recover_t2_scale, scale1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pointless operation when the scale is already 'local'; just call the above t1_recover
instead of t1_recover_t2_scale
.
docs/time/index.rst
Outdated
@@ -169,8 +169,8 @@ yday :class:`~astropy.time.TimeYearDayTime` 2000:001:00:00:0 | |||
=========== ================================================= ============================== | |||
|
|||
.. note:: The :class:`~astropy.time.TimeFITS` format allows for most | |||
but not all of the the FITS standard [#]_. Not implemented (yet) is | |||
support for a ``LOCAL`` timescale. Furthermore, FITS supports some deprecated | |||
but not all of the the FITS standard [#]_. It also supports the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
.. note:: The :class:`~astropy.time.TimeFITS` format implements most
of the FITS standard [#]_, including support for the ``LOCAL`` timescale.
Note, though, that FITS supports some deprecated
docs/time/index.rst
Outdated
@@ -276,13 +276,18 @@ tdb Barycentric Dynamical Time (TDB) | |||
tt Terrestrial Time (TT) | |||
ut1 Universal Time (UT1) | |||
utc Coordinated Universal Time (UTC) | |||
local Local Time Scale (LOCAL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is typeset with the (...)
part next to the text, maybe just right-adjust (LOCAL)
to the end of the column (and move (TT)
out by one space)
docs/time/index.rst
Outdated
====== ================================= | ||
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete extraneous spaces.
docs/time/index.rst
Outdated
.. [#] Wikipedia `time standard <https://en.wikipedia.org/wiki/Time_standard>`_ article | ||
.. [#] SOFA Time Scale and Calendar Tools | ||
`(PDF) <http://www.iausofa.org/sofa_ts_c.pdf>`_ | ||
.. [#] `<http://www.ucolick.org/~sla/leapsecs/timescales.html>`_ | ||
|
||
.. note:: ``local`` TimeScale is a scale that cannot be converted to/from any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe nice to tell what it is meant for. E.g,
.. note:: the ``local`` time scale is meant for free-running clocks or simulation times,
i.e., to represent a time without properly defined scale. It this cannot be converted
to any other scale, and arithmetic is possible only with |Time| instances with scale
``local`` and with |TimeDelta| instances with scale ``local`` or `None`.
any other timescale)
docs/time/index.rst
Outdated
.. note:: ``local`` TimeScale is a scale that cannot be converted to/from any | ||
other scale and to which only TimeDelta with ``None`` type scale could be | ||
added or subtracted. | ||
|
||
The system of transformation between supported time scales is shown in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think maybe here note again that local
is excluded; then we do not have to update the figure. E.g.,
The system of transformations between the standard time scales (i.e., all but ``local``)
is shown in the ...
Done :) |
Indeed, time for a changelog entry! |
@mhvk Is there any more work needed on this? |
@vermashresth - I think it is all OK, but would like to have a last look. Unfortunately, haven't had time to do so yet. As for the comment about |
@vermashresth - I finally got around to a final review of your nice PR. To ensure we can merge it, I went ahead and just made a commit with some further small changes. I'll now merge, assuming tests pass. |
OK, I guess I need to rebase as well so we can get the changelog properly set up... Will do that. |
OK, merged. Thanks, @vermashresth! |
Finally :D |
issue #6487
Added the ability to use
local
time scale forTime
andTimeDelta
. Operations that are valid nowSubtracting two
Time
instances of 'local' orNone
type.Subtracting/Adding
Time
andTimeDelta
instances oflocal
orNone
type.local
typeTime
orTimeDelta
can only be converted to/fromlocal
type, all other types are invalid.Also made changes to existing tests, because not all transformations between
Time
are valid now.