Basic refactoring and Tom's recommendations
[imap-fix-internaldate] / date_interpreter.py
CommitLineData
c9da760a
PD
1'''
2date_interpreter.py - The module contains the MailIterator class.
3
4Copyright (c) 2012 Intra2net AG
5Author: Plamen Dimitrov
6
7This program is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
c9da760a
PD
16'''
17
7a1d4c35 18import datetime, time
c9da760a 19import re
c9da760a
PD
20
21#reg expressions
22RECEIVED_DATE = re.compile(r'(0?[1-9]|[1-2][0-9]|3[01])\s+([A-Z][a-z][a-z])\s+'
23 r'(19[0-9]{2}|[2-9][0-9]{3}|[0-9]{2})\s+(2[0-3]|[0-1][0-9]):([0-5][0-9])(?::(60|[0-5][0-9]))?\s*'
24 r'(?:([-\+])([0-9]{2})([0-5][0-9]))*')
25INTERNAL_DATE = re.compile(r'(?P<day>[ 0123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9])'
26 r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
27 r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])')
28CONTROL_SYMBOLS = re.compile(r'[\n\r\t]')
29
30class DateInterpreter:
7a1d4c35
PD
31 """This class extracts dates from imap server responses and compares them.
32 This class contains only static methods."""
c9da760a
PD
33
34 def __init__(self):
35 return
36
37 @classmethod
38 def extract_internal_date(cls, fetchresult):
39 """Extracts the internal date from INTERNALDATE, returns datetime."""
40 return datetime.datetime.fromtimestamp(time.mktime(fetchresult))
41
42 @classmethod
43 def extract_received_date(cls, fetchresult):
44 """Extracts the first date from RECEIVED, returns datetime."""
45 fetchresult = CONTROL_SYMBOLS.sub('', fetchresult[0][1].decode("utf-8"))
46 received_dates = RECEIVED_DATE.findall(fetchresult)
47 if(len(received_dates)==0):
48 return ""
49 else: received_date = received_dates[0]
50 #print("Retrieved date ", received_date, " from header ", fetchresult)
51 month = datetime.datetime.strptime(received_date[1],'%b').month
52
53 if(received_date[3]!=""):
54 hours = int(received_date[3])
55 else: hours = 0
56 if(received_date[4]!=""):
57 minutes = int(received_date[4])
58 else: minutes = 0
59 if(received_date[5]!=""):
60 seconds = int(received_date[5])
61 else: seconds = 0
62
63 if(received_date[6]!=""):
64 zonen = received_date[6]
65 else: zonen = b'+'
66 if(received_date[7]!=""):
67 zoneh = int(received_date[7])
68 else: zoneh = 0
69 if(received_date[8]!=""):
70 zonem = int(received_date[8])
71 else: zonem = 0
72 # subtract time zone to get unified time
73 zone = (zoneh * 60 + zonem) * 60
74 if(zonen == b'-'):
75 zone = -zone
76
77 time_tuple = (int(received_date[2]), month, int(received_date[0]), hours, minutes, seconds, -1, -1, -1)
78 #'mktime' assumes arg in local timezone, so add timezone/altzone
79 utc = time.mktime(time_tuple)
80 #adjust to DST
81 if(time.daylight and time.localtime(utc)[-1]):
82 zone = zone + time.altzone
83 else:
84 zone = zone + time.timezone
85
86 received_time_tuple = time.localtime(utc - zone)
87 converted_received_date = datetime.datetime.fromtimestamp(time.mktime(received_time_tuple))
88 return converted_received_date
89
90 @classmethod
91 def compare_dates(cls, date1, date2, tolerance=1800):
92 """Compares datetime objects for deviation given certain tolerance."""
93 """Returns 1 if there is a significant difference."""
94 #print(date1, "<>", date2)
95 timedelta = abs(date1 - date2)
96 if(timedelta.total_seconds()>tolerance):
8301e589 97 return True
c9da760a 98 else:
8301e589 99 return False