1 # Copyright 2010-2023 Kurt McKee <contactme@kurtmckee.org> 2 # Copyright 2002-2008 Mark Pilgrim 3 # All rights reserved. 4 # 5 # This file is a part of feedparser. 6 # 7 # Redistribution and use in source and binary forms, with or without 8 # modification, are permitted provided that the following conditions are met: 9 # 10 # * Redistributions of source code must retain the above copyright notice, 11 # this list of conditions and the following disclaimer. 12 # * Redistributions in binary form must reproduce the above copyright notice, 13 # this list of conditions and the following disclaimer in the documentation 14 # and/or other materials provided with the distribution. 15 # 16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' 17 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 # POSSIBILITY OF SUCH DAMAGE. 27 28 import datetime 29 30 timezonenames = { 31 'ut': 0, 'gmt': 0, 'z': 0, 32 'adt': -3, 'ast': -4, 'at': -4, 33 'edt': -4, 'est': -5, 'et': -5, 34 'cdt': -5, 'cst': -6, 'ct': -6, 35 'mdt': -6, 'mst': -7, 'mt': -7, 36 'pdt': -7, 'pst': -8, 'pt': -8, 37 'a': -1, 'n': 1, 38 'm': -12, 'y': 12, 39 } 40 # W3 date and time format parser 41 # http://www.w3.org/TR/NOTE-datetime 42 # Also supports MSSQL-style datetimes as defined at: 43 # http://msdn.microsoft.com/en-us/library/ms186724.aspx 44 # (basically, allow a space as a date/time/timezone separator) 45 46 47 def _parse_date_w3dtf(datestr): 48 if not datestr.strip(): 49 return None 50 parts = datestr.lower().split('t') 51 if len(parts) == 1: 52 # This may be a date only, or may be an MSSQL-style date 53 parts = parts[0].split() 54 if len(parts) == 1: 55 # Treat this as a date only 56 parts.append('00:00:00z') 57 elif len(parts) > 2: 58 return None 59 date = parts[0].split('-', 2) 60 if not date or len(date[0]) != 4: 61 return None 62 # Ensure that `date` has 3 elements. Using '1' sets the default 63 # month to January and the default day to the 1st of the month. 64 date.extend(['1'] * (3 - len(date))) 65 try: 66 year, month, day = [int(i) for i in date] 67 except ValueError: 68 # `date` may have more than 3 elements or may contain 69 # non-integer strings. 70 return None 71 if parts[1].endswith('z'): 72 parts[1] = parts[1][:-1] 73 parts.append('z') 74 # Append the numeric timezone offset, if any, to parts. 75 # If this is an MSSQL-style date then parts[2] already contains 76 # the timezone information, so `append()` will not affect it. 77 # Add 1 to each value so that if `find()` returns -1 it will be 78 # treated as False. 79 loc = parts[1].find('-') + 1 or parts[1].find('+') + 1 or len(parts[1]) + 1 80 loc = loc - 1 81 parts.append(parts[1][loc:]) 82 parts[1] = parts[1][:loc] 83 time = parts[1].split(':', 2) 84 # Ensure that time has 3 elements. Using '0' means that the 85 # minutes and seconds, if missing, will default to 0. 86 time.extend(['0'] * (3 - len(time))) 87 if parts[2][:1] in ('-', '+'): 88 try: 89 tzhour = int(parts[2][1:3]) 90 tzmin = int(parts[2][4:]) 91 except ValueError: 92 return None 93 if parts[2].startswith('-'): 94 tzhour = tzhour * -1 95 tzmin = tzmin * -1 96 else: 97 tzhour = timezonenames.get(parts[2], 0) 98 tzmin = 0 99 try: 100 hour, minute, second = [int(float(i)) for i in time] 101 except ValueError: 102 return None 103 # Create the datetime object and timezone delta objects 104 try: 105 stamp = datetime.datetime(year, month, day, hour, minute, second) 106 except ValueError: 107 return None 108 delta = datetime.timedelta(0, 0, 0, 0, tzmin, tzhour) 109 # Return the date and timestamp in a UTC 9-tuple 110 try: 111 return (stamp - delta).utctimetuple() 112 except (OverflowError, ValueError): 113 # IronPython throws ValueErrors instead of OverflowErrors 114 return None 115