comparison mercurial/util.py @ 3815:bf6ab30559e6

Add date matching support Add extended date formats (eg "Dec", "2006") Add a couple missing basic date formats Improve default date element scheme to parsedate Add matchdate function to match a date spec Add -e switch and range matching to debugdate
author Matt Mackall <mpm@selenic.com>
date Wed, 06 Dec 2006 15:11:44 -0600
parents 4d93b37b5963
children fc5ba0ab7f45
comparison
equal deleted inserted replaced
3813:6fa11a9d7cac 3815:bf6ab30559e6
83 '%m/%d/%y', 83 '%m/%d/%y',
84 '%m/%d/%Y', 84 '%m/%d/%Y',
85 '%a %b %d %H:%M:%S %Y', 85 '%a %b %d %H:%M:%S %Y',
86 '%a %b %d %I:%M:%S%p %Y', 86 '%a %b %d %I:%M:%S%p %Y',
87 '%b %d %H:%M:%S %Y', 87 '%b %d %H:%M:%S %Y',
88 '%b %d %I:%M:%S%p %Y',
89 '%b %d %H:%M:%S',
88 '%b %d %I:%M:%S%p', 90 '%b %d %I:%M:%S%p',
89 '%b %d %H:%M', 91 '%b %d %H:%M',
90 '%b %d %I:%M%p', 92 '%b %d %I:%M%p',
91 '%b %d %Y', 93 '%b %d %Y',
92 '%b %d', 94 '%b %d',
93 '%H:%M:%S', 95 '%H:%M:%S',
94 '%I:%M:%SP', 96 '%I:%M:%SP',
95 '%H:%M', 97 '%H:%M',
96 '%I:%M%p', 98 '%I:%M%p',
97 ) 99 )
100
101 extendeddateformats = defaultdateformats + (
102 "%Y",
103 "%Y-%m",
104 "%b",
105 "%b %Y",
106 )
98 107
99 class SignalInterrupt(Exception): 108 class SignalInterrupt(Exception):
100 """Exception raised on SIGTERM and SIGHUP.""" 109 """Exception raised on SIGTERM and SIGHUP."""
101 110
102 # like SafeConfigParser but with case-sensitive keys 111 # like SafeConfigParser but with case-sensitive keys
1056 s = time.strftime(format, time.gmtime(float(t) - tz)) 1065 s = time.strftime(format, time.gmtime(float(t) - tz))
1057 if timezone: 1066 if timezone:
1058 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60)) 1067 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1059 return s 1068 return s
1060 1069
1061 def strdate(string, format='%a %b %d %H:%M:%S %Y'): 1070 def strdate(string, format, defaults):
1062 """parse a localized time string and return a (unixtime, offset) tuple. 1071 """parse a localized time string and return a (unixtime, offset) tuple.
1063 if the string cannot be parsed, ValueError is raised.""" 1072 if the string cannot be parsed, ValueError is raised."""
1064 def timezone(string): 1073 def timezone(string):
1065 tz = string.split()[-1] 1074 tz = string.split()[-1]
1066 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit(): 1075 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1074 # NOTE: unixtime = localunixtime + offset 1083 # NOTE: unixtime = localunixtime + offset
1075 offset, date = timezone(string), string 1084 offset, date = timezone(string), string
1076 if offset != None: 1085 if offset != None:
1077 date = " ".join(string.split()[:-1]) 1086 date = " ".join(string.split()[:-1])
1078 1087
1079 # add missing elements 1088 # add missing elements from defaults
1080 if '%y' not in format.lower(): 1089 for part in defaults:
1081 date += "@" + datestr(makedate(), "%Y", False) 1090 found = [True for p in part if ("%"+p) in format]
1082 format += "@%Y" 1091 if not found:
1083 if '%m' not in format and '%b' not in format: 1092 date += "@" + defaults[part]
1084 date += "@" + datestr(makedate(), "%m", False) 1093 format += "@%" + part[0]
1085 format += "@%m"
1086 if '%d' not in format:
1087 date += "@" + datestr(makedate(), "%d", False)
1088 format += "@%d"
1089 1094
1090 timetuple = time.strptime(date, format) 1095 timetuple = time.strptime(date, format)
1091 localunixtime = int(calendar.timegm(timetuple)) 1096 localunixtime = int(calendar.timegm(timetuple))
1092 if offset is None: 1097 if offset is None:
1093 # local timezone 1098 # local timezone
1095 offset = unixtime - localunixtime 1100 offset = unixtime - localunixtime
1096 else: 1101 else:
1097 unixtime = localunixtime + offset 1102 unixtime = localunixtime + offset
1098 return unixtime, offset 1103 return unixtime, offset
1099 1104
1100 def parsedate(string, formats=None): 1105 def parsedate(string, formats=None, defaults=None):
1101 """parse a localized time string and return a (unixtime, offset) tuple. 1106 """parse a localized time string and return a (unixtime, offset) tuple.
1102 The date may be a "unixtime offset" string or in one of the specified 1107 The date may be a "unixtime offset" string or in one of the specified
1103 formats.""" 1108 formats."""
1104 if not string: 1109 if not string:
1105 return 0, 0 1110 return 0, 0
1107 formats = defaultdateformats 1112 formats = defaultdateformats
1108 string = string.strip() 1113 string = string.strip()
1109 try: 1114 try:
1110 when, offset = map(int, string.split(' ')) 1115 when, offset = map(int, string.split(' '))
1111 except ValueError: 1116 except ValueError:
1117 # fill out defaults
1118 if not defaults:
1119 defaults = {}
1120 now = makedate()
1121 for part in "d mb yY HI M S".split():
1122 if part not in defaults:
1123 if part[0] in "HMS":
1124 defaults[part] = "00"
1125 elif part[0] in "dm":
1126 defaults[part] = "1"
1127 else:
1128 defaults[part] = datestr(now, "%" + part[0], False)
1129
1112 for format in formats: 1130 for format in formats:
1113 try: 1131 try:
1114 when, offset = strdate(string, format) 1132 when, offset = strdate(string, format, defaults)
1115 except ValueError: 1133 except ValueError:
1116 pass 1134 pass
1117 else: 1135 else:
1118 break 1136 break
1119 else: 1137 else:
1125 if abs(when) > 0x7fffffff: 1143 if abs(when) > 0x7fffffff:
1126 raise Abort(_('date exceeds 32 bits: %d') % when) 1144 raise Abort(_('date exceeds 32 bits: %d') % when)
1127 if offset < -50400 or offset > 43200: 1145 if offset < -50400 or offset > 43200:
1128 raise Abort(_('impossible time zone offset: %d') % offset) 1146 raise Abort(_('impossible time zone offset: %d') % offset)
1129 return when, offset 1147 return when, offset
1148
1149 def matchdate(date):
1150 """Return a function that matches a given date match specifier
1151
1152 Formats include:
1153
1154 '{date}' match a given date to the accuracy provided
1155
1156 '<{date}' on or before a given date
1157
1158 '>{date}' on or after a given date
1159
1160 """
1161
1162 def lower(date):
1163 return parsedate(date, extendeddateformats)[0]
1164
1165 def upper(date):
1166 d = dict(mb="12", HI="23", M="59", S="59")
1167 for days in "31 30 29".split():
1168 try:
1169 d["d"] = days
1170 return parsedate(date, extendeddateformats, d)[0]
1171 except:
1172 pass
1173 d["d"] = "28"
1174 return parsedate(date, extendeddateformats, d)[0]
1175
1176 if date[0] == "<":
1177 when = upper(date[1:])
1178 return lambda x: x <= when
1179 elif date[0] == ">":
1180 when = lower(date[1:])
1181 return lambda x: x >= when
1182 elif date[0] == "-":
1183 try:
1184 days = int(date[1:])
1185 except ValueError:
1186 raise Abort(_("invalid day spec: %s") % date[1:])
1187 when = makedate()[0] - days * 3600 * 24
1188 return lambda x: x <= when
1189 elif " to " in date:
1190 a, b = date.split(" to ")
1191 start, stop = lower(a), upper(b)
1192 return lambda x: x >= start and x <= stop
1193 else:
1194 start, stop = lower(date), upper(date)
1195 return lambda x: x >= start and x <= stop
1130 1196
1131 def shortuser(user): 1197 def shortuser(user):
1132 """Return a short representation of a user name or email address.""" 1198 """Return a short representation of a user name or email address."""
1133 f = user.find('@') 1199 f = user.find('@')
1134 if f >= 0: 1200 if f >= 0: