Source code for irfpy.util.utc

''' Module related to the universal time.

.. codeauthor:: Yoshifumi Futaana

The module has functionalities of:

- conversion among many formats in the universal time
- production of regularly gridded time interval

**Conversion**
This contais functionality of connverting among UTC-epoch,
datetime, Julday, matlab time, and mpl time.

You may also be interested in Matplotlib's package ``matplotlib.dates``
where several similar functions are defined.

.. table:: Conversions

    ================= ================= =============== ================================== ============= ================ ============
    From \ To         String expression UTC epoch       datetime.datetime                  julday.Julday matplotlib float matlab float
    ================= ================= =============== ================================== ============= ================ ============
    String expression                                   dateutil.parser.parse()
    UTC epoch                                           dateutime.datetime.fromtimestamp()
    datetime.datetime obj.strftime()    obj.timestamp()
    julday.Julday
    matplotlib float
    matlab float
    ================= ================= =============== ================================== ============= ================ ============

.. todo::

    Fill in the table specifying the conversion of times.

.. todo::

    Refactor the functions to coordinate the consistency in the function names for time conversion.

**Regular gridding**

- :func:`irfpy.util.utc.dtrange`: Return a list of equally-gridded datetime objects
- :func:`irfpy.util.utc.trange`: Return a generator of equally-gridded datetime objects

'''
import datetime
import calendar
import logging
_logger = logging.getLogger(__name__)

import dateutil.parser

from matplotlib.dates import date2num, num2date
import matplotlib.dates as mpldates

from irfpy.util import julday


[docs]def datestr2mplnum(string_date): """ Date expression of string to maplotlib.dates float It may be similar to :func:`matplotlib.dates.datestr2num` but when used in np.genfromtxt converter, the datestr2num fails due to the toordinary conversion exception. >>> s = '2014-10-30T15:27:13.332' >>> n1 = datestr2mplnum(s) >>> print(n1) 735536.6439043055 >>> n2 = mpldates.datestr2num(s) >>> print(n2) 735536.6439043055 """ dd = dateutil.parser.parse(string_date) n = mpldates.date2num(dd) return n
[docs]def dt2tt(dt): '''Convert datetime object to epoch time ''' return calendar.timegm(dt.utctimetuple()) + dt.microsecond / 1e6
[docs]def tt2dt(tt): '''Convert epoch time to datetime instance ''' return datetime.datetime.utcfromtimestamp(tt)
[docs]def dt2jd(dt): '''Convert datetime object to irfpy.util.julday.Julday object. ''' return julday.Julday(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second + dt.microsecond / 1e6)
[docs]def jd2dt(jd): '''Convert irfpy.util.julday.Julday object to datetime.datetime object ''' return jd.getDatetime()
[docs]def tt2jd(tt): dt = tt2dt(tt) return dt2jd(dt)
[docs]def jd2tt(jd): dt = jd2dt(jd) return dt2tt(dt)
[docs]def mat2dt(matlabtime, remove_tz=True): ''' Convert matlab floating time to ``datetime.datetime`` instance. :param matlabtime: Matlab time. :type matlabtime: ``float`` :param remove_tz: Time zone (UTC) is removed if set True. Keep as it was if set False. ``num2data`` function add `tzinfo` as UTC, while most of the ``irfpy`` library does not. So by default, the information will be removed. :type remove_tz: ``boolean`` Matlab time is a ``float`` and similar to Julian day, but offset is different. Also ``matplotlib`` has different offset. Thus, this method will convert Matlab time to ``datatime.datetime`` instance. To convert ``matplotlib`` time to ``datetime.datetime``, you can use ``matplotlib.dates.num2date`` method. :param matlabtime: Matlab time :returns: ``datetime.datetime`` >>> from matplotlib.dates import num2date, date2num >>> mattime = 733408 # corresponding to 2008-01-01 00:00:00 >>> pytime = mat2dt(mattime) >>> print(pytime.strftime('%F %T')) 2008-01-01 00:00:00 >>> print(pytime.strftime('%F %T %Z').strip()) 2008-01-01 00:00:00 >>> pytime2 = mat2dt(mattime, remove_tz=False) >>> print(pytime2.strftime('%F %T %Z')) 2008-01-01 00:00:00 UTC >>> mpltime = date2num(pytime) # If you use matplotlib time, take care! >>> print('%.1f' % mpltime) # This is different from mattime by 366 days. 733042.0 ''' dt = num2date(matlabtime - 366) if remove_tz: dt = dt.replace(tzinfo=None) return dt
[docs]def dt2mat(dt): ''' Convert the datetime.datetime instance to matlab floating time. Note that the function will return matlab time, not matplotlib time. They have a difference by 366. >>> t = datetime.datetime(2006, 12, 2, 15, 30, 0) >>> tmat = dt2mat(t) >>> print(tmat) # doctest: +ELLIPSIS 733013.645833... >>> trev = mat2dt(tmat) >>> print(trev) 2006-12-02 15:30:00 If you use date2num in matplotlib.dates module, you will get 366 day difference. This is matplotlib time. >>> print(date2num(t)) # doctest: +ELLIPSIS 732647.645833... ''' num = date2num(dt) + 366 return num
[docs]def convert(intime, outfmt=float): '''Convert between irfpy.util.julday.Julday, datetime.datetime, and time_t. :param intime: Time in supported format. :param outfmt: Specify the format. irfpy.util.julday.Julday, datetime, and time_t(float) is supported. Default is time_t. Supporeted time format is ``irfpy.util.julday.Julday``, ``datetime.datetime`` and time_t (=float). Matplotlib's number (also float) is *NOT* supported. ''' if isinstance(intime, julday.Julday): if outfmt == julday.Julday: return intime elif outfmt == datetime.datetime: return jd2dt(intime) elif outfmt == float: return jd2tt(intime) else: raise RuntimeError('Format unknown: %s' % outfmt) elif isinstance(intime, datetime.datetime): if outfmt == julday.Julday: return dt2jd(intime) elif outfmt == datetime.datetime: return intime elif outfmt == float: return dt2tt(intime) else: raise RuntimeError('Format unknown: %s' % outfmt) else: ### Try to interpret as a time_t. try: intime_flt = float(intime) except TypeError as e: raise RuntimeError('Input Format unknown: %s' % intime.__class__) if outfmt == julday.Julday: return tt2jd(intime_flt) elif outfmt == datetime.datetime: return tt2dt(intime_flt) elif outfmt == float: return intime else: raise RuntimeError('Format unknown: %s' % outfmt)
[docs]def ymd2doy(year, month, day): ''' Convert the calendar time to day of year. ''' origin = datetime.date(year, 1, 1) oneday = datetime.timedelta(days=1) origin = origin target = datetime.date(year, month, day) _logger.debug('Date of the day %s, origin of the year %s' % (target, origin)) diff = target - origin return diff.days + 1
[docs]def doy2ymd(year, doi): ''' Convert the day of year to the calendar day as a tuple of (month, day). If the speicified doy does not exist for the specified year, ValueError is raised. ''' origin = datetime.date(year, 1, 1) days = datetime.timedelta(days=(doi - 1)) result = origin + days if result.year != year: msg = 'Given doi (%d) does not exist for the year %d' % (doi, year) _logger.error(msg) raise ValueError(msg) return result.month, result.day
[docs]def dtrange(t0, t1, dt): """ Return the equally stepped datetime object. Return the equally separated datatime objects between t0 and t1 (exclusive) with a step of dt. Indeed, this is an alias to :func:`irfpy.util.timeseries.dtrange`. >>> t0 = datetime.datetime(2014, 10, 1) >>> t1 = datetime.datetime(2014, 10, 10) >>> dt = datetime.timedelta(hours=12) >>> tlist = dtrange(t0, t1, dt) >>> print(len(tlist)) 18 >>> print(tlist[5]) 2014-10-03 12:00:00 >>> print(tlist[-1]) 2014-10-09 12:00:00 """ from irfpy.util import timeseries return timeseries.dtrange(t0, t1, dt)
[docs]def dtlinspace(t0, t1, ndiv): """ Return the equally separated datetime objects. :param t0: Start time :param t1: Stop time :param ndiv: Number of elements :return: A list of datetime object """ dt = (t1 - t0).total_seconds() dtlist = [t0 + datetime.timedelta(seconds=dt) * (i / (ndiv - 1)) for i in range(ndiv)] return dtlist
[docs]def trange(t0, t1, dt): """ Generator of equally separated datetime object. >>> t0 = datetime.datetime(2014, 1, 1) >>> t1 = datetime.datetime(2014, 1, 3) >>> dt = datetime.timedelta(hours=12) >>> for t in trange(t0, t1, dt): ... print(t) 2014-01-01 00:00:00 2014-01-01 12:00:00 2014-01-02 00:00:00 2014-01-02 12:00:00 Of course you can by enclosure. >>> tlist = [t.strftime('%FT%T') for t in trange(t0, t1, dt)] >>> print(tlist) ['2014-01-01T00:00:00', '2014-01-01T12:00:00', '2014-01-02T00:00:00', '2014-01-02T12:00:00'] """ t = t0 while t < t1: yield t t += dt
import unittest import doctest
[docs]def doctests(): return unittest.TestSuite(( doctest.DocTestSuite(), ))
if __name__ == '__main__': unittest.main(defaultTest='doctests')