Date/Time Conversion

Download DateUtils

This utility class will convert different date time formats into the proper format to create a Python datetime object. It will use the current locate to determine differences that can not be determined in any other way such as 01/01/2010 meaning either dd/mm/yyyy or mm/dd/yyyy.

The methods are all classmethods.

A string value is passed into the DateUtils.toDatetime() method. If you know the format of your date or only want to allow a specific format that format can be passed into the format keyword argument.

Examples:

from dateutils import DateUtils

DateUtils.toDatetime('2010/04/05')
Out[2]: datetime.datetime(2010, 4, 5, 0, 0)

from dateutils import DateUtils

DateUtils.toDatetime('2010/04/05 23:20:59')
Out[3]: datetime.datetime(2010, 4, 5, 23, 20, 59)

from dateutils import DateUtils

DateUtils.toDatetime('20100405232059', format="yyyymmddHHMMSS")
Out[6]: datetime.datetime(2010, 4, 5, 23, 20, 59)

Exceptions

  1. A ValueError will be raised if the value does not match the format or if the format can not be determined.

  2. A KeyError will be raised if the the date time format is not supported.

#
# dateutils.py
#

import re, locale
from datetime import datetime


class DateUtils(object):
    __DATE_REGEX = r"[/.]+"
    __dateRegex = re.compile(__DATE_REGEX)
    __YMD = "yyyy-mm-dd hh:mm:ss"
    __MDY = "mm-dd-yyyy hh:mm:ss"
    __DMY = "dd-mm-yyyy hh:mm:ss"
    __YMD_NON_DLM = "yyyymmddHHMMSS"

    def __transposeFromYMDHMS(self, value):
        size = len(value)
        y = value[:4]
        m = value[5:7]
        d = value[8:10]
        H = M = S = 0
        if size >= 13: H = value[11:13]
        if size >= 16: M = value[14:16]
        if size >= 19: S = value[17:]
        return "%s-%s-%s %02i:%02i:%02i" % (y, m, d, H, M, S)

    def __transposeFromMDYHMS(self, value):
        size = len(value)
        m = value[:2]
        d = value[3:5]
        y = value[6:10]
        H = M = S = 0
        if size >= 13: H = value[11:13]
        if size >= 16: M = value[14:16]
        if size >= 19: S = value[17:]
        return "%s-%s-%s %02i:%02i:%02i" % (y, m, d, H, M, S)

    def __transposeFromDMYHMS(self, value):
        size = len(value)
        d = value[:2]
        m = value[3:5]
        y = value[6:10]
        H = M = S = 0
        if size >= 13: H = value[11:13]
        if size >= 16: M = value[14:16]
        if size >= 19: S = value[17:]
        return "%s-%s-%s %02i:%02i:%02i" % (y, m, d, H, M, S)

    def __transposeNonDelimiterYMDHMS(self, value):
        size = len(value)
        y = value[:4]
        m = value[4:6]
        d = value[6:8]
        H = M = S = 0
        if size >= 10: H = value[8:10]
        if size >= 12: M = value[10:12]
        if size >= 14: S = value[12:]
        return "%s-%s-%s %02i:%02i:%02i" % (y, m, d, H, M, S)

    __FROM_DATE_FORMATS = {__YMD: __transposeFromYMDHMS,
                           __MDY: __transposeFromMDYHMS,
                           __DMY: __transposeFromDMYHMS,
                           __YMD_NON_DLM: __transposeNonDelimiterYMDHMS}

    @classmethod
    def toDatetime(self, value, format=None):
        """
        Convert a string representation of a date and time to a python
        datetime object.

        @param value: The string value to convert.
        @keyword format: The format to convert from.
        @return: A Python datetime object.
        """
        if format is None: format = DateUtils._snoopDateFormat(value)
        fmt = self.__dateRegex.sub('-', format)

        try:
            value = self.__FROM_DATE_FORMATS[fmt](self, value)
            date, time = value.split(' ')
        except KeyError, e:
            msg = "Invalid date format should be one of %s."
            raise KeyError(msg % self.__FROM_DATE_FORMATS.keys())
        except ValueError, e:
            msg = "The value %s does not match the format %s, %s"
            raise ValueError(msg % (value, format, e))

        try:
            return datetime(*[int(float(x)) for x in date.split("-")] +
                            [int(float(x)) for x in time.split(":")])
        except Exception, e:
            msg = "Invalid format for datetime %s, found: %s, %s" % \
                  (fmt, value, e)
            raise Exception(msg)

    @classmethod
    def _snoopDateFormat(self, value):
        """
        We determine what we can then rely on the current systems locale
        setting to determine the difference of 01/01/2010 to mean mm/dd/yyy
        or dd/mm/yyyy.
        """
        fmt = None
        delim = any([char for char in value if char in ('-', '.', '/', ':')])
        year = value[:4].isdigit()
        lfmt = locale.nl_langinfo(locale.D_FMT)

        if year and not delim:
            fmt = self.__YMD_NON_DLM
        elif year and delim:
            fmt = self.__YMD
        elif lfmt[1] == 'm':
            fmt = self.__MDY
        elif lfmt[1] == 'd':
            fmt = self.__DMY
        else:
            msg = "Invalid date time format: %s"
            raise ValueError(msg % value)

        return fmt

    def getFormatTypes(self):
        return (self.__YMD, self.__MDY, self.__DMY, self.__YMD_NON_DLM)

    @classmethod
    def isValidDate(self, dObj):
        result = True
        year = dObj.year
        month = dObj.month
        day = dObj.day

        if year < datetime.MINYEAR or year > datetime.MAXYEAR:
            result = False

        if month < 1 or month > 12:
            result = False

        monthDays = list(self.__MONTH_DAYS)

        if 0 == year % 4 and 0 != year % 100 or 0 == year % 400:
            monthDays[1] = 27

        if day < 1 or day > monthDays[month-1]:
            result = False

        return result

    @classmethod
    def isValidTime(self, tObj):
        result = True
        hour = tObj.hour
        minute = tObj.minute
        second = tObj.second

        if hour < 0 or hour > 23:
            result = False

        if minute < 0 or minute > 59:
            result = False

        if second < 0 or second > 59:
            result = False

        return result

    @classmethod
    def isValidDateTime(self, dtObj):
        result = True

        if not DateUtils.isValidDate(dtObj):
            result = False

        if not DateUtils.isValidTime(dtObj):
            result = False

        return result