
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
