1b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# (c) 2005 Clark C. Evans and contributors 2b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# This module is part of the Python Paste Project and is released under 3b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# the MIT License: http://www.opensource.org/licenses/mit-license.php 4b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Some of this code was funded by: http://prometheusresearch.com 5b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 6b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikDate, Time, and Timespan Parsing Utilities 7b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 8b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikThis module contains parsing support to create "human friendly" 9b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik``datetime`` object parsing. The explicit goal of these routines is 10b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikto provide a multi-format date/time support not unlike that found in 11b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikMicrosoft Excel. In most approaches, the input is very "strict" to 12b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikprevent errors -- however, this approach is much more liberal since we 13b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikare assuming the user-interface is parroting back the normalized value 14b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikand thus the user has immediate feedback if the data is not typed in 15b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikcorrectly. 16b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 17b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``parse_date`` and ``normalize_date`` 18b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 19b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik These functions take a value like '9 jan 2007' and returns either an 20b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``date`` object, or an ISO 8601 formatted date value such 21b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik as '2007-01-09'. There is an option to provide an Oracle database 22b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik style output as well, ``09 JAN 2007``, but this is not the default. 23b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 24b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This module always treats '/' delimiters as using US date order 25b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (since the author's clients are US based), hence '1/9/2007' is 26b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik January 9th. Since this module treats the '-' as following 27b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik European order this supports both modes of data-entry; together 28b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik with immediate parroting back the result to the screen, the author 29b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik has found this approach to work well in pratice. 30b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 31b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``parse_time`` and ``normalize_time`` 32b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 33b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik These functions take a value like '1 pm' and returns either an 34b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``time`` object, or an ISO 8601 formatted 24h clock time 35b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik such as '13:00'. There is an option to provide for US style time 36b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik values, '1:00 PM', however this is not the default. 37b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 38b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``parse_datetime`` and ``normalize_datetime`` 39b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 40b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik These functions take a value like '9 jan 2007 at 1 pm' and returns 41b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik either an ``datetime`` object, or an ISO 8601 formatted 42b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return (without the T) such as '2007-01-09 13:00'. There is an 43b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik option to provide for Oracle / US style, '09 JAN 2007 @ 1:00 PM', 44b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik however this is not the default. 45b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 46b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``parse_delta`` and ``normalize_delta`` 47b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 48b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik These functions take a value like '1h 15m' and returns either an 49b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``timedelta`` object, or an 2-decimal fixed-point 50b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik numerical value in hours, such as '1.25'. The rationale is to 51b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik support meeting or time-billing lengths, not to be an accurate 52b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik representation in mili-seconds. As such not all valid 53b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ``timedelta`` values will have a normalized representation. 54b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 55b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 56b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom datetime import timedelta, time, date 57b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom time import localtime 58b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 59b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik__all__ = ['parse_timedelta', 'normalize_timedelta', 60b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'parse_time', 'normalize_time', 61b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'parse_date', 'normalize_date'] 62b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 63b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _number(val): 64b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 65b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return int(val) 66b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except: 67b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return None 68b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 69b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# 70b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# timedelta 71b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# 72b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef parse_timedelta(val): 73b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 74b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik returns a ``timedelta`` object, or None 75b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 76b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not val: 77b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return None 78b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.lower() 79b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if "." in val: 80b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = float(val) 81b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return timedelta(hours=int(val), minutes=60*(val % 1.0)) 82b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fHour = ("h" in val or ":" in val) 83b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fMin = ("m" in val or ":" in val) 84b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for noise in "minu:teshour()": 85b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.replace(noise, ' ') 86b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.strip() 87b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.split() 88b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = 0.0 89b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mi = 0 90b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val.reverse() 91b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if fHour: 92b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = int(val.pop()) 93b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if fMin: 94b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mi = int(val.pop()) 95b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(val) > 0 and not hr: 96b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = int(val.pop()) 97b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return timedelta(hours=hr, minutes=mi) 98b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 99b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef normalize_timedelta(val): 100b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 101b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik produces a normalized string value of the timedelta 102b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 103b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This module returns a normalized time span value consisting of the 104b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik number of hours in fractional form. For example '1h 15min' is 105b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik formatted as 01.25. 106b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 107b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if type(val) == str: 108b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = parse_timedelta(val) 109b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not val: 110b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '' 111b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = val.seconds/3600 112b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mn = (val.seconds % 3600)/60 113b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return "%d.%02d" % (hr, mn * 100/60) 114b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 115b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# 116b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# time 117b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# 118b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef parse_time(val): 119b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not val: 120b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return None 121b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = mi = 0 122b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.lower() 123b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik amflag = (-1 != val.find('a')) # set if AM is found 124b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pmflag = (-1 != val.find('p')) # set if PM is found 125b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for noise in ":amp.": 126b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.replace(noise, ' ') 127b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.split() 128b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(val) > 1: 129b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = int(val[0]) 130b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mi = int(val[1]) 131b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 132b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val[0] 133b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(val) < 1: 134b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pass 135b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif 'now' == val: 136b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tm = localtime() 137b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = tm[3] 138b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mi = tm[4] 139b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif 'noon' == val: 140b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = 12 141b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif len(val) < 3: 142b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = int(val) 143b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not amflag and not pmflag and hr < 7: 144b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr += 12 145b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif len(val) < 5: 146b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = int(val[:-2]) 147b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mi = int(val[-2:]) 148b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 149b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = int(val[:1]) 150b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if amflag and hr >= 12: 151b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = hr - 12 152b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if pmflag and hr < 12: 153b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = hr + 12 154b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return time(hr, mi) 155b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 156b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef normalize_time(value, ampm): 157b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not value: 158b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '' 159b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if type(value) == str: 160b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = parse_time(value) 161b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not ampm: 162b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return "%02d:%02d" % (value.hour, value.minute) 163b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = value.hour 164b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik am = "AM" 165b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if hr < 1 or hr > 23: 166b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = 12 167b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif hr >= 12: 168b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik am = "PM" 169b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if hr > 12: 170b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik hr = hr - 12 171b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return "%02d:%02d %s" % (hr, value.minute, am) 172b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 173b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# 174b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Date Processing 175b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# 176b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 177b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_one_day = timedelta(days=1) 178b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 179b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_str2num = {'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6, 180b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12 } 181b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 182b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _month(val): 183b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for (key, mon) in _str2num.items(): 184b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if key in val: 185b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return mon 186b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise TypeError("unknown month '%s'" % val) 187b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 188b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_days_in_month = {1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30, 189b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31, 190b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik } 191b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_num2str = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun', 192b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec', 193b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik } 194b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik_wkdy = ("mon", "tue", "wed", "thu", "fri", "sat", "sun") 195b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 196b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef parse_date(val): 197b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not(val): 198b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return None 199b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.lower() 200b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik now = None 201b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 202b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # optimized check for YYYY-MM-DD 203b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik strict = val.split("-") 204b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(strict) == 3: 205b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik (y, m, d) = strict 206b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if "+" in d: 207b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik d = d.split("+")[0] 208b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if " " in d: 209b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik d = d.split(" ")[0] 210b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 211b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik now = date(int(y), int(m), int(d)) 212b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = "xxx" + val[10:] 213b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except ValueError: 214b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pass 215b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 216b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # allow for 'now', 'mon', 'tue', etc. 217b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not now: 218b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik chk = val[:3] 219b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if chk in ('now','tod'): 220b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik now = date.today() 221b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif chk in _wkdy: 222b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik now = date.today() 223b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik idx = list(_wkdy).index(chk) + 1 224b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik while now.isoweekday() != idx: 225b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik now += _one_day 226b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 227b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # allow dates to be modified via + or - /w number of days, so 228b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # that now+3 is three days from now 229b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if now: 230b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tail = val[3:].strip() 231b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tail = tail.replace("+"," +").replace("-"," -") 232b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for item in tail.split(): 233b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 234b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik days = int(item) 235b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except ValueError: 236b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pass 237b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 238b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik now += timedelta(days=days) 239b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return now 240b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 241b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # ok, standard parsing 242b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = mo = dy = None 243b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for noise in ('/', '-', ',', '*'): 244b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.replace(noise, ' ') 245b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for noise in _wkdy: 246b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val.replace(noise, ' ') 247b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out = [] 248b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik last = False 249b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ldig = False 250b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for ch in val: 251b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if ch.isdigit(): 252b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if last and not ldig: 253b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.append(' ') 254b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik last = ldig = True 255b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 256b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if ldig: 257b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.append(' ') 258b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ldig = False 259b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik last = True 260b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out.append(ch) 261b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = "".join(out).split() 262b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if 3 == len(val): 263b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik a = _number(val[0]) 264b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik b = _number(val[1]) 265b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik c = _number(val[2]) 266b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(val[0]) == 4: 267b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = a 268b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if b: # 1999 6 23 269b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = b 270b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = c 271b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # 1999 Jun 23 272b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _month(val[1]) 273b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = c 274b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif a is not None and a > 0: 275b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = c 276b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(val[2]) < 4: 277b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise TypeError("four digit year required") 278b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if b: # 6 23 1999 279b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = b 280b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = a 281b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # 23 Jun 1999 282b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = a 283b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _month(val[1]) 284b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # Jun 23, 2000 285b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = b 286b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = c 287b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(val[2]) < 4: 288b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise TypeError("four digit year required") 289b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _month(val[0]) 290b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif 2 == len(val): 291b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik a = _number(val[0]) 292b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik b = _number(val[1]) 293b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if a is not None and a > 999: 294b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = a 295b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = 1 296b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if b is not None and b > 0: # 1999 6 297b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = b 298b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # 1999 Jun 299b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _month(val[1]) 300b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif a is not None and a > 0: 301b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if b is not None and b > 999: # 6 1999 302b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = a 303b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = b 304b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = 1 305b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif b is not None and b > 0: # 6 23 306b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = a 307b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = b 308b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # 23 Jun 309b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = a 310b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _month(val[1]) 311b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 312b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if b > 999: # Jun 2001 313b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = b 314b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = 1 315b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # Jun 23 316b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = b 317b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _month(val[0]) 318b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif 1 == len(val): 319b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = val[0] 320b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not val.isdigit(): 321b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _month(val) 322b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if mo is not None: 323b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = 1 324b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 325b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik v = _number(val) 326b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = str(v) 327b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if 8 == len(val): # 20010623 328b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = _number(val[:4]) 329b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _number(val[4:6]) 330b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = _number(val[6:]) 331b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif len(val) in (3,4): 332b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if v is not None and v > 1300: # 2004 333b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = v 334b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = 1 335b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = 1 336b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: # 1202 337b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = _number(val[:-2]) 338b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = _number(val[-2:]) 339b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik elif v < 32: 340b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = v 341b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 342b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise TypeError("four digit year required") 343b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tm = localtime() 344b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if mo is None: 345b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mo = tm[1] 346b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if dy is None: 347b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik dy = tm[2] 348b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if yr is None: 349b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik yr = tm[0] 350b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return date(yr, mo, dy) 351b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 352b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef normalize_date(val, iso8601=True): 353b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not val: 354b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '' 355b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if type(val) == str: 356b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik val = parse_date(val) 357b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if iso8601: 358b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return "%4d-%02d-%02d" % (val.year, val.month, val.day) 359b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return "%02d %s %4d" % (val.day, _num2str[val.month], val.year) 360