Python - datetime

From PeformIQ Upgrade
Jump to navigation Jump to search

References

Precision

Here's a simple two-liner to calculate roughly how many YEARS you can squeeze into what's left of the 53 bits of precsion in an IEEE754 64-bit float:

 >>> import math
 >>> 10 ** (math.log10(2 ** 53) - math.log10(60 * 60 * 24) - 6) / 365.25
 285.42092094268787
 >>>

Watch out for round-off; add the smallest non-zero numbers first:

return timedelta.seconds + timedelta.microseconds / 1E6 + timedelta.days * 86400

Examples

Simple Example

#!/usr/bin/env python

import datetime

start_date = datetime.datetime(2007,01,01)
end_date   = datetime.datetime(2008,03,11)

print start_date
print end_date

date = start_date

print 'Date'

while date < end_date:
   print date.strftime('%Y-%m-%d')

   date += datetime.timedelta(days=1)

Examples of Range of Functions

#!/usr/bin/env python
#
#  $Id:$
#
#---------------------------------------------------------------------

import sys
import getopt

from datetime import datetime, timedelta

#---------------------------------------------------------------------
"""
From datetime documentation...

def astimezone(self, tz):
   if self.tzinfo is tz:
      return self
   # Convert self to UTC, and attach the new time zone object.
   utc = (self - self.utcoffset()).replace(tzinfo=tz)
   # Convert from UTC to tz's local time.
   return tz.fromutc(utc)
"""

#---------------------------------------------------------------------

def display(dt):
   print "  timetuple() -> %s" % dt.timetuple()
   print "   strftime() -> %s" % dt.strftime("%Y-%m-%d %H:%M:%S")
   print "isocalendar() -> %s" % str(dt.isocalendar())
   print "    weekday() -> %d" % dt.weekday()
   print " isoweekday() -> %d" % dt.isoweekday()
   print "  isoformat() -> %s" % dt.isoformat()
   print "    __str__() -> %s" % dt.__str__()
   print "      ctime() -> %s" % dt.ctime()
   print "     tzname() -> %s" % dt.tzname()
   print "        dst() -> %s" % dt.dst()
   print "     timetz() -> %s" % dt.timetz()

#---------------------------------------------------------------------

def test():
   now = datetime.now()

   print "now():"
   display(now)

   utcnow = datetime.utcnow()

   print "utcnow():"
   display(utcnow)

   plus_1_day = now + timedelta(days=1)

   print "plus_1_day():"
   display(plus_1_day)

   plus_2_day = now + timedelta(days=2)

   print "plus_2_day():"
   display(plus_2_day)

#---------------------------------------------------------------------

def usage():
   USAGE = """
   
     Usage:
     
       $ dt.py
    
   """
   
   sys.stderr.write(USAGE)
   
#---------------------------------------------------------------------

def main(argv):
   global logfile, verbose_flg

   #----- Process command line arguments ----------------------------

   try:
      opts, args = getopt.getopt(argv, "hl:u:t", ["help", "logfile=", "verbose"])
   except getopt.GetoptError:
      usage()
      sys.exit(2)
   else:
      for opt, arg in opts:
         if opt in ("-h", "--help"):
            usage()
            sys.exit(0)
         elif opt in ("-l", "--logfile"):
            logfile = arg
         elif opt in ("-v", "--verbose"):
            verbose_flg = True

   test()

#---------------------------------------------------------------------

if __name__ == "__main__":
   main(sys.argv[1:])

#---------------------------------------------------------------------

Exploring datetime and time Modules

#!/usr/bin/env python

import time
import datetime
import pprint


print datetime.datetime.now()
print datetime.datetime.now().date()
print datetime.datetime.now().date().isoformat()
print datetime.datetime.now().time()

print datetime.__doc__

print
print

print "time.time() -> ", time.time()

print

pp = pprint
pp.pprint(time.__dict__)

print time.__doc__

Timezone Examples

>>> import datetime  
>>> from dateutil import tz # python dateutil offers loads of convenience: http://labix.org/python-dateutil
>>> dt = datetime.datetime(2008, 8, 21, 12, 51, 0, 0, tz.tzlocal())
>>> print repr(dt), ":", dt
datetime.datetime(2008, 8, 21, 12, 51, tzinfo=tzlocal()) : 2008-08-21 12:51:00+02:00
>>> dt_utc = dt.astimezone(tz.tzutc())
>>> print repr(dt_utc), ":", dt
datetime.datetime(2008, 8, 21, 10, 51, tzinfo=tzutc()) : 2008-08-21 12:51:00+02:00
>>> dt == dt_utc

Using strptime() and strftime()

  from datetime import datetime

  dt = datetime.strptime('10:13:15 2006-03-07', '%H:%M:%S %Y-%m-%d')

  ts = datetime.now().strftime('%Y%m%d%H%M%S')
   rdt = task.fields.ReminderDateTime

   if rdt:
      ctx.locals.ReminderDate = rdt.strftime('%Y-%m-%d')
      ctx.locals.ReminderTime = rdt.hour * 60 + rdt.minute
   else:
      ctx.locals.ReminderDate = ctx.locals.Today
      ctx.locals.ReminderTime = 630
   from time import strptime

   ...

      'ReminderDateTime'        : datetime(*strptime(ctx.locals.ReminderDateTime, '%Y-%m-%d')[0:6]) + timedelta(minutes=int(ctx.locals.ReminderTime)),

Python Documentation

Timezone

from datetime import tzinfo, timedelta, datetime

ZERO = timedelta(0)
HOUR = timedelta(hours=1)

# A UTC class.

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

# A class building tzinfo objects for fixed-offset time zones.
# Note that FixedOffset(0, "UTC") is a different way to build a
# UTC tzinfo object.

class FixedOffset(tzinfo):
    """Fixed offset in minutes east from UTC."""

    def __init__(self, offset, name):
        self.__offset = timedelta(minutes = offset)
        self.__name = name

    def utcoffset(self, dt):
        return self.__offset

    def tzname(self, dt):
        return self.__name

    def dst(self, dt):
        return ZERO

# A class capturing the platform's idea of local time.

import time as _time

STDOFFSET = timedelta(seconds = -_time.timezone)
if _time.daylight:
    DSTOFFSET = timedelta(seconds = -_time.altzone)
else:
    DSTOFFSET = STDOFFSET

DSTDIFF = DSTOFFSET - STDOFFSET

class LocalTimezone(tzinfo):

    def utcoffset(self, dt):
        if self._isdst(dt):
            return DSTOFFSET
        else:
            return STDOFFSET

    def dst(self, dt):
        if self._isdst(dt):
            return DSTDIFF
        else:
            return ZERO

    def tzname(self, dt):
        return _time.tzname[self._isdst(dt)]

    def _isdst(self, dt):
        tt = (dt.year, dt.month, dt.day,
              dt.hour, dt.minute, dt.second,
              dt.weekday(), 0, -1)
        stamp = _time.mktime(tt)
        tt = _time.localtime(stamp)
        return tt.tm_isdst > 0

Local = LocalTimezone()


# A complete implementation of current DST rules for major US time zones.

def first_sunday_on_or_after(dt):
    days_to_go = 6 - dt.weekday()
    if days_to_go:
        dt += timedelta(days_to_go)
    return dt

# In the US, DST starts at 2am (standard time) on the first Sunday in April.
DSTSTART = datetime(1, 4, 1, 2)
# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct.
# which is the first Sunday on or after Oct 25.
DSTEND = datetime(1, 10, 25, 1)

class USTimeZone(tzinfo):

    def __init__(self, hours, reprname, stdname, dstname):
        self.stdoffset = timedelta(hours=hours)
        self.reprname = reprname
        self.stdname = stdname
        self.dstname = dstname

    def __repr__(self):
        return self.reprname

    def tzname(self, dt):
        if self.dst(dt):
            return self.dstname
        else:
            return self.stdname

    def utcoffset(self, dt):
        return self.stdoffset + self.dst(dt)

    def dst(self, dt):
        if dt is None or dt.tzinfo is None:
            # An exception may be sensible here, in one or both cases.
            # It depends on how you want to treat them.  The default
            # fromutc() implementation (called by the default astimezone()
            # implementation) passes a datetime with dt.tzinfo is self.
            return ZERO
        assert dt.tzinfo is self

        # Find first Sunday in April & the last in October.
        start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
        end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))

        # Can't compare naive to aware objects, so strip the timezone from
        # dt first.
        if start <= dt.replace(tzinfo=None) < end:
            return HOUR
        else:
            return ZERO

Eastern  = USTimeZone(-5, "Eastern",  "EST", "EDT")
Central  = USTimeZone(-6, "Central",  "CST", "CDT")
Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
Pacific  = USTimeZone(-8, "Pacific",  "PST", "PDT")

Datetime Woes

See: http://blog.mfabrik.com/2008/06/30/relativity-of-time-shortcomings-in-python-datetime-and-workaround/

# Copyright (c) 2008, Red Innovation Ltd., Finland
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of Red Innovation nor the names of its contributors 
#       may be used to endorse or promote products derived from this software 
#       without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL RED INNOVATION BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

__doc__    = """
This module provides monkey-patched Python datetime class
that fully supports different time zones and conversions
between them.

See the source for licensing terms.
"""

__author__    = 'Antti Haapala <antti@redinnovation.com>'
__date__      = '24 Jun 2008'
__version__   = '$Revision$'
__copyright__ = '2008 Red Innovation Ltd.'
__license__   = '3-clause BSD'

from datetime import datetime as _datetime, tzinfo as _tzinfo
from pytz import timezone as _timezone

import time as _time
import pytz as _pytz
import math as _math
import re as _re
from pytz import utc, UTC, HOUR, ZERO

_utc = _pytz.utc
_default_tz = _utc


def set_default_timezone(new_tz):
    """Sets the default time zone used by the objects
       contained in this module. new_tz may be either
       a pytz-compatible tzinfo (requires normalize 
       and localize methods), or a time zone name known
       to pytz.
    """

    global _default_tz
    if type(new_tz) is str or type(new_tz) is unicode:
        new_tz = _pytz.timezone(new_tz)

    _default_tz = new_tz

class FixedOffset(_tzinfo):
    """Fixed offset in minutes east from UTC. Based on 
       the python tutorial and pytz test code."""

    def __init__(self, offset, name):
        """Constructor. Create a new tzinfo object
        with given offset in minutes and name."""
        self.__offset = timedelta(minutes = offset)
        self.__name = name

    def utcoffset(self, dt):
        return self.__offset

    def tzname(self, dt):
        return self.__name

    def dst(self, dt):
        return ZERO

    def localize(self, dt, is_dst=False):
        """Convert naive time to local time. Copied 
        from pytz tzinfo classes"""

        if dt.tzinfo is not None:
            raise ValueError, 'Not naive datetime (tzinfo is already set)'

        return dt.replace(tzinfo=self)

    def normalize(self, dt, is_dst=False):
        """Correct the timezone information on the 
        given datetime. Copied from pytz tzinfo classes."""

        if dt.tzinfo is None:
            raise ValueError, 'Naive time - no tzinfo set'

        return dt.replace(tzinfo=self)

    def __str__(self):
        return self.__name

    def __repr__(self):
        return '<%s>' % self.__name


_fixed_offset_tzs = { }

def _get_fixed_offset_tz(offsetmins):
    """For internal use only: Returns a tzinfo with 
    the given fixed offset. This creates only one instance
    for each offset; the zones are kept in a dictionary"""

    if offsetmins == 0:
        return _utc

    if not _fixed_offset_tzs.has_key(offsetmins):
       if offsetmins < 0:
           sign = '-'
           absoff = -offsetmins
       else:
           sign = '+'
           absoff = offsetmins

       name = "UTC%s%02d:%02d" % (sign, int(absoff / 60), absoff % 60)
       inst = FixedOffset(offsetmins, name)
       _fixed_offset_tzs[offsetmins] = inst

    return _fixed_offset_tzs[offsetmins]


_iso8601_parser = _re.compile("""
    ^
    (?P<year> [0-9]{4})(?P<ymdsep>-?)
    (?P<month>[0-9]{2})(?P=ymdsep)
    (?P<day>  [0-9]{2})

    (?: # time part... optional... at least hour must be specified
	(?:T|\s+)
        (?P<hour>[0-9]{2})
        (?:
            # minutes, separated with :, or none, from hours
            (?P<hmssep>[:]?)
            (?P<minute>[0-9]{2})
            (?:
                # same for seconds, separated with :, or none, from hours
                (?P=hmssep)
                (?P<second>[0-9]{2})
            )?
        )?
        
        # fractions
        (?: [,.] (?P<frac>[0-9]{1,10}))?

        # timezone, Z, +-hh or +-hh:?mm. MUST BE, but complain if not there.
        (
            (?P<tzempty>Z) 
        | 
            (?P<tzh>[+-][0-9]{2}) 
            (?: :? # optional separator 
                (?P<tzm>[0-9]{2})
            )?
        )?
    )?
    $
""", _re.X) # """

def _parse_iso(timestamp):
    """Internal function for parsing a timestamp in 
    ISO 8601 format"""

    timestamp = timestamp.strip()
    
    m = _iso8601_parser.match(timestamp)
    if not m:
        raise ValueError("Not a proper ISO 8601 timestamp!")

    year  = int(m.group('year'))
    month = int(m.group('month'))
    day   = int(m.group('day'))
    
    h, min, s, us = None, None, None, 0
    frac = 0
    if m.group('tzempty') == None and m.group('tzh') == None:
        raise ValueError("Not a proper ISO 8601 timestamp: " +
                "missing timezone (Z or +hh[:mm])!")

    if m.group('frac'):
        frac = m.group('frac')
        power = len(frac)
        frac  = long(frac) / 10.0 ** power

    if m.group('hour'):
        h = int(m.group('hour'))

    if m.group('minute'):
        min = int(m.group('minute'))

    if m.group('second'):
        s = int(m.group('second'))

    if frac != None:
        # ok, fractions of hour?
        if min == None:
           frac, min = _math.modf(frac * 60.0)
           min = int(min)

        # fractions of second?
        if s == None:
           frac, s = _math.modf(frac * 60.0)
           s = int(s)

        # and extract microseconds...
        us = int(frac * 1000000)

    if m.group('tzempty') == 'Z':
        offsetmins = 0
    else:
        # timezone: hour diff with sign
        offsetmins = int(m.group('tzh')) * 60
        tzm = m.group('tzm')
      
        # add optional minutes
        if tzm != None:
            tzm = long(tzm)
            offsetmins += tzm if offsetmins > 0 else -tzm

    tz = _get_fixed_offset_tz(offsetmins)
    return datetime(year, month, day, h, min, s, us, tz)


class datetime(_datetime):
    """Time zone aware subclass of Python datetime.datetime"""

    __name__ = 'fixed_datetime.datetime'

    def __new__(cls, year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, is_dst=False):
        """Creates a localized timestamp with the given parameters.
        If tzinfo is omitted, the default time zone will be used."""

        if tzinfo == None:
            tzinfo = _default_tz

        dt = _datetime(year, month, day, hour, minute, second, microsecond)
        dt = tzinfo.localize(dt, is_dst=is_dst)
        return _datetime.__new__(
            cls, dt.year, dt.month, dt.day, 
            dt.hour, dt.minute, dt.second, 
            dt.microsecond, dt.tzinfo)

    def __radd__(self, addend):
        """Autonormalized addition of datetimes and timedeltas."""

        added = _datetime.__radd__(self, addend)
        added = self.tzinfo.normalize(added)
        return datetime.__from_datetime_with_tz(added)

    def __add__(self, addend):
        """Autonormalized addition of datetimes and timedeltas."""

        added = _datetime.__add__(self, addend)
        added = self.tzinfo.normalize(added)
        return datetime.__from_datetime_with_tz(added)

    def utctimetuple(self):
        """Return UTC time tuple, compatible with time.gmtime().

        Notice: the original datetime documentation is misleading:
        Calling utctimetuple() on a timezone-aware datetime will 
        return the tuple in UTC, not in local time."""
        return _datetime.utctimetuple(self)

    def astimezone(self, tzinfo):
        """Convert to local time in new timezone tz.

        The result is normalized across DST boundaries."""

        dt = _datetime.astimezone(self, tzinfo)
        dt = tzinfo.normalize(dt)
        return datetime.__from_datetime_with_tz(dt)

    @staticmethod
    def __from_datetime_with_tz(dt):
        """Internal: create a datetime instance from
        a timezone-aware instance of the builtin datetime type."""

        if dt.tzinfo == None:
            raise ValueError("The given datetime.datetime is not timezone-aware!")

        return datetime(dt.year, dt.month, dt.day,
            dt.hour, dt.minute, dt.second, dt.microsecond,
            dt.tzinfo)

    @staticmethod
    def fromtimestamp(timestamp, tz=None):
        """Tz's local time from POSIX timestamp."""

        bd = _time.gmtime(long(timestamp))

        us = 0
        if isinstance(timestamp, float):
            us  = timestamp % 1.0
            us *= 1000000

        args  = list(bd[:6])
        args += [ int(us), _utc ]

        _tmp = datetime(*args)

        if tz == None:
            tz = _default_tz

        rv = _tmp.astimezone(tz)
        return datetime.__from_datetime_with_tz(rv)

    @staticmethod
    def today(tz=None):
        """New datetime with tz's local day and time
        If tz is not specified, use the default timezone"""

        return datetime.fromtimestamp(long(_time.time()), tz)

    @staticmethod
    def now(tz=None):
        """New datetime with tz's local day and time
        If tz is not specified, use the default timezone"""

        return datetime.fromtimestamp(_time.time(), tz)

    @staticmethod
    def utcnow():
        """Return a new datetime representing UTC day and time."""

        return datetime.now(tz=_utc)

    @staticmethod
    def utcfromtimestamp():
        """Return a new UTC datetime from a POSIX timestamp (like time.time())."""

        return datetime.utcfromtimestamp(tz=_utc)

    @staticmethod
    def parseisoformat(timestamp):
        """Parses the given ISO 8601 compatible timestamp string 
        and converts it to fixed_datetime.datetime. The timestamp
        must conform to following formats:

             - the format is DATE SEP TIME TIMEZONE without
               any intervening spaces.

             - the date must be in format YYYY-MM-DD

             - the time may be either
                 * HH:MM:SS,FFFF
                 * HH:MM,FFFF
                 * HH,FFFF
               FFFF is the fractional part. Decimal point can be
               used too.

             - the time zone must be either Z, -HH:MM or +HH:MM

             - the date and time must be separated either by
               whitespace or single T letter

             - the separators - and : may all be omitted, or
               must all be present.

             Examples (Unix Epoch):

                 1970-01-01T00:00:00Z 
                 1970-01-01T00Z 
                 1969-12-31 19,5-04:30
                 19700101T030000+0300
        """

        return _parse_iso(timestamp)

    def isoformat(self, sep='T', short=False):
        """Returns the date represented by this instance
        in ISO 8601 conforming format. The separator
        is used to separate the date and time, and defaults
        to 'T'. This method supports both long and short 
        formats. The long format is
 
            YYYY-MM-DD HH:MM:SS[.FFFFFF]=HH:MM

        and short is
     
            YYYYMMDDTHHMMSS[.FFFFFF]=HHMM

        The fractional part is separated with decimal point and
        is omitted if microseconds stored in this datetime are
        0.
        """

        if not short:
            return _datetime.isoformat(self, sep)

        args = [ self.year, self.month, self.day, self.hour, self.minute, self.second ]

        fmt  = "%04d%02d%02dT%02d%02d%02d"
        tz   = "%s%02d%02d"

        if self.microsecond != 0:
            fmt += ".%06d"
            args += [ self.microsecond ]

        offset = self.tzinfo.utcoffset(self)
        tzseconds = offset.seconds + offset.days * 24 * 60 * 60
        sign = '+' if tzseconds >= 0 else '-'
        tzseconds = abs(tzseconds)
        tzout = tz % (sign, int(tzseconds / 3600), int((tzseconds / 60) % 60))

        dtout = fmt % tuple(args)        
        return dtout + tzout

from datetime import date, timedelta, time, tzinfo, MAXYEAR, MINYEAR