Commit | Line | Data |
---|---|---|
c9da760a | 1 | ''' |
8a9d4c89 | 2 | mail_date_parser.py - The module contains the MailDateParser class. |
c9da760a PD |
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. | |
c9da760a PD |
16 | ''' |
17 | ||
7a1d4c35 | 18 | import datetime, time |
c9da760a | 19 | import re |
8fe4e3ff | 20 | import logging |
c9da760a PD |
21 | |
22 | #reg expressions | |
23 | RECEIVED_DATE = re.compile(r'(0?[1-9]|[1-2][0-9]|3[01])\s+([A-Z][a-z][a-z])\s+' | |
24 | 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*' | |
25 | r'(?:([-\+])([0-9]{2})([0-5][0-9]))*') | |
26 | 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])' | |
27 | r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])' | |
28 | r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])') | |
29 | CONTROL_SYMBOLS = re.compile(r'[\n\r\t]') | |
30 | ||
8a9d4c89 | 31 | class MailDateParser: |
7a1d4c35 PD |
32 | """This class extracts dates from imap server responses and compares them. |
33 | This class contains only static methods.""" | |
c9da760a PD |
34 | |
35 | def __init__(self): | |
36 | return | |
37 | ||
38 | @classmethod | |
39 | def extract_internal_date(cls, fetchresult): | |
40 | """Extracts the internal date from INTERNALDATE, returns datetime.""" | |
41 | return datetime.datetime.fromtimestamp(time.mktime(fetchresult)) | |
42 | ||
43 | @classmethod | |
44 | def extract_received_date(cls, fetchresult): | |
45 | """Extracts the first date from RECEIVED, returns datetime.""" | |
8a9d4c89 | 46 | fetchresult = CONTROL_SYMBOLS.sub('', fetchresult) |
c9da760a PD |
47 | received_dates = RECEIVED_DATE.findall(fetchresult) |
48 | if(len(received_dates)==0): | |
49 | return "" | |
50 | else: received_date = received_dates[0] | |
8fe4e3ff | 51 | logging.debug("Retrieved date %s from header %s.", received_date, fetchresult) |
c9da760a PD |
52 | month = datetime.datetime.strptime(received_date[1],'%b').month |
53 | ||
54 | if(received_date[3]!=""): | |
55 | hours = int(received_date[3]) | |
56 | else: hours = 0 | |
57 | if(received_date[4]!=""): | |
58 | minutes = int(received_date[4]) | |
59 | else: minutes = 0 | |
60 | if(received_date[5]!=""): | |
61 | seconds = int(received_date[5]) | |
62 | else: seconds = 0 | |
63 | ||
64 | if(received_date[6]!=""): | |
65 | zonen = received_date[6] | |
66 | else: zonen = b'+' | |
67 | if(received_date[7]!=""): | |
68 | zoneh = int(received_date[7]) | |
69 | else: zoneh = 0 | |
70 | if(received_date[8]!=""): | |
71 | zonem = int(received_date[8]) | |
72 | else: zonem = 0 | |
73 | # subtract time zone to get unified time | |
74 | zone = (zoneh * 60 + zonem) * 60 | |
75 | if(zonen == b'-'): | |
76 | zone = -zone | |
77 | ||
78 | time_tuple = (int(received_date[2]), month, int(received_date[0]), hours, minutes, seconds, -1, -1, -1) | |
79 | #'mktime' assumes arg in local timezone, so add timezone/altzone | |
80 | utc = time.mktime(time_tuple) | |
81 | #adjust to DST | |
82 | if(time.daylight and time.localtime(utc)[-1]): | |
83 | zone = zone + time.altzone | |
84 | else: | |
85 | zone = zone + time.timezone | |
86 | ||
87 | received_time_tuple = time.localtime(utc - zone) | |
88 | converted_received_date = datetime.datetime.fromtimestamp(time.mktime(received_time_tuple)) | |
89 | return converted_received_date | |
90 | ||
91 | @classmethod | |
92 | def compare_dates(cls, date1, date2, tolerance=1800): | |
93 | """Compares datetime objects for deviation given certain tolerance.""" | |
94 | """Returns 1 if there is a significant difference.""" | |
8fe4e3ff | 95 | logging.debug("Comparing dates %s <> %s.", date1, date2) |
c9da760a PD |
96 | timedelta = abs(date1 - date2) |
97 | if(timedelta.total_seconds()>tolerance): | |
8301e589 | 98 | return True |
c9da760a | 99 | else: |
8301e589 | 100 | return False |