Attachment 'dateutils.py'

Download

   1 #
   2 # dateutils.py
   3 #
   4 # SVN Keywords
   5 #----------------------------------
   6 # $Author: $
   7 # $Date: $
   8 # $Revision: $
   9 #----------------------------------
  10 
  11 import re, locale, datetime
  12 
  13 
  14 class DateUtils(object):
  15     __SPLIT_REGEX = re.compile(r"(.*)([Tt ])(.*)")
  16     __DATE_REGEX = re.compile(r"[/.]+")
  17     __ZONE_REGEX = re.compile(r"(.*)([-Zz+])(.*)")
  18     __MONTH_NUM = {
  19         'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
  20         'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12
  21         }
  22     __KW_NAMES = ('hour', 'minute', 'second', 'microsecond', 'tzinfo')
  23     _YMD = "yyyy-mm-dd hh:mm:ss"
  24     _MDY = "mm-dd-yyyy hh:mm:ss"
  25     _DMY = "dd-mm-yyyy hh:mm:ss"
  26     _YMD_NON_DLM = "yyyymmddHHMMSS"
  27     _RFC2616_RFC1123 = "wkd, dd month yyyy hh:mm:ss GMT"
  28     _RFC2616_RFC850 = "weekday, dd-mmm-yy hh:mm:ss GMT"
  29     _RFC2616_ASCTIME = "wkd mmm dd hh:mm:ss yyyy"
  30     _RFC3339_ISO8601 = "yyyy-mm-ddTHH:MM:SS.ssssssZ"
  31 
  32     def __transposeFromYMDHMS(self, value):
  33         """
  34         yyyy-mm-dd hh:mm:ss[.ssssss]
  35         """
  36         value, dot, ms = value.partition('.')
  37         y = int(value[:4])
  38         m = int(value[5:7])
  39         d = int(value[8:10])
  40         size = len(value)
  41         H = size >= 13 and int(value[11:13]) or 0
  42         M = size >= 16 and int(value[14:16]) or 0
  43         S = size >= 19 and int(value[17:]) or 0
  44         ms = ms.isdigit() and int("{:<06d}".format(int(ms))) or 0
  45         return ((y, m, d), (H, M, S, ms, None))
  46 
  47     def __transposeFromMDYHMS(self, value):
  48         """
  49         mm-dd-yyyy hh:mm:ss[.ssssss]
  50         """
  51         value, dot, ms = value.partition('.')
  52         m = int(value[:2])
  53         d = int(value[3:5])
  54         y = int(value[6:10])
  55         size = len(value)
  56         H = size >= 13 and int(value[11:13]) or 0
  57         M = size >= 16 and int(value[14:16]) or 0
  58         S = size >= 19 and int(value[17:]) or 0
  59         ms = ms.isdigit() and int("{:<06d}".format(int(ms))) or 0
  60         return ((y, m, d), (H, M, S, ms, None))
  61 
  62     def __transposeFromDMYHMS(self, value):
  63         """
  64         dd-mm-yyyy hh:mm:ss[.ssssss]
  65         or
  66         dd-MMM-yyyy hh:mm:ss[.ssssss]
  67         """
  68         value, dot, ms = value.partition('.')
  69         d = int(value[:2])
  70         m = self.__MONTH_NUM.get(value[3:6].capitalize())
  71 
  72         if m:
  73             idx = 1
  74         else:
  75             idx = 0
  76             m = int(value[3:5])
  77 
  78         y = int(value[idx+6:idx+10])
  79         size = len(value)
  80         H = size >= (idx+13) and int(value[idx+11:idx+13]) or 0
  81         M = size >= (idx+16) and int(value[idx+14:idx+16]) or 0
  82         S = size >= (idx+19) and int(value[idx+17:]) or 0
  83         ms = ms.isdigit() and int("{:<06d}".format(int(ms))) or 0
  84         return ((y, m, d), (H, M, S, ms, None))
  85 
  86     def __transposeNonDelimiterYMDHMS(self, value):
  87         """
  88         yyyymmddHHMMSS
  89         """
  90         value, ms = len(value) > 14 and (value[:14], value[14:]) or (value, "")
  91         y = int(value[:4])
  92         m = int(value[4:6])
  93         d = int(value[6:8])
  94         size = len(value)
  95         H = size >= 10 and int(value[8:10]) or 0
  96         M = size >= 12 and int(value[10:12]) or 0
  97         S = size >= 14 and int(value[12:]) or 0
  98         ms = ms.isdigit() and int("{:<06d}".format(int(ms))) or 0
  99         return ((y, m, d), (H, M, S, ms, None))
 100 
 101     def __transposeRFC1123(self, value):
 102         """
 103         wkd mmm dd hh:mm:ss yyyy
 104         """
 105         d = int(value[5:7])
 106         m = self.__MONTH_NUM.get(value[8:11], 1) # Default to Jan
 107         y = int(value[12:16])
 108         H = int(value[17:19])
 109         M = int(value[20:22])
 110         S = int(value[23:25])
 111         return ((y, m, d), (H, M, S, 0, None))
 112 
 113     def __transposeRFC850(self, value):
 114         """
 115         weekday, dd-mmm-yy hh:mm:ss GMT
 116         """
 117         start = value.index(',') + 2
 118         d = int(value[start:start+2])
 119         m = self.__MONTH_NUM.get(value[start+3:start+3+3], 1) # Default to Jan
 120         y = value[start+7:start+7+2]
 121         # Assume life began at the UNIX epic 1970-01-01.
 122         y = int(int(y) < 70 and '20' + y or '19' + y)
 123         H = int(value[start+10:start+10+2])
 124         M = int(value[start+13:start+13+2])
 125         S = int(value[start+16:start+16+2])
 126         # TODO Fix time zones
 127         return ((y, m, d), (H, M, S, 0, ))
 128 
 129     def __transposeASCTIME(self, value):
 130         """
 131         wkd mmm dd hh:mm:ss yyyy
 132         """
 133         m = self.__MONTH_NUM.get(value[4:7], 1) # Default to Jan
 134         d = int(value[8:10].strip())
 135         y = int(value[20:])
 136         H = int(value[11:13])
 137         M = int(value[14:16])
 138         S = int(value[17:19])
 139         return ((y, m, d), (H, M, S, 0, None))
 140 
 141     def __transposeRFC3339(self, value):
 142         """
 143         yyyy-mm-ddTHH:MM:SS[.ssssss][Z]|[[+]|[-]HH:MM]
 144         """
 145         value, dot, tmp = value.partition('.')
 146         splitTmp = self.__ZONE_REGEX.findall(dot and tmp or value)
 147         ms, sym, zone = splitTmp and splitTmp[0] or (tmp, '', '')
 148         y = int(value[:4])
 149         m = int(value[5:7])
 150         d = int(value[8:10])
 151         H = int(value[11:13])
 152         M = int(value[14:16])
 153         S = int(value[17:19])
 154         ms = ms.isdigit() and int("{:<06d}".format(int(ms))) or 0
 155         # TODO Fix time zones
 156         #zone = sym.upper() == 'Z' and "+0000" or 
 157         return ((y, m, d), (H, M, S, ms, ))
 158 
 159 
 160     __FROM_DATE_FORMATS = {
 161         _YMD: __transposeFromYMDHMS,
 162         _MDY: __transposeFromMDYHMS,
 163         _DMY: __transposeFromDMYHMS,
 164         _YMD_NON_DLM: __transposeNonDelimiterYMDHMS,
 165         _RFC2616_RFC1123: __transposeRFC1123,
 166         _RFC2616_RFC850: __transposeRFC850,
 167         _RFC2616_ASCTIME: __transposeASCTIME,
 168         _RFC3339_ISO8601: __transposeRFC3339
 169         }
 170 
 171 
 172     @classmethod
 173     def toDatetime(self, value, format=None):
 174         """
 175         Convert a string representation of a date and time to a python
 176         datetime object.
 177 
 178         @param value: The string value to convert.
 179         @keyword format: The format to convert from.
 180         @return: A Python datetime object.
 181         """
 182         tmp = self.__SPLIT_REGEX.findall(value)
 183         head, sep, tail = tmp and tmp[0] or (value, "", "")
 184         head = self.__DATE_REGEX.sub('-', head)
 185         value = head + sep + tail.lstrip()
 186 
 187         if format is None:
 188             format = DateUtils._snoopDateFormat(value)
 189 
 190         try:
 191             args, kwargs = self.__FROM_DATE_FORMATS[format](self, value)
 192             kwargs = dict(zip(self.__KW_NAMES, kwargs))
 193         except KeyError as e:
 194             msg = "Invalid date format should be one of {}."
 195             raise KeyError(msg.format(self.__FROM_DATE_FORMATS.keys()))
 196         except ValueError as e:
 197             msg = "The value {} does not match the format {}, {}"
 198             raise ValueError(msg.format(value, format, e))
 199 
 200         try:
 201             return datetime.datetime(*args, **kwargs)
 202         except ValueError as e:
 203             msg = "Invalid format for datetime {}, found: {}, {}".format(
 204                 fmt, value, e)
 205             raise ValueError(msg)
 206 
 207     @classmethod
 208     def _snoopDateFormat(self, value):
 209         """
 210         We determine what we can then rely on the current systems locale
 211         setting to determine the difference of 01/01/2010 to mean mm/dd/yyy
 212         or dd/mm/yyyy.
 213         """
 214         fmt = None
 215         delim = any([char for char in value if char in ('-', '.', '/', ':')])
 216         hasT = len(value) >= 11 and "T" == value[10]
 217         year = value[:4].isdigit()
 218         day = value[:3].isalpha()
 219         comma = value.find(',')
 220         lfmt = locale.nl_langinfo(locale.D_FMT)
 221 
 222         if hasT:
 223             fmt = self._RFC3339_ISO8601
 224         elif day and comma == 3:
 225             fmt = self._RFC2616_RFC1123
 226         elif day and comma > 3:
 227             fmt = self._RFC2616_RFC850
 228         elif day and comma < 0:
 229             fmt = self._RFC2616_ASCTIME
 230         elif year and not delim:
 231             fmt = self._YMD_NON_DLM
 232         elif year and delim:
 233             fmt = self._YMD
 234         elif lfmt[1] == 'm':
 235             fmt = self._MDY
 236         elif lfmt[1] == 'd':
 237             fmt = self._DMY
 238         else:
 239             msg = "Invalid date time format: {}"
 240             raise ValueError(msg.format(value))
 241 
 242         return fmt
 243 
 244     @classmethod
 245     def getFormatTypes(self):
 246         return (self._YMD, self._MDY, self._DMY, self._YMD_NON_DLM,
 247                 self._RFC2616_RFC1123, self._RFC2616_RFC850,
 248                 self._RFC2616_ASCTIME, self._RFC3339_ISO8601)
 249 
 250 
 251 def testYMDHMS():
 252     print "Testing {}".format(DateUtils._YMD)
 253     value = "2010-12-14 20:56:15"
 254     print "String", value, "Object:", DateUtils.toDatetime(value)
 255     value = "2010.12.14 20:56:15"
 256     print "String", value, "Object:", DateUtils.toDatetime(value)
 257     value = "2010/12/14 20:56:15"
 258     print "String", value, "Object:", DateUtils.toDatetime(value)
 259     value = "2010-12-14 20"
 260     print "String", value, "Object:", DateUtils.toDatetime(value)
 261     value = "2010-12-14 20:56"
 262     print "String", value, "Object:", DateUtils.toDatetime(value)
 263     value = "2010-12-14 20:56:15.9"
 264     print "String", value, "Object:", DateUtils.toDatetime(value)
 265 
 266 def testMDYHMS():
 267     print "Testing {}".format(DateUtils._MDY)
 268     value = "12-14-2010 20:56:15"
 269     print "String", value, "Object:", DateUtils.toDatetime(value)
 270     value = "12.14.2010 20:56:15"
 271     print "String", value, "Object:", DateUtils.toDatetime(value)
 272     value = "12/14/2010 20:56:15"
 273     print "String", value, "Object:", DateUtils.toDatetime(value)
 274     value = "12-14-2010 20"
 275     print "String", value, "Object:", DateUtils.toDatetime(value)
 276     value = "12-14-2010 20:56"
 277     print "String", value, "Object:", DateUtils.toDatetime(value)
 278     value = "12-14-2010 20:56:15.99"
 279     print "String", value, "Object:", DateUtils.toDatetime(value)
 280 
 281 def testDMYHMS():
 282     print "Testing {}".format(DateUtils._DMY)
 283     locale.setlocale(locale.LC_ALL, 'en_GB.UTF-8')
 284     value = "14-12-2010 20:56:15"
 285     print "String", value, "Object:", DateUtils.toDatetime(value)
 286     value = "14.12.2010 20:56:15"
 287     print "String", value, "Object:", DateUtils.toDatetime(value)
 288     value = "14/12/2010 20:56:15"
 289     print "String", value, "Object:", DateUtils.toDatetime(value)
 290     value = "14-12-2010 20"
 291     print "String", value, "Object:", DateUtils.toDatetime(value)
 292     value = "14-12-2010 20:56"
 293     print "String", value, "Object:", DateUtils.toDatetime(value)
 294     value = "14-12-2010 20:56:15.999"
 295     print "String", value, "Object:", DateUtils.toDatetime(value)
 296     locale.resetlocale()
 297 
 298 def testNonDelimiterYMDHMS():
 299     print "Testing {}".format(DateUtils._YMD_NON_DLM)
 300     value = "20101214205615"
 301     print "String", value, "Object:", DateUtils.toDatetime(value)
 302     value = "201012142056"
 303     print "String", value, "Object:", DateUtils.toDatetime(value)
 304     value = "2010121420"
 305     print "String", value, "Object:", DateUtils.toDatetime(value)
 306     value = "201012142056159999"
 307     print "String", value, "Object:", DateUtils.toDatetime(value)
 308 
 309 def testDmmmYHMS():
 310     print "Testing {}".format(DateUtils._DMY)
 311     locale.setlocale(locale.LC_ALL, 'en_GB.UTF-8')
 312     value = "14-DEC-2010 20:56:15"
 313     print "String", value, "Object:", DateUtils.toDatetime(value)
 314     value = "14.DEC.2010 20:56:15"
 315     print "String", value, "Object:", DateUtils.toDatetime(value)
 316     value = "14/DEC/2010 20:56:15"
 317     print "String", value, "Object:", DateUtils.toDatetime(value)
 318     value = "14-DEC-2010 20"
 319     print "String", value, "Object:", DateUtils.toDatetime(value)
 320     value = "14-DEC-2010 20:56"
 321     print "String", value, "Object:", DateUtils.toDatetime(value)
 322     value = "14-DEC-2010 20:56:15.99999"
 323     print "String", value, "Object:", DateUtils.toDatetime(value)
 324     locale.resetlocale()
 325 
 326 def testRFC1123():
 327     print "Testing {}".format(DateUtils._RFC2616_RFC1123)
 328     value = "Tue, 14 Dec 2010 20:56:15 GMT"
 329     print "String", value, "Object:", DateUtils.toDatetime(value)
 330 
 331 def testRFC850():
 332     print "Testing {}".format(DateUtils._RFC2616_RFC850)
 333     value = "Tuesday, 14-Dec-10 20:56:15 GMT"
 334     print "String", value, "Object:", DateUtils.toDatetime(value)
 335 
 336 def testASCTIME():
 337     print "Testing {}".format(DateUtils._RFC2616_ASCTIME)
 338     value = "Tue Dec 14 20:56:15 2010"
 339     print "String", value, "Object:", DateUtils.toDatetime(value)
 340     value = "Tue Dec  4 20:56:15 2010"
 341     print "String", value, "Object:", DateUtils.toDatetime(value)
 342 
 343 def testRFC3339():
 344     print "Testing {}".format(DateUtils._RFC3339_ISO8601)
 345     value = "2013-07-06T11:33:40Z"
 346     print "String", value, "Object:", DateUtils.toDatetime(value)
 347     value = "2013-07-06T11:33:40+00:00"
 348     print "String", value, "Object:", DateUtils.toDatetime(value)
 349 
 350 
 351 if __name__=='__main__':
 352     import sys, traceback
 353 
 354     try:
 355         testYMDHMS()
 356     except:
 357         tb = sys.exc_info()[2]
 358         traceback.print_tb(tb)
 359         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])
 360 
 361     try:
 362         testMDYHMS()
 363     except:
 364         tb = sys.exc_info()[2]
 365         traceback.print_tb(tb)
 366         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])
 367 
 368     try:
 369         testDMYHMS()
 370     except:
 371         tb = sys.exc_info()[2]
 372         traceback.print_tb(tb)
 373         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])
 374 
 375     try:
 376         testNonDelimiterYMDHMS()
 377     except:
 378         tb = sys.exc_info()[2]
 379         traceback.print_tb(tb)
 380         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])
 381 
 382     try:
 383         testDmmmYHMS()
 384     except:
 385         tb = sys.exc_info()[2]
 386         traceback.print_tb(tb)
 387         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])
 388 
 389     try:
 390         testRFC1123()
 391     except:
 392         tb = sys.exc_info()[2]
 393         traceback.print_tb(tb)
 394         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])
 395 
 396     try:
 397         testRFC850()
 398     except:
 399         tb = sys.exc_info()[2]
 400         traceback.print_tb(tb)
 401         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])
 402 
 403     try:
 404         testASCTIME()
 405     except:
 406         tb = sys.exc_info()[2]
 407         traceback.print_tb(tb)
 408         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])
 409 
 410     try:
 411         testRFC3339()
 412     except:
 413         tb = sys.exc_info()[2]
 414         traceback.print_tb(tb)
 415         print "%s: %s\n" % (sys.exc_info()[0], sys.exc_info()[1])

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.

You are not allowed to attach a file to this page.