2020import calendar
2121import collections .abc
2222import datetime
23+ import warnings
2324
2425from google .protobuf .internal import field_mask
2526
3334_MICROS_PER_SECOND = 1000000
3435_SECONDS_PER_DAY = 24 * 3600
3536_DURATION_SECONDS_MAX = 315576000000
37+ _TIMESTAMP_SECONDS_MIN = - 62135596800
38+ _TIMESTAMP_SECONDS_MAX = 253402300799
3639
3740_EPOCH_DATETIME_NAIVE = datetime .datetime (1970 , 1 , 1 , tzinfo = None )
3841_EPOCH_DATETIME_AWARE = _EPOCH_DATETIME_NAIVE .replace (
@@ -85,10 +88,10 @@ def ToJsonString(self):
8588 and uses 3, 6 or 9 fractional digits as required to represent the
8689 exact time. Example of the return format: '1972-01-01T10:00:20.021Z'
8790 """
88- nanos = self .nanos % _NANOS_PER_SECOND
89- total_sec = self .seconds + ( self . nanos - nanos ) // _NANOS_PER_SECOND
90- seconds = total_sec % _SECONDS_PER_DAY
91- days = (total_sec - seconds ) // _SECONDS_PER_DAY
91+ _CheckTimestampValid ( self . seconds , self .nanos )
92+ nanos = self .nanos
93+ seconds = self . seconds % _SECONDS_PER_DAY
94+ days = (self . seconds - seconds ) // _SECONDS_PER_DAY
9295 dt = datetime .datetime (1970 , 1 , 1 ) + datetime .timedelta (days , seconds )
9396
9497 result = dt .isoformat ()
@@ -166,6 +169,7 @@ def FromJsonString(self, value):
166169 else :
167170 seconds += (int (timezone [1 :pos ])* 60 + int (timezone [pos + 1 :]))* 60
168171 # Set seconds and nanos
172+ _CheckTimestampValid (seconds , nanos )
169173 self .seconds = int (seconds )
170174 self .nanos = int (nanos )
171175
@@ -175,39 +179,53 @@ def GetCurrentTime(self):
175179
176180 def ToNanoseconds (self ):
177181 """Converts Timestamp to nanoseconds since epoch."""
182+ _CheckTimestampValid (self .seconds , self .nanos )
178183 return self .seconds * _NANOS_PER_SECOND + self .nanos
179184
180185 def ToMicroseconds (self ):
181186 """Converts Timestamp to microseconds since epoch."""
187+ _CheckTimestampValid (self .seconds , self .nanos )
182188 return (self .seconds * _MICROS_PER_SECOND +
183189 self .nanos // _NANOS_PER_MICROSECOND )
184190
185191 def ToMilliseconds (self ):
186192 """Converts Timestamp to milliseconds since epoch."""
193+ _CheckTimestampValid (self .seconds , self .nanos )
187194 return (self .seconds * _MILLIS_PER_SECOND +
188195 self .nanos // _NANOS_PER_MILLISECOND )
189196
190197 def ToSeconds (self ):
191198 """Converts Timestamp to seconds since epoch."""
199+ _CheckTimestampValid (self .seconds , self .nanos )
192200 return self .seconds
193201
194202 def FromNanoseconds (self , nanos ):
195203 """Converts nanoseconds since epoch to Timestamp."""
196- self .seconds = nanos // _NANOS_PER_SECOND
197- self .nanos = nanos % _NANOS_PER_SECOND
204+ seconds = nanos // _NANOS_PER_SECOND
205+ nanos = nanos % _NANOS_PER_SECOND
206+ _CheckTimestampValid (seconds , nanos )
207+ self .seconds = seconds
208+ self .nanos = nanos
198209
199210 def FromMicroseconds (self , micros ):
200211 """Converts microseconds since epoch to Timestamp."""
201- self .seconds = micros // _MICROS_PER_SECOND
202- self .nanos = (micros % _MICROS_PER_SECOND ) * _NANOS_PER_MICROSECOND
212+ seconds = micros // _MICROS_PER_SECOND
213+ nanos = (micros % _MICROS_PER_SECOND ) * _NANOS_PER_MICROSECOND
214+ _CheckTimestampValid (seconds , nanos )
215+ self .seconds = seconds
216+ self .nanos = nanos
203217
204218 def FromMilliseconds (self , millis ):
205219 """Converts milliseconds since epoch to Timestamp."""
206- self .seconds = millis // _MILLIS_PER_SECOND
207- self .nanos = (millis % _MILLIS_PER_SECOND ) * _NANOS_PER_MILLISECOND
220+ seconds = millis // _MILLIS_PER_SECOND
221+ nanos = (millis % _MILLIS_PER_SECOND ) * _NANOS_PER_MILLISECOND
222+ _CheckTimestampValid (seconds , nanos )
223+ self .seconds = seconds
224+ self .nanos = nanos
208225
209226 def FromSeconds (self , seconds ):
210227 """Converts seconds since epoch to Timestamp."""
228+ _CheckTimestampValid (seconds , 0 )
211229 self .seconds = seconds
212230 self .nanos = 0
213231
@@ -229,6 +247,7 @@ def ToDatetime(self, tzinfo=None):
229247 # https://github.com/python/cpython/issues/109849) or full range (on some
230248 # platforms, see https://github.com/python/cpython/issues/110042) of
231249 # datetime.
250+ _CheckTimestampValid (self .seconds , self .nanos )
232251 delta = datetime .timedelta (
233252 seconds = self .seconds ,
234253 microseconds = _RoundTowardZero (self .nanos , _NANOS_PER_MICROSECOND ),
@@ -252,8 +271,22 @@ def FromDatetime(self, dt):
252271 # manipulated into a long value of seconds. During the conversion from
253272 # struct_time to long, the source date in UTC, and so it follows that the
254273 # correct transformation is calendar.timegm()
255- self .seconds = calendar .timegm (dt .utctimetuple ())
256- self .nanos = dt .microsecond * _NANOS_PER_MICROSECOND
274+ seconds = calendar .timegm (dt .utctimetuple ())
275+ nanos = dt .microsecond * _NANOS_PER_MICROSECOND
276+ _CheckTimestampValid (seconds , nanos )
277+ self .seconds = seconds
278+ self .nanos = nanos
279+
280+
281+ def _CheckTimestampValid (seconds , nanos ):
282+ if seconds < _TIMESTAMP_SECONDS_MIN or seconds > _TIMESTAMP_SECONDS_MAX :
283+ raise ValueError (
284+ 'Timestamp is not valid: Seconds {0} must be in range '
285+ '[-62135596800, 253402300799].' .format (seconds ))
286+ if nanos < 0 or nanos >= _NANOS_PER_SECOND :
287+ raise ValueError (
288+ 'Timestamp is not valid: Nanos {} must be in a range '
289+ '[0, 999999].' .format (nanos ))
257290
258291
259292class Duration (object ):
0 commit comments