skyfielders / python-skyfield

Elegant astronomy for Python
MIT License
1.42k stars 213 forks source link

timescale object works with .UTC but not with .UT1 #562

Closed aendie closed 3 years ago

aendie commented 3 years ago

I would like to confirm if this is a skyfield issue or am I doing something fundamentally illegal?

In both Python 2 and 3, I can reproduce an error on line 14 "t3 = ts.ut1(.......)" although line 12 "t2 = ts.utc(.......)" works.

Python 2 or 3 code (just change the first line to .../env python3):

#!/usr/bin/env python2
# -*- coding: UTF-8 -*-

import datetime
from skyfield.api import load
from pytz import timezone
utc = timezone('UTC')

ts = load.timescale()   # timescale object
PreviousNewMoon = datetime.datetime(2022, 11, 23, 22, 57, 13, 903886, tzinfo=utc)

t2 = ts.utc(PreviousNewMoon + datetime.timedelta(days=28))
print("t2 = " + str(t2))
t3 = ts.ut1(PreviousNewMoon + datetime.timedelta(days=28))
print("t3 = " + str(t3))

Python 2 hang:

image

Python 3 hang:

image

P.S. I forgot to mention that the Python 2 test was with Skyfield 1.30 and the Python 3 test was with Skyfield 1.37.

Bernmeister commented 3 years ago

I took a quick look at the documentation and it looks as if you have to pass the date components for UT1.

brandon-rhodes commented 3 years ago

I took a quick look at the documentation and it looks as if you have to pass the date components for UT1.

Yes, that's correct, @Bernmeister — thanks for the helpful link to the documentation!

Python datetime objects don't support UT1, so far as I know, but only UTC-derived time zones, so the problem of folks submitting UT1 date components as a datetime has never come up. If you do need to use the components of a datetime as though they were UT1, then simply pass the components dt.year, dt.month, and so forth as the arguments to ts.ut1().

Let me know if you run into any snags.

aendie commented 3 years ago

Ah ... the golden rule "READ THE DOCS". Thanks - I naturally assumed that ts.utc() and ts.ut1() were interchangeable.

The following code works in Python 3 and with a minor exception in Python 2:

import datetime
from skyfield.api import load, utc

ts = load.timescale()   # timescale object
PreviousNewMoon = datetime.datetime(2022, 11, 23, 22, 57, 13, 903886, tzinfo=utc)

t2 = ts.utc(PreviousNewMoon + datetime.timedelta(days=28))
print("t2 = " + str(t2))

dt3 = PreviousNewMoon + datetime.timedelta(days=28)
sec = dt3.second + dt3.microsecond/1000000.
t3 = ts.ut1(dt3.year, dt3.month, dt3.day, dt3.hour, dt3.minute, sec)
print("t3 = " + str(t3))

Result in Python 2 with Skyfield 1.30:

image

Result in Python 3 with Skyfield 1.37:

image

The minor exception in Python 2 is that you have to use sec = dt3.second + int(dt3.microsecond)/1000000. because dt3.microsecond returns a string in Python 2(.7.18)

One does not have to apply .replace(tzinfo=None) to dt3. HOWEVER: This is only correct when the datetime is in timezone 'utc'. For example: if it is in timezone 'est' even .replace(tzinfo=None) does not give the correct answer (of course). A time in another timezone has to be converted to timezone 'utc' first.

And ts.ut1() does not require the last argument , jd=None apparently.

Remember, read the ...

image001

brandon-rhodes commented 3 years ago

I'll expand the documentation of those methods in a few days to help make the various options clearer — in particular, to explain why there's an unused jd argument dangling at the end of ut1()!

brandon-rhodes commented 3 years ago

There, I've improved the docs to hopefully prevent ambiguity in the future. The online version should update the next time I do a release!