1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Provide time related exceptions and functions"""
23
24 import time, types, re, calendar
25 from duplicity import globals
26
27
30
31 _interval_conv_dict = {"s": 1, "m": 60, "h": 3600, "D": 86400,
32 "W": 7*86400, "M": 30*86400, "Y": 365*86400}
33 _integer_regexp = re.compile("^[0-9]+$")
34 _interval_regexp = re.compile("^([0-9]+)([smhDWMY])")
35 _genstr_date_regexp1 = re.compile("^(?P<year>[0-9]{4})[-/]"
36 "(?P<month>[0-9]{1,2})[-/]"
37 "(?P<day>[0-9]{1,2})$")
38 _genstr_date_regexp2 = re.compile("^(?P<month>[0-9]{1,2})[-/]"
39 "(?P<day>[0-9]{1,2})[-/]"
40 "(?P<year>[0-9]{4})$")
41 _genstr_date_regexp3 = re.compile("^(?P<year>[0-9]{4})"
42 "(?P<month>[0-9]{2})"
43 "(?P<day>[0-9]{2})Z$")
44 curtime = curtimestr = None
45 prevtime = prevtimestr = None
46
47 bad_interval_string = _("""Bad interval string "%s"
48
49 Intervals are specified like 2Y (2 years) or 2h30m (2.5 hours). The
50 allowed special characters are s, m, h, D, W, M, and Y. See the man
51 page for more information.""")
52
53 bad_time_string = _("""Bad time string "%s"
54
55 The acceptible time strings are intervals (like "3D64s"), w3-datetime
56 strings, like "2002-04-26T04:22:01-07:00" (strings like
57 "2002-04-26T04:22:01" are also acceptable - duplicity will use the
58 current time zone), or ordinary dates like 2/4/1997 or 2001-04-23
59 (various combinations are acceptable, but the month always precedes
60 the day).""")
61
68
74
76 """Return w3 or duplicity datetime compliant listing of timeinseconds"""
77
78 if globals.old_filenames:
79
80
81
82 lcltime = time.localtime(timeinseconds)
83 return time.strftime("%Y-%m-%dT%H" + globals.time_separator +
84 "%M" + globals.time_separator + "%S",
85 lcltime) + gettzd(lcltime[-1])
86 else:
87
88 lcltime = time.gmtime(timeinseconds)
89 return time.strftime("%Y%m%dT%H%M%SZ", lcltime)
90
92 """Return time in seconds from w3 or duplicity timestring
93
94 If there is an error parsing the string, or it doesn't look
95 like a valid datetime string, return None.
96 """
97 try:
98 date, daytime = timestring[:19].split("T")
99 if len(timestring) == 16:
100
101 year, month, day = map(int,
102 [date[0:4], date[4:6], date[6:8]])
103 hour, minute, second = map(int,
104 [daytime[0:2], daytime[2:4], daytime[4:6]])
105 else:
106
107 year, month, day = map(int, date.split("-"))
108 hour, minute, second = map(int,
109 daytime.split(globals.time_separator))
110 assert 1900 < year < 2100, year
111 assert 1 <= month <= 12
112 assert 1 <= day <= 31
113 assert 0 <= hour <= 23
114 assert 0 <= minute <= 59
115 assert 0 <= second <= 61
116
117
118
119
120 timetuple = (year, month, day, hour, minute, second, -1, -1, 0)
121
122 if len(timestring) == 16:
123
124
125
126
127 utc_in_secs = calendar.timegm(timetuple)
128 else:
129
130
131
132
133 local_in_secs = time.mktime(timetuple)
134 utc_in_secs = local_in_secs - time.timezone
135
136
137
138
139 if len(timestring) == 16:
140 return long(utc_in_secs)
141 else:
142 return long(utc_in_secs + tzdtoseconds(timestring[19:]))
143 except (TypeError, ValueError, AssertionError):
144 return None
145
147 """Return pretty version of time"""
148 return time.asctime(time.localtime(timeinseconds))
149
153
155 """Convert num of seconds to readable string like "2 hours"."""
156 partlist = []
157 hours, seconds = divmod(seconds, 3600)
158 if hours > 1:
159 partlist.append("%d hours" % hours)
160 elif hours == 1:
161 partlist.append("1 hour")
162
163 minutes, seconds = divmod(seconds, 60)
164 if minutes > 1:
165 partlist.append("%d minutes" % minutes)
166 elif minutes == 1:
167 partlist.append("1 minute")
168
169 if seconds == 1:
170 partlist.append("1 second")
171 elif not partlist or seconds > 1:
172 if isinstance(seconds, int) or isinstance(seconds, long):
173 partlist.append("%s seconds" % seconds)
174 else:
175 partlist.append("%.2f seconds" % seconds)
176 return " ".join(partlist)
177
182
183 if len(interval_string) < 2:
184 error()
185
186 total = 0
187 while interval_string:
188 match = _interval_regexp.match(interval_string)
189 if not match:
190 error()
191 num, ext = int(match.group(1)), match.group(2)
192 if not ext in _interval_conv_dict or num < 0:
193 error()
194 total += num*_interval_conv_dict[ext]
195 interval_string = interval_string[match.end(0):]
196 return total
197
199 """Return w3's timezone identification string.
200
201 Expresed as [+/-]hh:mm. For instance, PST is -08:00. Zone is
202 coincides with what localtime(), etc., use.
203
204 """
205
206
207
208
209
210
211 if dstflag > 0:
212 offset = -1 * time.altzone/60
213 else:
214 offset = -1 * time.timezone/60
215 if offset > 0:
216 prefix = "+"
217 elif offset < 0:
218 prefix = "-"
219 else:
220 return "Z"
221
222 hours, minutes = map(abs, divmod(offset, 60))
223 assert 0 <= hours <= 23
224 assert 0 <= minutes <= 59
225 return "%s%02d%s%02d" % (prefix, hours, globals.time_separator, minutes)
226
228 """Given w3 compliant TZD, return how far ahead UTC is"""
229 if tzd == "Z":
230 return 0
231 assert len(tzd) == 6
232 assert (tzd[0] == "-" or tzd[0] == "+") and \
233 tzd[3] == globals.time_separator
234 return -60 * (60 * int(tzd[:3]) + int(tzd[4:]))
235
236 -def cmp(time1, time2):
237 """Compare time1 and time2 and return -1, 0, or 1"""
238 if type(time1) is types.StringType:
239 time1 = stringtotime(time1)
240 assert time1 is not None
241 if type(time2) is types.StringType:
242 time2 = stringtotime(time2)
243 assert time2 is not None
244
245 if time1 < time2:
246 return -1
247 elif time1 == time2:
248 return 0
249 else:
250 return 1
251
253 """Convert a generic time string to a time in seconds"""
254 if override_curtime is None:
255 override_curtime = curtime
256 if timestr == "now":
257 return override_curtime
258
259 def error():
260 raise TimeException(bad_time_string % timestr)
261
262
263 if _integer_regexp.search(timestr):
264 return int(timestr)
265
266
267
268
269
270
271
272
273 t = stringtotime(timestr) or stringtotime(timestr+gettzd(0))
274 if t:
275 return t
276
277 try:
278 return override_curtime - intstringtoseconds(timestr)
279 except TimeException:
280 pass
281
282
283 match = _genstr_date_regexp1.search(timestr) or \
284 _genstr_date_regexp2.search(timestr) or \
285 _genstr_date_regexp3.search(timestr)
286 if not match:
287 error()
288 timestr = "%s-%02d-%02dT00:00:00%s" % (match.group('year'),
289 int(match.group('month')),
290 int(match.group('day')),
291 gettzd(0))
292 t = stringtotime(timestr)
293 if t:
294 return t
295 else:
296 error()
297