Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(121)

Side by Side Diff: third_party/google-endpoints/strict_rfc3339.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2012 (C) Daniel Richman, Adam Greig
2 #
3 # This file is part of strict_rfc3339.
4 #
5 # strict_rfc3339 is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # strict_rfc3339 is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with strict_rfc3339. If not, see <http://www.gnu.org/licenses/>.
17
18 """
19 Super simple lightweight RFC3339 functions
20 """
21
22 import re
23 import time
24 import calendar
25
26 __all__ = ["validate_rfc3339",
27 "InvalidRFC3339Error",
28 "rfc3339_to_timestamp",
29 "timestamp_to_rfc3339_utcoffset",
30 "timestamp_to_rfc3339_localoffset",
31 "now_to_rfc3339_utcoffset",
32 "now_to_rfc3339_localoffset"]
33
34 rfc3339_regex = re.compile(
35 r"^(\d\d\d\d)\-(\d\d)\-(\d\d)T"
36 r"(\d\d):(\d\d):(\d\d)(\.\d+)?(Z|([+\-])(\d\d):(\d\d))$")
37
38
39 def validate_rfc3339(datestring):
40 """Check an RFC3339 string is valid via a regex and some range checks"""
41
42 m = rfc3339_regex.match(datestring)
43 if m is None:
44 return False
45
46 groups = m.groups()
47
48 year, month, day, hour, minute, second = [int(i) for i in groups[:6]]
49
50 if not 1 <= year <= 9999:
51 # Have to reject this, unfortunately (despite it being OK by rfc3339):
52 # calendar.timegm/calendar.monthrange can't cope (since datetime can't)
53 return False
54
55 if not 1 <= month <= 12:
56 return False
57
58 (_, max_day) = calendar.monthrange(year, month)
59 if not 1 <= day <= max_day:
60 return False
61
62 if not (0 <= hour <= 23 and 0 <= minute <= 59 and 0 <= second <= 59):
63 # forbid leap seconds :-(. See README
64 return False
65
66 if groups[7] != "Z":
67 (offset_sign, offset_hours, offset_mins) = groups[8:]
68 if not (0 <= int(offset_hours) <= 23 and 0 <= int(offset_mins) <= 59):
69 return False
70
71 # all OK
72 return True
73
74
75 class InvalidRFC3339Error(ValueError):
76 """Subclass of ValueError thrown by rfc3339_to_timestamp"""
77 pass
78
79
80 def rfc3339_to_timestamp(datestring):
81 """Convert an RFC3339 date-time string to a UTC UNIX timestamp"""
82
83 if not validate_rfc3339(datestring):
84 raise InvalidRFC3339Error
85
86 groups = rfc3339_regex.match(datestring).groups()
87
88 time_tuple = [int(p) for p in groups[:6]]
89 timestamp = calendar.timegm(time_tuple)
90
91 seconds_part = groups[6]
92 if seconds_part is not None:
93 timestamp += float("0" + seconds_part)
94
95 if groups[7] != "Z":
96 (offset_sign, offset_hours, offset_mins) = groups[8:]
97 offset_seconds = int(offset_hours) * 3600 + int(offset_mins) * 60
98 if offset_sign == '-':
99 offset_seconds = -offset_seconds
100 timestamp -= offset_seconds
101
102 return timestamp
103
104
105 def _seconds_and_microseconds(timestamp):
106 """
107 Split a floating point timestamp into an integer number of seconds since
108 the epoch, and an integer number of microseconds (having rounded to the
109 nearest microsecond).
110
111 If `_seconds_and_microseconds(x) = (y, z)` then the following holds (up to
112 the error introduced by floating point operations):
113
114 * `x = y + z / 1_000_000.`
115 * `0 <= z < 1_000_000.`
116 """
117
118 if isinstance(timestamp, int):
119 return (timestamp, 0)
120 else:
121 timestamp_us = int(round(timestamp * 1e6))
122 return divmod(timestamp_us, 1000000)
123
124 def _make_datestring_start(time_tuple, microseconds):
125 ds_format = "{0:04d}-{1:02d}-{2:02d}T{3:02d}:{4:02d}:{5:02d}"
126 datestring = ds_format.format(*time_tuple)
127
128 seconds_part_str = "{0:06d}".format(microseconds)
129 # There used to be a bug here where it could be 1000000
130 assert len(seconds_part_str) == 6 and seconds_part_str[0] != '-'
131 seconds_part_str = seconds_part_str.rstrip("0")
132 if seconds_part_str != "":
133 datestring += "." + seconds_part_str
134
135 return datestring
136
137
138 def timestamp_to_rfc3339_utcoffset(timestamp):
139 """Convert a UTC UNIX timestamp to RFC3339, with the offset as 'Z'"""
140
141 seconds, microseconds = _seconds_and_microseconds(timestamp)
142
143 time_tuple = time.gmtime(seconds)
144 datestring = _make_datestring_start(time_tuple, microseconds)
145 datestring += "Z"
146
147 assert abs(rfc3339_to_timestamp(datestring) - timestamp) < 0.000001
148 return datestring
149
150
151 def timestamp_to_rfc3339_localoffset(timestamp):
152 """
153 Convert a UTC UNIX timestamp to RFC3339, using the local offset.
154
155 localtime() provides the time parts. The difference between gmtime and
156 localtime tells us the offset.
157 """
158
159 seconds, microseconds = _seconds_and_microseconds(timestamp)
160
161 time_tuple = time.localtime(seconds)
162 datestring = _make_datestring_start(time_tuple, microseconds)
163
164 gm_time_tuple = time.gmtime(seconds)
165 offset = calendar.timegm(time_tuple) - calendar.timegm(gm_time_tuple)
166
167 if abs(offset) % 60 != 0:
168 raise ValueError("Your local offset is not a whole minute")
169
170 offset_minutes = abs(offset) // 60
171 offset_hours = offset_minutes // 60
172 offset_minutes %= 60
173
174 offset_string = "{0:02d}:{1:02d}".format(offset_hours, offset_minutes)
175
176 if offset < 0:
177 datestring += "-"
178 else:
179 datestring += "+"
180
181 datestring += offset_string
182 assert abs(rfc3339_to_timestamp(datestring) - timestamp) < 0.000001
183
184 return datestring
185
186
187 def now_to_rfc3339_utcoffset(integer=True):
188 """Convert the current time to RFC3339, with the offset as 'Z'"""
189
190 timestamp = time.time()
191 if integer:
192 timestamp = int(timestamp)
193 return timestamp_to_rfc3339_utcoffset(timestamp)
194
195
196 def now_to_rfc3339_localoffset(integer=True):
197 """Convert the current time to RFC3339, using the local offset."""
198
199 timestamp = time.time()
200 if integer:
201 timestamp = int(timestamp)
202 return timestamp_to_rfc3339_localoffset(timestamp)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698