1 # Copyright (C) 2013 W. Trevor King <wking@tremily.us>
3 # This file is part of pycalender.
5 # pycalender is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 3 of the License, or (at your option) any later
10 # pycalender is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along with
15 # pycalender. If not, see <http://www.gnu.org/licenses/>.
17 """Functions for processing dates with times
19 As defined in :RFC:`5545`, section 3.3.5 (Date-Time).
22 import datetime as _datetime
24 from . import base as _base
25 from . import date as _date
26 from . import time as _time
29 class DateTime (_base.DataType):
33 def decode(cls, property, value):
34 """Parse dates with times
36 As defined in :RFC:`5545`, section 3.3.5 (Date-Time).
40 >>> DateTime.decode(property={}, value='19980118T230000')
41 datetime.datetime(1998, 1, 18, 23, 0)
42 >>> DateTime.decode(property={}, value='19980119T070000Z')
43 datetime.datetime(1998, 1, 19, 7, 0, tzinfo=datetime.timezone.utc)
45 The following represents 2:00 A.M. in New York on January 19,
48 >>> ny = {'TZID': 'America/New_York'}
49 >>> DateTime.decode(property=ny, value='19980119T020000')
50 ... # doctest: +NORMALIZE_WHITESPACE
51 datetime.datetime(1998, 1, 19, 2, 0,
52 tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
54 If, based on the definition of the referenced time zone, the local
55 time described occurs more than once (when changing from daylight
56 to standard time), the ``DATE-TIME`` value refers to the first
57 occurrence of the referenced time. Thus,
58 ``TZID=America/New_York:20071104T013000`` indicates November 4,
59 2007 at 1:30 A.M. EDT (UTC-04:00).
61 >>> DateTime.decode(property=ny, value='20071104T013000')
62 ... # doctest: +NORMALIZE_WHITESPACE
63 datetime.datetime(2007, 11, 4, 1, 30,
64 tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
66 If the local time described does not occur (when changing from
67 standard to daylight time), the ``DATE-TIME`` value is interpreted
68 using the UTC offset before the gap in local times. Thus,
69 ``TZID=America/New_York:20070311T023000`` indicates March 11, 2007
70 at 3:30 A.M. EDT (UTC-04:00), one hour after 1:30 A.M. EST
73 >>> DateTime.decode(property=ny, value='20070311T023000')
74 ... # doctest: +NORMALIZE_WHITESPACE
75 datetime.datetime(2007, 3, 11, 2, 30,
76 tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
78 A time value MUST only specify the second 60 when specifying a
79 positive leap second. For example:
81 >>> DateTime.decode(property={}, value='19970630T235960Z')
82 datetime.datetime(1997, 6, 30, 23, 59, 59, tzinfo=datetime.timezone.utc)
84 Implementations that do not support leap seconds SHOULD interpret
85 the second 60 as equivalent to the second 59.
87 The following represents July 14, 1997, at 1:30 PM in New York
88 City in each of the three time formats
90 >>> DateTime.decode(property={}, value='19970714T133000')
91 datetime.datetime(1997, 7, 14, 13, 30)
92 >>> d = DateTime.decode(property={}, value='19970714T173000Z')
94 datetime.datetime(1997, 7, 14, 17, 30, tzinfo=datetime.timezone.utc)
95 >>> d.astimezone(pytz.timezone('America/New_York'))
96 ... # doctest: +NORMALIZE_WHITESPACE
97 datetime.datetime(1997, 7, 14, 13, 30,
98 tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
99 >>> DateTime.decode(property=ny, value='19970714T133000')
100 ... # doctest: +NORMALIZE_WHITESPACE
101 datetime.datetime(1997, 7, 14, 13, 30,
102 tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
104 date,time = value.split('T')
105 date = _date.Date.decode(property=property, value=date)
106 time = _time.Time.decode(property=property, value=time)
107 return _datetime.datetime.combine(date=date, time=time)
110 def encode(cls, property, value):
111 return '{}T{}'.format(
112 _date.Date.encode(property=property, value=value.date()),
113 _time.Time.encode(property=property, value=value.timetz()),