ephem.Date

PyEphem uses a simple floating point number to represent the date and time inside of its astronomy routines. While it would have been possible for PyEphem to conceal this by always converting dates into Python’s native datetime type before returning them, doing so — besides being slower — would have incurred a slight rounding error in every date returned, and the accumulation of this error could have caused problems for users who need high precision. So PyEphem provides its own ephem.Date type, which preserves the full precision of the dates it uses internally.

The ephem.Date type simply subclasses the Python float to provide it with some extra features. The most important is that it displays itself as a date when printed (and also when formatted with %s, and when converted with the str() function):

>>> import ephem
>>> d = ephem.Date('1984/05/30 16:23:45.12')
>>> print(d)
1984/5/30 16:23:45

But behind the scenes, each date is really a Python floating point number:

>>> isinstance(d, float)
True
>>> print('Behind the date %s is the number %f.' % (d, d))
Behind the date 1984/5/30 16:23:45 is the number 30831.183161.

PyEphem uses the modern Gregorian calendar unless you ask about a date from back before the Gregorian calendar started, in which case it switches to the old Julian calendar. Pope Gregory XIII made the date skip ten days ahead to resynchronize the months with the seasons when he instituted his new calendar. You can see this jump by asking about the date on either side of the change:

>>> print(ephem.Date(-115860.0))
1582/10/4 12:00:00
>>> print(ephem.Date(-115859.0))
1582/10/15 12:00:00

Time zones

PyEphem’s Date type itself does not support time zones. All PyEphem dates are expressed in Universal Time (UTC), which is similar to Standard Time in Greenwich, England. But if you need a time displayed in your local timezone, then you can use the PyEphem localtime function, which takes a PyEphem date and returns a Python datetime giving your local time.

>>> lt = ephem.localtime(d)
>>> print(lt)
1984-05-30 12:23:45.120000
>>> print(repr(lt))
datetime.datetime(1984, 5, 30, 12, 23, 45, 120000)

The output of this code will differ depending on the time zone in which you live. Since I am in the Eastern time zone, the time.localtime() call above subtracted four hours from 16:23, and returned 12:23 Eastern Daylight Time. (Note that Daylight Time was chosen because the date fell in May; had the date been in the winter, Standard Time would have been used.)

Conversions

PyEphem dates can be converted to and from several other representations.

Strings

As illustrated in the opening section above, dates can be initialized with strings, and can display themselves as strings on demand.

>>> d = ephem.Date('1984/05/30 16:23:45.12')
>>> print(d)
1984/5/30 16:23:45

The string you provide when creating a date can omit some of the numbers at the end. In fact, the only you have to specify is the year! The month, if not provided, defaults to January; the day defaults to the first of the month; and hours, minutes, and seconds default to zero.

>>> print(ephem.Date('1984/05/30 16'))
1984/5/30 16:00:00
>>> print(ephem.Date('1984'))
1984/1/1 00:00:00

Note that the string output does not include fractional seconds; you will have to use one of the other conversions, below, if you need to be that precise.

Datetime objects

When creating an ephem.Date, you can specify the date as either a date or datetime object from the datetime standard Python module. You can also ask a PyEphem date to convert itself the other direction by calling its datetime() method.

>>> from datetime import date, datetime, timezone
>>> print(ephem.Date(datetime(2005, 4, 18, 22, 15)))
2005/4/18 22:15:00
>>> d = ephem.Date('2000/12/25 12:41:16')
>>> d.datetime()
datetime.datetime(2000, 12, 25, 12, 41, 16)

In those last two commands, note that slight round-off error has converted sixteen seconds to 15.999999 seconds! The inevitability of such errors is why PyEphem exposes its own date type instead of returning Python datetime objects automatically.

Note that Python datetime objects use your local time by default, whereas PyEphem expects time to be expressed in UTC. This means that passing datetime.now() to PyEphem will give unexpected results. Instead, use the datetime.utcnow() constructor:

>>> d = datetime.utcnow()
>>> print(ephem.Date(d))
2015/12/14 15:42:14
>>> d = datetime.fromtimestamp(1450107734, tz=timezone.utc)
>>> print(ephem.Date(d))
2015/12/14 15:42:14
Tuples

PyEphem can return a date as a six-element tuple giving the year, month, day, hour, minute, and seconds, where the seconds include any fractions of a second. You can also provide such a tuple when creating a PyEphem date.

>>> timetuple = (1984, 5, 30, 12, 23, 45)
>>> print(ephem.Date(timetuple))
1984/5/30 12:23:45
>>> d = ephem.Date('2001/12/14 16:07:57')
>>> print(d.tuple())
(2001, 12, 14, 16, 7, 57.0)

Several functions in the Python standard module time will accept the time formatted as one of these six-element tuples. This feature was used in the Time Zones section, above, to convert a PyEphem date into local time.

Triples

There may be occasions where you need to manipulate the year and month but do not need to break the day into hours and minutes. In these cases, you can provide a three-item tuple (a “triple” of values) when creating a PyEphem date, and receive one back by calling the triple() method.

>>> timetriple = (1998, 2, 26.691458333334594)
>>> print(ephem.Date(timetriple))
1998/2/26 16:35:42
>>> d = ephem.Date('1996/4/17 22:37:11.5')
>>> print(d.triple())
(1996, 4, 17.94249421296263)
Floats

Finally, since a PyEphem date is really just a floating-point number, so you can manually supply the value you want it to have.

>>> print(ephem.Date(37238.1721875))
2001/12/14 16:07:57
>>> d = ephem.Date('2000/12/25 12:41:16')
>>> print('%.6f' % d)
36884.028657

For more information on what the floating point number means when interpreted as a date, see the next section.

Calculating with dates

PyEphem dates are encoded as the “Dublin Julian Day”, which is the number of days (including any fraction) that have passed since the last day of 1899, at noon. From there, increasing the value by one moves to the next day:

>>> print(ephem.Date(0))
1899/12/31 12:00:00
>>> print(ephem.Date(1))
1900/1/1 12:00:00
>>> print(ephem.Date(2))
1900/1/2 12:00:00

Negative numbers are also perfectly legitimate, and count backwards from the same reference point:

>>> print(ephem.Date(-1))
1899/12/30 12:00:00
>>> print(ephem.Date(-2))
1899/12/29 12:00:00

Fractions of a day, of course, move the time forward by hours within a single day. Note that doing math on a date returns a simple Python float, which you have to re-cast to an XEphem date if you want to display it:

>>> n = ephem.Date(7) + 0.5
>>> print(n)
7.5
>>> print(ephem.Date(n))
1900/1/8 00:00:00

To make math with dates more convenient, PyEphem provides constants hour, minute, and second that represent those three fractions of a day.

>>> print(ephem.Date(n + ephem.hour))
1900/1/8 01:00:00
>>> print(ephem.Date(n + ephem.minute))
1900/1/8 00:01:00
>>> print(ephem.Date(n + ephem.second))
1900/1/8 00:00:01
>>> print(ephem.Date(n + 12 * ephem.hour + 36 * ephem.minute))
1900/1/8 12:36:00