1# -*- coding: utf-8 -*- 2"""POSIX timestamp implementation.""" 3 4import decimal 5 6from dfdatetime import definitions 7from dfdatetime import factory 8from dfdatetime import interface 9 10 11class PosixTimeEpoch(interface.DateTimeEpoch): 12 """POSIX time epoch.""" 13 14 def __init__(self): 15 """Initializes a POSIX time epoch.""" 16 super(PosixTimeEpoch, self).__init__(1970, 1, 1) 17 18 19class PosixTime(interface.DateTimeValues): 20 """POSIX timestamp. 21 22 The POSIX timestamp is a signed integer that contains the number of 23 seconds since 1970-01-01 00:00:00 (also known as the POSIX epoch). 24 Negative values represent date and times predating the POSIX epoch. 25 26 The POSIX timestamp was initially 32-bit though 64-bit variants 27 are known to be used. 28 29 Attributes: 30 is_local_time (bool): True if the date and time value is in local time. 31 """ 32 33 _EPOCH = PosixTimeEpoch() 34 35 def __init__(self, time_zone_offset=None, timestamp=None): 36 """Initializes a POSIX timestamp. 37 38 Args: 39 time_zone_offset (Optional[int]): time zone offset in number of minutes 40 from UTC or None if not set. 41 timestamp (Optional[int]): POSIX timestamp. 42 """ 43 super(PosixTime, self).__init__(time_zone_offset=time_zone_offset) 44 self._precision = definitions.PRECISION_1_SECOND 45 self._timestamp = timestamp 46 47 @property 48 def timestamp(self): 49 """int: POSIX timestamp or None if not set.""" 50 return self._timestamp 51 52 def _GetNormalizedTimestamp(self): 53 """Retrieves the normalized timestamp. 54 55 Returns: 56 decimal.Decimal: normalized timestamp, which contains the number of 57 seconds since January 1, 1970 00:00:00 and a fraction of second used 58 for increased precision, or None if the normalized timestamp cannot be 59 determined. 60 """ 61 if self._normalized_timestamp is None: 62 if self._timestamp is not None: 63 self._normalized_timestamp = decimal.Decimal(self._timestamp) 64 65 if self._time_zone_offset: 66 self._normalized_timestamp -= self._time_zone_offset * 60 67 68 return self._normalized_timestamp 69 70 def CopyFromDateTimeString(self, time_string): 71 """Copies a POSIX timestamp from a date and time string. 72 73 Args: 74 time_string (str): date and time value formatted as: 75 YYYY-MM-DD hh:mm:ss.######[+-]##:## 76 77 Where # are numeric digits ranging from 0 to 9 and the seconds 78 fraction can be either 3 or 6 digits. The time of day, seconds 79 fraction and time zone offset are optional. The default time zone 80 is UTC. 81 """ 82 date_time_values = self._CopyDateTimeFromString(time_string) 83 84 year = date_time_values.get('year', 0) 85 month = date_time_values.get('month', 0) 86 day_of_month = date_time_values.get('day_of_month', 0) 87 hours = date_time_values.get('hours', 0) 88 minutes = date_time_values.get('minutes', 0) 89 seconds = date_time_values.get('seconds', 0) 90 time_zone_offset = date_time_values.get('time_zone_offset', 0) 91 92 self._timestamp = self._GetNumberOfSecondsFromElements( 93 year, month, day_of_month, hours, minutes, seconds) 94 self._time_zone_offset = time_zone_offset 95 96 def CopyToDateTimeString(self): 97 """Copies the POSIX timestamp to a date and time string. 98 99 Returns: 100 str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss" or None 101 if the timestamp is missing. 102 """ 103 if self._timestamp is None: 104 return None 105 106 number_of_days, hours, minutes, seconds = self._GetTimeValues( 107 self._timestamp) 108 109 year, month, day_of_month = self._GetDateValuesWithEpoch( 110 number_of_days, self._EPOCH) 111 112 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( 113 year, month, day_of_month, hours, minutes, seconds) 114 115 116class PosixTimeInMilliseconds(interface.DateTimeValues): 117 """POSIX timestamp in milliseconds. 118 119 Variant of the POSIX timestamp in milliseconds. 120 121 Attributes: 122 is_local_time (bool): True if the date and time value is in local time. 123 """ 124 125 _EPOCH = PosixTimeEpoch() 126 127 def __init__(self, time_zone_offset=None, timestamp=None): 128 """Initializes a POSIX timestamp in milliseconds. 129 130 Args: 131 time_zone_offset (Optional[int]): time zone offset in number of minutes 132 from UTC or None if not set. 133 timestamp (Optional[int]): POSIX timestamp in milliseconds. 134 """ 135 super(PosixTimeInMilliseconds, self).__init__( 136 time_zone_offset=time_zone_offset) 137 self._precision = definitions.PRECISION_1_MILLISECOND 138 self._timestamp = timestamp 139 140 @property 141 def timestamp(self): 142 """int: POSIX timestamp in milliseconds or None if not set.""" 143 return self._timestamp 144 145 def _GetNormalizedTimestamp(self): 146 """Retrieves the normalized timestamp. 147 148 Returns: 149 decimal.Decimal: normalized timestamp, which contains the number of 150 seconds since January 1, 1970 00:00:00 and a fraction of second used 151 for increased precision, or None if the normalized timestamp cannot be 152 determined. 153 """ 154 if self._normalized_timestamp is None: 155 if self._timestamp is not None: 156 self._normalized_timestamp = ( 157 decimal.Decimal(self._timestamp) / 158 definitions.MILLISECONDS_PER_SECOND) 159 160 if self._time_zone_offset: 161 self._normalized_timestamp -= self._time_zone_offset * 60 162 163 return self._normalized_timestamp 164 165 def CopyFromDateTimeString(self, time_string): 166 """Copies a POSIX timestamp from a date and time string. 167 168 Args: 169 time_string (str): date and time value formatted as: 170 YYYY-MM-DD hh:mm:ss.######[+-]##:## 171 172 Where # are numeric digits ranging from 0 to 9 and the seconds 173 fraction can be either 3 or 6 digits. The time of day, seconds 174 fraction and time zone offset are optional. The default time zone 175 is UTC. 176 """ 177 date_time_values = self._CopyDateTimeFromString(time_string) 178 179 year = date_time_values.get('year', 0) 180 month = date_time_values.get('month', 0) 181 day_of_month = date_time_values.get('day_of_month', 0) 182 hours = date_time_values.get('hours', 0) 183 minutes = date_time_values.get('minutes', 0) 184 seconds = date_time_values.get('seconds', 0) 185 microseconds = date_time_values.get('microseconds', 0) 186 time_zone_offset = date_time_values.get('time_zone_offset', 0) 187 188 timestamp = self._GetNumberOfSecondsFromElements( 189 year, month, day_of_month, hours, minutes, seconds) 190 timestamp *= definitions.MILLISECONDS_PER_SECOND 191 192 if microseconds: 193 milliseconds, _ = divmod( 194 microseconds, definitions.MILLISECONDS_PER_SECOND) 195 timestamp += milliseconds 196 197 self._timestamp = timestamp 198 self._time_zone_offset = time_zone_offset 199 200 def CopyToDateTimeString(self): 201 """Copies the POSIX timestamp to a date and time string. 202 203 Returns: 204 str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or 205 None if the timestamp is missing. 206 """ 207 if self._timestamp is None: 208 return None 209 210 timestamp, milliseconds = divmod( 211 self._timestamp, definitions.MILLISECONDS_PER_SECOND) 212 number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) 213 214 year, month, day_of_month = self._GetDateValuesWithEpoch( 215 number_of_days, self._EPOCH) 216 217 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:03d}'.format( 218 year, month, day_of_month, hours, minutes, seconds, milliseconds) 219 220 221class PosixTimeInMicroseconds(interface.DateTimeValues): 222 """POSIX timestamp in microseconds. 223 224 Variant of the POSIX timestamp in microseconds. 225 226 Attributes: 227 is_local_time (bool): True if the date and time value is in local time. 228 """ 229 230 _EPOCH = PosixTimeEpoch() 231 232 def __init__(self, time_zone_offset=None, timestamp=None): 233 """Initializes a POSIX timestamp in microseconds. 234 235 Args: 236 time_zone_offset (Optional[int]): time zone offset in number of minutes 237 from UTC or None if not set. 238 timestamp (Optional[int]): POSIX timestamp in microseconds. 239 """ 240 super(PosixTimeInMicroseconds, self).__init__( 241 time_zone_offset=time_zone_offset) 242 self._precision = definitions.PRECISION_1_MICROSECOND 243 self._timestamp = timestamp 244 245 @property 246 def timestamp(self): 247 """int: POSIX timestamp in microseconds or None if not set.""" 248 return self._timestamp 249 250 def _GetNormalizedTimestamp(self): 251 """Retrieves the normalized timestamp. 252 253 Returns: 254 decimal.Decimal: normalized timestamp, which contains the number of 255 seconds since January 1, 1970 00:00:00 and a fraction of second used 256 for increased precision, or None if the normalized timestamp cannot be 257 determined. 258 """ 259 if self._normalized_timestamp is None: 260 if self._timestamp is not None: 261 self._normalized_timestamp = ( 262 decimal.Decimal(self._timestamp) / 263 definitions.MICROSECONDS_PER_SECOND) 264 265 if self._time_zone_offset: 266 self._normalized_timestamp -= self._time_zone_offset * 60 267 268 return self._normalized_timestamp 269 270 def CopyFromDateTimeString(self, time_string): 271 """Copies a POSIX timestamp from a date and time string. 272 273 Args: 274 time_string (str): date and time value formatted as: 275 YYYY-MM-DD hh:mm:ss.######[+-]##:## 276 277 Where # are numeric digits ranging from 0 to 9 and the seconds 278 fraction can be either 3 or 6 digits. The time of day, seconds 279 fraction and time zone offset are optional. The default time zone 280 is UTC. 281 """ 282 date_time_values = self._CopyDateTimeFromString(time_string) 283 284 year = date_time_values.get('year', 0) 285 month = date_time_values.get('month', 0) 286 day_of_month = date_time_values.get('day_of_month', 0) 287 hours = date_time_values.get('hours', 0) 288 minutes = date_time_values.get('minutes', 0) 289 seconds = date_time_values.get('seconds', 0) 290 microseconds = date_time_values.get('microseconds', 0) 291 time_zone_offset = date_time_values.get('time_zone_offset', 0) 292 293 timestamp = self._GetNumberOfSecondsFromElements( 294 year, month, day_of_month, hours, minutes, seconds) 295 timestamp *= definitions.MICROSECONDS_PER_SECOND 296 timestamp += microseconds 297 298 self._timestamp = timestamp 299 self._time_zone_offset = time_zone_offset 300 301 def CopyToDateTimeString(self): 302 """Copies the POSIX timestamp to a date and time string. 303 304 Returns: 305 str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or 306 None if the timestamp is missing. 307 """ 308 if self._timestamp is None: 309 return None 310 311 timestamp, microseconds = divmod( 312 self._timestamp, definitions.MICROSECONDS_PER_SECOND) 313 number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) 314 315 year, month, day_of_month = self._GetDateValuesWithEpoch( 316 number_of_days, self._EPOCH) 317 318 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}'.format( 319 year, month, day_of_month, hours, minutes, seconds, microseconds) 320 321 322class PosixTimeInNanoseconds(interface.DateTimeValues): 323 """POSIX timestamp in nanoseconds. 324 325 Variant of the POSIX timestamp in nanoseconds. 326 327 Attributes: 328 is_local_time (bool): True if the date and time value is in local time. 329 """ 330 331 _EPOCH = PosixTimeEpoch() 332 333 def __init__(self, time_zone_offset=None, timestamp=None): 334 """Initializes a POSIX timestamp in nanoseconds. 335 336 Args: 337 time_zone_offset (Optional[int]): time zone offset in number of minutes 338 from UTC or None if not set. 339 timestamp (Optional[int]): POSIX timestamp in nanoseconds. 340 """ 341 super(PosixTimeInNanoseconds, self).__init__( 342 time_zone_offset=time_zone_offset) 343 self._precision = definitions.PRECISION_1_NANOSECOND 344 self._timestamp = timestamp 345 346 @property 347 def timestamp(self): 348 """int: POSIX timestamp or None if not set.""" 349 return self._timestamp 350 351 def _GetNormalizedTimestamp(self): 352 """Retrieves the normalized timestamp. 353 354 Returns: 355 decimal.Decimal: normalized timestamp, which contains the number of 356 seconds since January 1, 1970 00:00:00 and a fraction of second used 357 for increased precision, or None if the normalized timestamp cannot be 358 determined. 359 """ 360 if self._normalized_timestamp is None: 361 if self._timestamp is not None: 362 self._normalized_timestamp = ( 363 decimal.Decimal(self._timestamp) / 364 definitions.NANOSECONDS_PER_SECOND) 365 366 if self._time_zone_offset: 367 self._normalized_timestamp -= self._time_zone_offset * 60 368 369 return self._normalized_timestamp 370 371 def _CopyFromDateTimeString(self, time_string): 372 """Copies a POSIX timestamp from a date and time string. 373 374 Args: 375 time_string (str): date and time value formatted as: 376 YYYY-MM-DD hh:mm:ss.######[+-]##:## 377 378 Where # are numeric digits ranging from 0 to 9 and the seconds 379 fraction can be either 3 or 6 digits. The time of day, seconds 380 fraction and time zone offset are optional. The default time zone 381 is UTC. 382 """ 383 date_time_values = self._CopyDateTimeFromString(time_string) 384 385 year = date_time_values.get('year', 0) 386 month = date_time_values.get('month', 0) 387 day_of_month = date_time_values.get('day_of_month', 0) 388 hours = date_time_values.get('hours', 0) 389 minutes = date_time_values.get('minutes', 0) 390 seconds = date_time_values.get('seconds', 0) 391 microseconds = date_time_values.get('microseconds', None) 392 time_zone_offset = date_time_values.get('time_zone_offset', 0) 393 394 timestamp = self._GetNumberOfSecondsFromElements( 395 year, month, day_of_month, hours, minutes, seconds) 396 timestamp *= definitions.NANOSECONDS_PER_SECOND 397 398 if microseconds: 399 nanoseconds = microseconds * definitions.MILLISECONDS_PER_SECOND 400 timestamp += nanoseconds 401 402 self._normalized_timestamp = None 403 self._timestamp = timestamp 404 self._time_zone_offset = time_zone_offset 405 406 def CopyFromDateTimeString(self, time_string): 407 """Copies a POSIX timestamp from a date and time string. 408 409 Args: 410 time_string (str): date and time value formatted as: 411 YYYY-MM-DD hh:mm:ss.######[+-]##:## 412 413 Where # are numeric digits ranging from 0 to 9 and the seconds 414 fraction can be either 3 or 6 digits. The time of day, seconds 415 fraction and time zone offset are optional. The default time zone 416 is UTC. 417 """ 418 self._CopyFromDateTimeString(time_string) 419 420 def _CopyToDateTimeString(self): 421 """Copies the POSIX timestamp to a date and time string. 422 423 Returns: 424 str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#########" or 425 None if the timestamp is missing or invalid. 426 """ 427 if self._timestamp is None: 428 return None 429 430 timestamp, nanoseconds = divmod( 431 self._timestamp, definitions.NANOSECONDS_PER_SECOND) 432 number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) 433 434 year, month, day_of_month = self._GetDateValuesWithEpoch( 435 number_of_days, self._EPOCH) 436 437 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:09d}'.format( 438 year, month, day_of_month, hours, minutes, seconds, nanoseconds) 439 440 def CopyToDateTimeString(self): 441 """Copies the POSIX timestamp to a date and time string. 442 443 Returns: 444 str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#########" or 445 None if the timestamp is missing or invalid. 446 """ 447 return self._CopyToDateTimeString() 448 449 450factory.Factory.RegisterDateTimeValues(PosixTime) 451factory.Factory.RegisterDateTimeValues(PosixTimeInMilliseconds) 452factory.Factory.RegisterDateTimeValues(PosixTimeInMicroseconds) 453factory.Factory.RegisterDateTimeValues(PosixTimeInNanoseconds) 454