2 date_interpreter.py - The module contains the MailIterator class.
4 Copyright (c) 2012 Intra2net AG
5 Author: Plamen Dimitrov
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.
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.
17 Add '-t' argument when running the module for a test mode.
18 For a detailed list of each message with a date conflict change
19 the 'log_level' in the configuration file from '30' to '20'.
27 RECEIVED_DATE = re.compile(r'(0?[1-9]|[1-2][0-9]|3[01])\s+([A-Z][a-z][a-z])\s+'
28 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*'
29 r'(?:([-\+])([0-9]{2})([0-5][0-9]))*')
30 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])'
31 r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
32 r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])')
33 CONTROL_SYMBOLS = re.compile(r'[\n\r\t]')
35 class DateInterpreter:
36 """This class extracts dates from imap server responses and compares them."""
42 def extract_internal_date(cls, fetchresult):
43 """Extracts the internal date from INTERNALDATE, returns datetime."""
44 return datetime.datetime.fromtimestamp(time.mktime(fetchresult))
47 def extract_received_date(cls, fetchresult):
48 """Extracts the first date from RECEIVED, returns datetime."""
49 fetchresult = CONTROL_SYMBOLS.sub('', fetchresult[0][1].decode("utf-8"))
50 received_dates = RECEIVED_DATE.findall(fetchresult)
51 if(len(received_dates)==0):
53 else: received_date = received_dates[0]
54 #print("Retrieved date ", received_date, " from header ", fetchresult)
55 month = datetime.datetime.strptime(received_date[1],'%b').month
57 if(received_date[3]!=""):
58 hours = int(received_date[3])
60 if(received_date[4]!=""):
61 minutes = int(received_date[4])
63 if(received_date[5]!=""):
64 seconds = int(received_date[5])
67 if(received_date[6]!=""):
68 zonen = received_date[6]
70 if(received_date[7]!=""):
71 zoneh = int(received_date[7])
73 if(received_date[8]!=""):
74 zonem = int(received_date[8])
76 # subtract time zone to get unified time
77 zone = (zoneh * 60 + zonem) * 60
81 time_tuple = (int(received_date[2]), month, int(received_date[0]), hours, minutes, seconds, -1, -1, -1)
82 #'mktime' assumes arg in local timezone, so add timezone/altzone
83 utc = time.mktime(time_tuple)
85 if(time.daylight and time.localtime(utc)[-1]):
86 zone = zone + time.altzone
88 zone = zone + time.timezone
90 received_time_tuple = time.localtime(utc - zone)
91 converted_received_date = datetime.datetime.fromtimestamp(time.mktime(received_time_tuple))
92 return converted_received_date
95 def compare_dates(cls, date1, date2, tolerance=1800):
96 """Compares datetime objects for deviation given certain tolerance."""
97 """Returns 1 if there is a significant difference."""
98 #print(date1, "<>", date2)
99 timedelta = abs(date1 - date2)
100 if(timedelta.total_seconds()>tolerance):