planner-el.texi (Keeping Track of Time): Recover paragraph that had
[planner-el.git] / planner2diary.py
blobb01820730c398fbb53de207d5c25581ae49c88e5
1 #!/usr/bin/python
3 # Copyright (C) 2001, 2008 Free Software Foundation, Inc.
5 # Author: John Wiegley
7 import os, os.path, sys, re, time, string
9 def IsLeapYear(year):
10 if year % 4 == 0:
11 if year % 100 == 0:
12 if year % 400 == 0:
13 return 1
14 else:
15 return 0
16 else:
17 return 1
18 else:
19 return 0
21 def NumberDaysYear(year):
22 return 365 + IsLeapYear(year)
24 def NumberDaysMonth(month = None, year = None):
25 if month is None:
26 m = time.localtime()[1]
27 else:
28 m = month
30 if year is None:
31 y = time.localtime()[0]
32 else:
33 y = year
35 if m == 2:
36 if IsLeapYear(y):
37 return 29
38 else:
39 return 28
40 elif m in (1, 3, 5, 7, 8, 10, 12):
41 return 31
42 else:
43 return 30
46 class Date(object):
47 """The Date class."""
49 Weekdays = ["Monday",
50 "Tuesday",
51 "Wednesday",
52 "Thursday",
53 "Friday",
54 "Saturday",
55 "Sunday"]
57 Months = ["January",
58 "February",
59 "March",
60 "April",
61 "May",
62 "June",
63 "July",
64 "August",
65 "September",
66 "October",
67 "November",
68 "December"]
70 #The slots in a Date object are constrained to allow more efficient operations.
71 __slots__ = ["year", "month", "day"]
73 def __init__(self, tm = None):
74 """The initializer has an optional argument, time, in the time module format,
75 wether as in seconds since the epoch (Unix time) wether as a tuple (time tuple).
76 If it is not provided, then it returns the current date."""
77 if tm is None:
78 t = time.localtime()
79 else:
80 if isinstance(tm, int):
81 t = time.localtime(tm)
82 else:
83 t = tm
85 self.year, self.month, self.day = t[:3]
87 def weekday(self):
88 """Returns the weekday of the date.
90 The format is as in the time module: Monday is 0 and sunday is 6."""
91 a = (14 - self.month)//12
92 y = self.year - a
93 m = self.month + 12*a -2
94 d = (self.day + y + y//4 - y//100 + y//400 + (31*m//12))%7
95 if d:
96 ret = d - 1
97 else:
98 ret = 6
99 return ret
101 def __str__(self):
102 return "%s, %d-%s-%d" % (Date.Weekdays[self.weekday()],
103 self.day,
104 Date.Months[self.month - 1],
105 self.year)
107 def copy(self):
108 """Deep copy of Date objects."""
109 ret = Date()
110 ret.year, ret.month, ret.day = self.year, self.month, self.day
111 return ret
113 #The iterator protocol. The iteration is "destructive", like in files.
114 def __iter__(self):
115 return self
117 def next(self):
118 #Last day of the month.
119 if self.day == NumberDaysMonth(self.month, self.year):
120 self.day = 1
121 #December case.
122 if self.month == 12:
123 self.month = 1
124 self.year += 1
125 else:
126 self.month += 1
127 else:
128 self.day += 1
130 #Extended iterator protocol. One can go backwards.
131 def previous(self):
132 #First day of the month.
133 if self.day == 1:
134 #January case.
135 if self.month == 1:
136 self.month = 12
137 self.year -= 1
138 else:
139 self.month -= 1
140 self.day = NumberDaysMonth(self.month, self.year)
141 else:
142 self.day -= 1
144 #Comparison methods.
145 def __eq__(self, date):
146 return self.year == date.year and self.month == date.month and\
147 self.day == date.day
149 def __lt__(self, other):
150 return (self.year, self.month, self.day) < (other.year, other.month, other.day)
152 def __le__(self, other):
153 return (self.year, self.month, self.day) <= (other.year, other.month, other.day)
155 #Dates can be used as keys in dictionaries.
156 def __hash__(self):
157 return hash((self.year, self.month, self.day))
159 #Some useful methods.
160 def GetYearDay(self):
161 """Returns the year day of a date."""
162 ret = self.day
163 for month in range(1, self.month):
164 ret += NumberDaysMonth(month, self.year)
165 return ret
167 def DaysToEndYear(self):
168 """Returns the number of days until the end of the year."""
169 ret = NumberDaysMonth(self.month, self.year) - self.day
170 for i in range(self.month + 1, 13):
171 ret += NumberDaysMonth(i, self.year)
172 return ret
174 def GetWeekday(self):
175 """Returns the weekday of the date in string format."""
176 return Date.Weekdays[self.weekday()]
178 def GetMonth(self):
179 """Returns the month of the date in string format."""
180 return Date.Months[self.month - 1]
182 def ToJDNumber(self):
183 """Returns the Julian day number of a date."""
184 a = (14 - self.month)//12
185 y = self.year + 4800 - a
186 m = self.month + 12*a - 3
187 return self.day + ((153*m + 2)//5) + 365*y + y//4 - y//100 + y//400 - 32045
189 #Binary operations.
190 def __add__(self, n):
191 """Adds a (signed) number of days to the date."""
192 if isinstance(n, int):
193 #Calculate julian day number and add n.
194 temp = self.ToJDNumber() + n
195 #Convert back to date format.
196 return DateFromJDNumber(temp)
197 else:
198 raise TypeError, "%s is not an integer." % str(n)
200 def __sub__(self, date):
201 """Returns the (signed) difference of days between the dates."""
202 #If it is an integer defer calculation to the __add__ method.
203 if isinstance(date, int):
204 return self.__add__(-date)
205 elif isinstance(date, Date):
206 #Case: The years are equal.
207 if self.year == date.year:
208 return self.GetYearDay() - date.GetYearDay()
209 else:
210 if self < date:
211 ret = self.DaysToEndYear() + date.GetYearDay()
212 for year in range(self.year + 1, date.year):
213 ret += NumberDaysYear(year)
214 return -ret
215 else:
216 ret = date.DaysToEndYear() + self.GetYearDay()
217 for year in range(date.year + 1, self.year):
218 ret += NumberDaysYear(year)
219 return ret
220 else:
221 raise TypeError, "%s is neither an integer nor a Date." % str(date)
223 #Adding an integer is "commutative".
224 def __radd__(self, n):
225 return self.__add__(n)
227 #Conversion methods.
228 def ToTimeTuple(self):
229 """Convert a date into a time tuple (time module) corresponding to the
230 same day with the midnight hour."""
231 ret = [self.year, self.month, self.day]
232 ret.extend([0, 0, 0])
233 ret.append(self.weekday())
234 ret.extend([self.GetYearDay(), 0])
235 return tuple(ret)
238 def needparsep(fname):
239 import re
240 import time
241 curdate = Date()
242 if re.search("^([0-9]{4})\.([0-9]+)\.([0-9]+)$", fname):
243 if curdate <= Date(time.strptime(fname, "%Y.%m.%d")):
244 return True
245 else:
246 return False
247 else:
248 return False
250 def needtransferp(line):
251 import re
252 if re.search("^\s*([0-9]+:[0-9]+)\s*(.+)", line):
253 return True
254 else:
255 return False
257 if __name__ == "__main__":
259 try:
260 dirname = sys.argv[1]
261 except IndexError:
262 dirname = "~/emacs/plans"
264 filelist = filter(needparsep, os.listdir(os.path.expanduser(dirname)))
266 selected = []
267 for filename in filelist:
268 thatdate = Date(time.strptime(filename, "%Y.%m.%d"))
269 thatdate = repr(thatdate.month)+"/"+repr(thatdate.day)+"/"+repr(thatdate.year)
271 selected = filter(needtransferp, open(os.path.join(os.path.expanduser(dirname), filename), "r"))
272 for line in selected:
273 m = re.search("^\s*([0-9]+:[0-9]+)\s*\|\s*([0-9]+:[0-9]+)\s*\|\s*(.+)", line)
274 if m:
275 outputstr = thatdate+" "+m.group(1)+"-"+m.group(2)+" "+m.group(3)
276 else:
277 m = re.search("^\s*([0-9]+:[0-9]+)\s*\|\s*(.+?)(\s+\(([0-9]+:[0-9]+)\))?\s*$", line)
278 if m.group(4):
279 start = m.group(1)
280 duration = m.group(4)
281 s = re.search("([0-9]+):([0-9]+)", start)
282 starthour = int(s.group(1))
283 startmin = int(s.group(2))
284 s = re.search("([0-9]+):([0-9]+)", duration)
285 min = startmin+int(s.group(2))
286 hour = starthour+int(s.group(1))
287 if min >= 60:
288 min -= 60
289 hour += 1
290 if hour >= 24:
291 hour -= 24
292 min = string.zfill(repr(min), 2)
293 hour = string.zfill(repr(hour), 2)
294 outputstr = thatdate+" "+m.group(1)+"-"+hour+":"+min+" "+m.group(2)
295 else:
296 outputstr = thatdate+" "+m.group(1)+" "+m.group(2)
297 print outputstr