Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/util.py @ 3812: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
3811:6fa11a9d7cac | 3812: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: |