Run update-copyright.py
[pycalendar.git] / pycalendar / dtype / datetime.py
1 # Copyright (C) 2013 W. Trevor King <wking@tremily.us>
2 #
3 # This file is part of pycalender.
4 #
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
8 # version.
9 #
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.
13 #
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/>.
16
17 """Functions for processing dates with times
18
19 As defined in :RFC:`5545`, section 3.3.5 (Date-Time).
20 """
21
22 import datetime as _datetime
23
24 from . import base as _base
25 from . import date as _date
26 from . import time as _time
27
28
29 class DateTime (_base.DataType):
30     name = 'DATE-TIME'
31
32     @classmethod
33     def decode(cls, property, value):
34         """Parse dates with times
35
36         As defined in :RFC:`5545`, section 3.3.5 (Date-Time).
37
38         >>> import pytz
39
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)
44
45         The following represents 2:00 A.M. in New York on January 19,
46         1998:
47
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>)
53
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).
60
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>)
65
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
71         (UTC-05:00).
72
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>)
77
78         A time value MUST only specify the second 60 when specifying a
79         positive leap second.  For example:
80
81         >>> DateTime.decode(property={}, value='19970630T235960Z')
82         datetime.datetime(1997, 6, 30, 23, 59, 59, tzinfo=datetime.timezone.utc)
83
84         Implementations that do not support leap seconds SHOULD interpret
85         the second 60 as equivalent to the second 59.
86
87         The following represents July 14, 1997, at 1:30 PM in New York
88         City in each of the three time formats
89
90         >>> DateTime.decode(property={}, value='19970714T133000')
91         datetime.datetime(1997, 7, 14, 13, 30)
92         >>> d = DateTime.decode(property={}, value='19970714T173000Z')
93         >>> d
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>)
103         """
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)
108
109     @classmethod
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()),
114             )