dfdatetime 20260730__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,289 @@
1
+ """RFC2579 date-time implementation."""
2
+
3
+ import decimal
4
+
5
+ from dfdatetime import definitions
6
+ from dfdatetime import factory
7
+ from dfdatetime import interface
8
+
9
+
10
+ class RFC2579DateTime(interface.DateTimeValues):
11
+ """RFC2579 date-time.
12
+
13
+ The RFC2579 date-time structure is 11 bytes of size and contains:
14
+
15
+ struct {
16
+ uin16_t year,
17
+ uint8_t month,
18
+ uint8_t day_of_month,
19
+ uint8_t hours,
20
+ uint8_t minutes,
21
+ uint8_t seconds,
22
+ uint8_t deciseconds,
23
+ char direction_from_utc,
24
+ uint8_t hours_from_utc,
25
+ uint8_t minutes_from_utc
26
+ }
27
+
28
+ Also see:
29
+ https://datatracker.ietf.org/doc/html/rfc2579
30
+
31
+ Attributes:
32
+ year (int): year, 0 through 65536.
33
+ month (int): month of year, 1 through 12.
34
+ day_of_month (int): day of month, 1 through 31.
35
+ hours (int): hours, 0 through 23.
36
+ minutes (int): minutes, 0 through 59.
37
+ seconds (int): seconds, 0 through 59, where 60 is used to represent
38
+ a leap-second.
39
+ deciseconds (int): deciseconds, 0 through 9.
40
+ """
41
+
42
+ # TODO: make attributes read-only.
43
+
44
+ # pylint: disable=missing-type-doc
45
+
46
+ def __init__(self, precision=None, rfc2579_date_time_tuple=None):
47
+ """Initializes a RFC2579 date-time.
48
+
49
+ Args:
50
+ precision (Optional[str]): precision of the date and time value, which
51
+ should be one of the PRECISION_VALUES in definitions.
52
+ rfc2579_date_time_tuple:
53
+ (Optional[tuple[int, int, int, int, int, int, int, str, int, int]]):
54
+ RFC2579 date-time time, contains year, month, day of month, hours,
55
+ minutes, seconds and deciseconds, and time zone offset in hours and
56
+ minutes from UTC.
57
+
58
+ Raises:
59
+ ValueError: if the system time is invalid.
60
+ """
61
+ super().__init__(precision=precision or definitions.PRECISION_100_MILLISECONDS)
62
+ self._day_of_month = None
63
+ self._deciseconds = None
64
+ self._hours = None
65
+ self._minutes = None
66
+ self._month = None
67
+ self._number_of_seconds = None
68
+ self._seconds = None
69
+ self._year = None
70
+
71
+ if rfc2579_date_time_tuple:
72
+ if len(rfc2579_date_time_tuple) < 10:
73
+ raise ValueError(
74
+ "Invalid RFC2579 date-time tuple 10 elements required."
75
+ )
76
+
77
+ if rfc2579_date_time_tuple[0] < 0 or rfc2579_date_time_tuple[0] > 65536:
78
+ raise ValueError("Year value out of bounds.")
79
+
80
+ if rfc2579_date_time_tuple[1] not in range(1, 13):
81
+ raise ValueError("Month value out of bounds.")
82
+
83
+ days_per_month = self._GetDaysPerMonth(
84
+ rfc2579_date_time_tuple[0], rfc2579_date_time_tuple[1]
85
+ )
86
+ if (
87
+ rfc2579_date_time_tuple[2] < 1
88
+ or rfc2579_date_time_tuple[2] > days_per_month
89
+ ):
90
+ raise ValueError("Day of month value out of bounds.")
91
+
92
+ if rfc2579_date_time_tuple[3] not in range(0, 24):
93
+ raise ValueError("Hours value out of bounds.")
94
+
95
+ if rfc2579_date_time_tuple[4] not in range(0, 60):
96
+ raise ValueError("Minutes value out of bounds.")
97
+
98
+ # TODO: support a leap second?
99
+ if rfc2579_date_time_tuple[5] not in range(0, 60):
100
+ raise ValueError("Seconds value out of bounds.")
101
+
102
+ if rfc2579_date_time_tuple[6] < 0 or rfc2579_date_time_tuple[6] > 9:
103
+ raise ValueError("Deciseconds value out of bounds.")
104
+
105
+ if rfc2579_date_time_tuple[7] not in ("+", "-"):
106
+ raise ValueError("Direction from UTC value out of bounds.")
107
+
108
+ if rfc2579_date_time_tuple[8] not in range(0, 14):
109
+ raise ValueError("Hours from UTC value out of bounds.")
110
+
111
+ if rfc2579_date_time_tuple[9] not in range(0, 60):
112
+ raise ValueError("Minutes from UTC value out of bounds.")
113
+
114
+ time_zone_offset = (
115
+ rfc2579_date_time_tuple[8] * 60
116
+ ) + rfc2579_date_time_tuple[9]
117
+
118
+ if rfc2579_date_time_tuple[7] == "-":
119
+ time_zone_offset = -time_zone_offset
120
+
121
+ self._time_zone_offset = time_zone_offset
122
+
123
+ self._year = rfc2579_date_time_tuple[0]
124
+ self._month = rfc2579_date_time_tuple[1]
125
+ self._day_of_month = rfc2579_date_time_tuple[2]
126
+ self._hours = rfc2579_date_time_tuple[3]
127
+ self._minutes = rfc2579_date_time_tuple[4]
128
+ self._seconds = rfc2579_date_time_tuple[5]
129
+ self._deciseconds = rfc2579_date_time_tuple[6]
130
+
131
+ self._number_of_seconds = self._GetNumberOfSecondsFromElements(
132
+ self._year,
133
+ self._month,
134
+ self._day_of_month,
135
+ self._hours,
136
+ self._minutes,
137
+ self._seconds,
138
+ )
139
+
140
+ def _GetNormalizedTimestamp(self):
141
+ """Retrieves the normalized timestamp.
142
+
143
+ Returns:
144
+ decimal.Decimal: normalized timestamp, which contains the number of
145
+ seconds since January 1, 1970 00:00:00 and a fraction of second used
146
+ for increased precision, or None if the normalized timestamp cannot be
147
+ determined.
148
+ """
149
+ if self._normalized_timestamp is None:
150
+ if self._number_of_seconds is not None:
151
+ self._normalized_timestamp = (
152
+ decimal.Decimal(self._deciseconds)
153
+ / definitions.DECISECONDS_PER_SECOND
154
+ )
155
+ self._normalized_timestamp += decimal.Decimal(self._number_of_seconds)
156
+
157
+ if self._time_zone_offset:
158
+ self._normalized_timestamp -= self._time_zone_offset * 60
159
+
160
+ return self._normalized_timestamp
161
+
162
+ @property
163
+ def deciseconds(self):
164
+ """int: number of deciseconds or None if not set."""
165
+ return self._deciseconds
166
+
167
+ @property
168
+ def day_of_month(self):
169
+ """int: day of month or None if not set."""
170
+ return self._day_of_month
171
+
172
+ @property
173
+ def hours(self):
174
+ """int: number of hours or None if not set."""
175
+ return self._hours
176
+
177
+ @property
178
+ def minutes(self):
179
+ """int: number of minutes or None if not set."""
180
+ return self._minutes
181
+
182
+ @property
183
+ def month(self):
184
+ """int: month or None if not set."""
185
+ return self._month
186
+
187
+ @property
188
+ def seconds(self):
189
+ """int: number of seconds or None if not set."""
190
+ return self._seconds
191
+
192
+ @property
193
+ def year(self):
194
+ """int: year or None if not set."""
195
+ return self._year
196
+
197
+ def CopyFromDateTimeString(self, time_string):
198
+ """Copies a RFC2579 date-time from a date and time string.
199
+
200
+ Args:
201
+ time_string (str): date and time value formatted as:
202
+ YYYY-MM-DD hh:mm:ss.######[+-]##:##
203
+
204
+ Where # are numeric digits ranging from 0 to 9 and the seconds
205
+ fraction can be either 3, 6 or 9 digits. The time of day, seconds
206
+ fraction and time zone offset are optional. The default time zone
207
+ is UTC.
208
+
209
+ Raises:
210
+ ValueError: if the date string is invalid or not supported.
211
+ """
212
+ date_time_values = self._CopyDateTimeFromString(time_string)
213
+
214
+ year = date_time_values.get("year", 0)
215
+ month = date_time_values.get("month", 0)
216
+ day_of_month = date_time_values.get("day_of_month", 0)
217
+ hours = date_time_values.get("hours", 0)
218
+ minutes = date_time_values.get("minutes", 0)
219
+ seconds = date_time_values.get("seconds", 0)
220
+ nanoseconds = date_time_values.get("nanoseconds", 0)
221
+ time_zone_offset = date_time_values.get("time_zone_offset")
222
+
223
+ deciseconds, _ = divmod(nanoseconds, definitions.NANOSECONDS_PER_DECISECOND)
224
+
225
+ if year < 0 or year > 65536:
226
+ raise ValueError(f"Unsupported year value: {year:d}.")
227
+
228
+ self._normalized_timestamp = None
229
+ self._number_of_seconds = self._GetNumberOfSecondsFromElements(
230
+ year, month, day_of_month, hours, minutes, seconds
231
+ )
232
+ self._time_zone_offset = time_zone_offset
233
+
234
+ self._year = year
235
+ self._month = month
236
+ self._day_of_month = day_of_month
237
+ self._hours = hours
238
+ self._minutes = minutes
239
+ self._seconds = seconds
240
+ self._deciseconds = deciseconds
241
+
242
+ def CopyToDateTimeString(self):
243
+ """Copies the RFC2579 date-time to a date and time string.
244
+
245
+ Returns:
246
+ str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#" or
247
+ None if the number of seconds is missing.
248
+ """
249
+ if self._number_of_seconds is None:
250
+ return None
251
+
252
+ return (
253
+ f"{self._year:04d}-{self._month:02d}-{self._day_of_month:02d} "
254
+ f"{self._hours:02d}:{self._minutes:02d}:{self._seconds:02d}"
255
+ f".{self._deciseconds:01d}"
256
+ )
257
+
258
+ def CopyToSerializableDict(self):
259
+ """Copies the date time value to a serializable dictionary.
260
+
261
+ Returns:
262
+ dict[str, object]: serializable dictionary.
263
+ """
264
+ time_zone_hours, time_zone_minutes = divmod(self._time_zone_offset, 60)
265
+ if self._time_zone_offset < 0:
266
+ time_zone_sign = "-"
267
+ time_zone_hours *= -1
268
+ else:
269
+ time_zone_sign = "+"
270
+
271
+ return {
272
+ "__class_name__": type(self).__name__,
273
+ "__type__": "DateTimeValues",
274
+ "rfc2579_date_time_tuple": (
275
+ self._year,
276
+ self._month,
277
+ self._day_of_month,
278
+ self._hours,
279
+ self._minutes,
280
+ self._seconds,
281
+ self._deciseconds,
282
+ time_zone_sign,
283
+ time_zone_hours,
284
+ time_zone_minutes,
285
+ ),
286
+ }
287
+
288
+
289
+ factory.Factory.RegisterDateTimeValues(RFC2579DateTime)
@@ -0,0 +1,329 @@
1
+ """Semantic time implementation."""
2
+
3
+ from dfdatetime import factory
4
+ from dfdatetime import interface
5
+
6
+
7
+ class SemanticTime(interface.DateTimeValues):
8
+ """Semantic time.
9
+
10
+ Semantic time is term to describe date and time values that have specific
11
+ meaning such as: "Never", "Yesterday", "Not set".
12
+
13
+ Attributes:
14
+ is_local_time (bool): True if the date and time value is in local time.
15
+ """
16
+
17
+ # pylint: disable=redundant-returns-doc
18
+
19
+ _SORT_ORDER = 50
20
+
21
+ def __init__(self, string=None):
22
+ """Initializes a semantic time.
23
+
24
+ Args:
25
+ string (str): semantic representation of the time, such as:
26
+ "Never", "Not set".
27
+ """
28
+ super().__init__()
29
+ self._string = string
30
+
31
+ @property
32
+ def string(self):
33
+ """str: semantic representation of the time, such as: "Never"."""
34
+ return self._string
35
+
36
+ def __eq__(self, other):
37
+ """Determines if the date time values are equal to other.
38
+
39
+ Args:
40
+ other (DateTimeValues): date time values to compare against.
41
+
42
+ Returns:
43
+ bool: True if the date time values are equal to other.
44
+ """
45
+ if not isinstance(other, SemanticTime):
46
+ return False
47
+
48
+ return self._SORT_ORDER == other._SORT_ORDER # pylint: disable=protected-access
49
+
50
+ def __ge__(self, other):
51
+ """Determines if the date time values are greater than or equal to other.
52
+
53
+ Args:
54
+ other (DateTimeValues): date time values to compare against.
55
+
56
+ Returns:
57
+ bool: True if the date time values are greater than or equal to other.
58
+
59
+ Raises:
60
+ ValueError: if other is not an instance of DateTimeValues.
61
+ """
62
+ if not isinstance(other, interface.DateTimeValues):
63
+ raise ValueError("Other not an instance of DateTimeValues")
64
+
65
+ if not isinstance(other, SemanticTime):
66
+ return False
67
+
68
+ return self._SORT_ORDER >= other._SORT_ORDER # pylint: disable=protected-access
69
+
70
+ def __gt__(self, other):
71
+ """Determines if the date time values are greater than other.
72
+
73
+ Args:
74
+ other (DateTimeValues): date time values to compare against.
75
+
76
+ Returns:
77
+ bool: True if the date time values are greater than other.
78
+
79
+ Raises:
80
+ ValueError: if other is not an instance of DateTimeValues.
81
+ """
82
+ if not isinstance(other, interface.DateTimeValues):
83
+ raise ValueError("Other not an instance of DateTimeValues")
84
+
85
+ if not isinstance(other, SemanticTime):
86
+ return False
87
+
88
+ return self._SORT_ORDER > other._SORT_ORDER # pylint: disable=protected-access
89
+
90
+ def __le__(self, other):
91
+ """Determines if the date time values are greater than or equal to other.
92
+
93
+ Args:
94
+ other (DateTimeValues): date time values to compare against.
95
+
96
+ Returns:
97
+ bool: True if the date time values are greater than or equal to other.
98
+
99
+ Raises:
100
+ ValueError: if other is not an instance of DateTimeValues.
101
+ """
102
+ if not isinstance(other, interface.DateTimeValues):
103
+ raise ValueError("Other not an instance of DateTimeValues")
104
+
105
+ if not isinstance(other, SemanticTime):
106
+ return True
107
+
108
+ return self._SORT_ORDER <= other._SORT_ORDER # pylint: disable=protected-access
109
+
110
+ def __lt__(self, other):
111
+ """Determines if the date time values are less than other.
112
+
113
+ Args:
114
+ other (DateTimeValues): date time values to compare against.
115
+
116
+ Returns:
117
+ bool: True if the date time values are less than other.
118
+
119
+ Raises:
120
+ ValueError: if other is not an instance of DateTimeValues.
121
+ """
122
+ if not isinstance(other, interface.DateTimeValues):
123
+ raise ValueError("Other not an instance of DateTimeValues")
124
+
125
+ if not isinstance(other, SemanticTime):
126
+ return True
127
+
128
+ return self._SORT_ORDER < other._SORT_ORDER # pylint: disable=protected-access
129
+
130
+ def __ne__(self, other):
131
+ """Determines if the date time values are not equal to other.
132
+
133
+ Args:
134
+ other (DateTimeValues): date time values to compare against.
135
+
136
+ Returns:
137
+ bool: True if the date time values are not equal to other.
138
+ """
139
+ if not isinstance(other, SemanticTime):
140
+ return True
141
+
142
+ return self._SORT_ORDER != other._SORT_ORDER # pylint: disable=protected-access
143
+
144
+ def _GetNormalizedTimestamp(self):
145
+ """Retrieves the normalized timestamp.
146
+
147
+ Returns:
148
+ decimal.Decimal: normalized timestamp, which contains the number of
149
+ seconds since January 1, 1970 00:00:00 and a fraction of second used
150
+ for increased precision, or None if the normalized timestamp cannot be
151
+ determined.
152
+ """
153
+ return None
154
+
155
+ def CopyFromDateTimeString(self, time_string):
156
+ """Copies semantic time from a date and time string.
157
+
158
+ Args:
159
+ time_string (str): semantic representation of the time, such as:
160
+ "Never", "Not set".
161
+
162
+ Raises:
163
+ ValueError: because semantic time cannot be copied from a string.
164
+ """
165
+ self._string = time_string
166
+
167
+ def CopyToDateTimeString(self):
168
+ """Copies the date time value to a date and time string.
169
+
170
+ Returns:
171
+ str: semantic representation of the time, such as: "Never", "Not set".
172
+ """
173
+ return self._string
174
+
175
+ def CopyToDateTimeStringISO8601(self):
176
+ """Copies the date time value to an ISO 8601 date and time string.
177
+
178
+ Returns:
179
+ str: date and time value formatted as an ISO 8601 date and time string,
180
+ which always be None since semantic time cannot be represented in
181
+ ISO 8601.
182
+ """
183
+ return None
184
+
185
+ def CopyToSerializableDict(self):
186
+ """Copies the date time value to a serializable dictionary.
187
+
188
+ Returns:
189
+ dict[str, object]: serializable dictionary.
190
+ """
191
+ return {
192
+ "__class_name__": type(self).__name__,
193
+ "__type__": "DateTimeValues",
194
+ "string": self._string,
195
+ }
196
+
197
+ def GetPlasoTimestamp(self):
198
+ """Retrieves a timestamp that is compatible with plaso.
199
+
200
+ Returns:
201
+ int: a POSIX timestamp in microseconds, which will always be 0.
202
+ """
203
+ return 0
204
+
205
+
206
+ class InvalidTime(SemanticTime):
207
+ """Semantic time that represents invalid."""
208
+
209
+ _SORT_ORDER = 1
210
+
211
+ def __init__(self):
212
+ """Initializes a semantic time that represents invalid."""
213
+ super().__init__(string="Invalid")
214
+
215
+
216
+ class Never(SemanticTime):
217
+ """Semantic time that represents never."""
218
+
219
+ _SORT_ORDER = 99
220
+
221
+ def __init__(self):
222
+ """Initializes a semantic time that represents never."""
223
+ super().__init__(string="Never")
224
+
225
+ def __eq__(self, other):
226
+ """Determines if the date time values are equal to other.
227
+
228
+ Args:
229
+ other (DateTimeValues): date time values to compare against.
230
+
231
+ Returns:
232
+ bool: True if the date time values are equal to other.
233
+ """
234
+ return isinstance(other, Never)
235
+
236
+ def __ge__(self, other):
237
+ """Determines if the date time values are greater than or equal to other.
238
+
239
+ Args:
240
+ other (DateTimeValues): date time values to compare against.
241
+
242
+ Returns:
243
+ bool: True if the date time values are greater than or equal to other.
244
+
245
+ Raises:
246
+ ValueError: if other is not an instance of DateTimeValues.
247
+ """
248
+ if not isinstance(other, interface.DateTimeValues):
249
+ raise ValueError("Other not an instance of DateTimeValues")
250
+
251
+ return True
252
+
253
+ def __gt__(self, other):
254
+ """Determines if the date time values are greater than other.
255
+
256
+ Args:
257
+ other (DateTimeValues): date time values to compare against.
258
+
259
+ Returns:
260
+ bool: True if the date time values are greater than other.
261
+
262
+ Raises:
263
+ ValueError: if other is not an instance of DateTimeValues.
264
+ """
265
+ if not isinstance(other, interface.DateTimeValues):
266
+ raise ValueError("Other not an instance of DateTimeValues")
267
+
268
+ return not isinstance(other, Never)
269
+
270
+ def __le__(self, other):
271
+ """Determines if the date time values are less than or equal to other.
272
+
273
+ Args:
274
+ other (DateTimeValues): date time values to compare against.
275
+
276
+ Returns:
277
+ bool: True if the date time values are greater than or equal to other.
278
+
279
+ Raises:
280
+ ValueError: if other is not an instance of DateTimeValues.
281
+ """
282
+ if not isinstance(other, interface.DateTimeValues):
283
+ raise ValueError("Other not an instance of DateTimeValues")
284
+
285
+ return isinstance(other, Never)
286
+
287
+ def __lt__(self, other):
288
+ """Determines if the date time values are less than other.
289
+
290
+ Args:
291
+ other (DateTimeValues): date time values to compare against.
292
+
293
+ Returns:
294
+ bool: True if the date time values are less than other.
295
+
296
+ Raises:
297
+ ValueError: if other is not an instance of DateTimeValues.
298
+ """
299
+ if not isinstance(other, interface.DateTimeValues):
300
+ raise ValueError("Other not an instance of DateTimeValues")
301
+
302
+ return False
303
+
304
+ def __ne__(self, other):
305
+ """Determines if the date time values are not equal to other.
306
+
307
+ Args:
308
+ other (DateTimeValues): date time values to compare against.
309
+
310
+ Returns:
311
+ bool: True if the date time values are not equal to other.
312
+ """
313
+ return not isinstance(other, Never)
314
+
315
+
316
+ class NotSet(SemanticTime):
317
+ """Semantic time that represents not set."""
318
+
319
+ _SORT_ORDER = 2
320
+
321
+ def __init__(self):
322
+ """Initializes a semantic time that represents not set."""
323
+ super().__init__(string="Not set")
324
+
325
+
326
+ factory.Factory.RegisterDateTimeValues(SemanticTime)
327
+ factory.Factory.RegisterDateTimeValues(InvalidTime)
328
+ factory.Factory.RegisterDateTimeValues(Never)
329
+ factory.Factory.RegisterDateTimeValues(NotSet)