Basic refactoring and Tom's recommendations
[imap-fix-internaldate] / date_interpreter.py
1 '''
2 date_interpreter.py - The module contains the MailIterator class.
3
4 Copyright (c) 2012 Intra2net AG
5 Author: Plamen Dimitrov
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 '''
17
18 import datetime, time
19 import re
20
21 #reg expressions
22 RECEIVED_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]))*')
25 INTERNAL_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])')
28 CONTROL_SYMBOLS = re.compile(r'[\n\r\t]')
29
30 class DateInterpreter:
31     """This class extracts dates from imap server responses and compares them.
32     This class contains only static methods."""
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):
97             return True
98         else:
99             return False