flow.record 3.11.dev5__tar.gz → 3.12.dev1__tar.gz
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.
- {flow.record-3.11.dev5/flow.record.egg-info → flow.record-3.12.dev1}/PKG-INFO +1 -1
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/elastic.py +1 -1
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/base.py +3 -2
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/fieldtypes/__init__.py +62 -32
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/jsonpacker.py +1 -1
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/packer.py +9 -6
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/stream.py +2 -2
- flow.record-3.12.dev1/flow/record/version.py +4 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1/flow.record.egg-info}/PKG-INFO +1 -1
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow.record.egg-info/requires.txt +6 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/pyproject.toml +2 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/_utils.py +2 -2
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_fieldtypes.py +78 -24
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_json_packer.py +2 -2
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_multi_timestamp.py +14 -12
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_packer.py +5 -3
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_rdump.py +78 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_record_adapter.py +1 -1
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_regression.py +2 -2
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_selector.py +2 -2
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tox.ini +1 -1
- flow.record-3.11.dev5/flow/record/version.py +0 -4
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/COPYRIGHT +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/LICENSE +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/MANIFEST.in +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/README.md +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/examples/filesystem.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/examples/passivedns.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/examples/records.json +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/examples/tcpconn.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/__init__.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/__init__.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/archive.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/avro.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/broker.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/csvfile.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/jsonfile.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/line.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/mongo.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/split.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/splunk.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/stream.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/text.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/adapter/xlsx.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/exceptions.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/fieldtypes/credential.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/fieldtypes/net/__init__.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/fieldtypes/net/ip.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/fieldtypes/net/ipv4.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/fieldtypes/net/tcp.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/fieldtypes/net/udp.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/selector.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/tools/__init__.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/tools/geoip.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/tools/rdump.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/utils.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow/record/whitelist.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow.record.egg-info/SOURCES.txt +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow.record.egg-info/dependency_links.txt +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow.record.egg-info/entry_points.txt +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/flow.record.egg-info/top_level.txt +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/setup.cfg +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/__init__.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/docs/Makefile +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/docs/conf.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/docs/index.rst +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/selector_explain_example.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/standalone_test.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_avro_adapter.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_compiled_selector.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_deprecations.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_fieldtype_ip.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_json_record_adapter.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_record.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_record_descriptor.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/test_splunk_adapter.py +0 -0
- {flow.record-3.11.dev5 → flow.record-3.12.dev1}/tests/utils_inspect.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flow.record
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.12.dev1
|
|
4
4
|
Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Affero General Public License v3
|
|
@@ -12,7 +12,7 @@ import os
|
|
|
12
12
|
import re
|
|
13
13
|
import sys
|
|
14
14
|
import warnings
|
|
15
|
-
from datetime import datetime
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
16
|
from itertools import zip_longest
|
|
17
17
|
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple
|
|
18
18
|
from urllib.parse import parse_qsl, urlparse
|
|
@@ -44,6 +44,7 @@ from .utils import to_native_str, to_str
|
|
|
44
44
|
from .whitelist import WHITELIST, WHITELIST_TREE
|
|
45
45
|
|
|
46
46
|
log = logging.getLogger(__package__)
|
|
47
|
+
_utcnow = functools.partial(datetime.now, timezone.utc)
|
|
47
48
|
|
|
48
49
|
RECORD_VERSION = 1
|
|
49
50
|
RESERVED_FIELDS = OrderedDict(
|
|
@@ -422,7 +423,7 @@ def _generate_record_class(name: str, fields: Tuple[Tuple[str, str]]) -> type:
|
|
|
422
423
|
_globals = {
|
|
423
424
|
"Record": Record,
|
|
424
425
|
"RECORD_VERSION": RECORD_VERSION,
|
|
425
|
-
"_utcnow":
|
|
426
|
+
"_utcnow": _utcnow,
|
|
426
427
|
"_zip_longest": zip_longest,
|
|
427
428
|
}
|
|
428
429
|
for field in all_fields.values():
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import binascii
|
|
2
4
|
import math
|
|
3
5
|
import os
|
|
4
6
|
import pathlib
|
|
5
7
|
import re
|
|
8
|
+
import sys
|
|
9
|
+
import warnings
|
|
6
10
|
from binascii import a2b_hex, b2a_hex
|
|
7
11
|
from datetime import datetime as _dt
|
|
8
12
|
from datetime import timezone
|
|
9
13
|
from posixpath import basename, dirname
|
|
10
|
-
from typing import Any, Tuple
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import urlparse
|
|
14
|
-
except ImportError:
|
|
15
|
-
import urllib.parse as urlparse
|
|
16
|
-
|
|
17
|
-
import warnings
|
|
14
|
+
from typing import Any, Optional, Tuple
|
|
15
|
+
from urllib.parse import urlparse
|
|
16
|
+
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
|
18
17
|
|
|
19
18
|
from flow.record.base import FieldType
|
|
20
19
|
|
|
@@ -22,6 +21,12 @@ RE_NORMALIZE_PATH = re.compile(r"[\\/]+")
|
|
|
22
21
|
RE_STRIP_NANOSECS = re.compile(r"(\.\d{6})\d+")
|
|
23
22
|
NATIVE_UNICODE = isinstance("", str)
|
|
24
23
|
|
|
24
|
+
UTC = timezone.utc
|
|
25
|
+
ISO_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
|
|
26
|
+
ISO_FORMAT_WITH_MS = "%Y-%m-%dT%H:%M:%S.%f%z"
|
|
27
|
+
|
|
28
|
+
PY_311 = sys.version_info >= (3, 11, 0)
|
|
29
|
+
|
|
25
30
|
PATH_POSIX = 0
|
|
26
31
|
PATH_WINDOWS = 1
|
|
27
32
|
|
|
@@ -32,6 +37,31 @@ float_type = float
|
|
|
32
37
|
path_type = pathlib.PurePath
|
|
33
38
|
|
|
34
39
|
|
|
40
|
+
def flow_record_tz(*, default_tz: str = "UTC") -> Optional[ZoneInfo | UTC]:
|
|
41
|
+
"""Return a ``ZoneInfo`` object based on the ``FLOW_RECORD_TZ`` environment variable.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
default_tz: Default timezone if ``FLOW_RECORD_TZ`` is not set (default: UTC).
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
None if ``FLOW_RECORD_TZ=NONE`` otherwise ``ZoneInfo(FLOW_RECORD_TZ)`` or ``UTC`` if ZoneInfo is not found.
|
|
48
|
+
"""
|
|
49
|
+
tz = os.environ.get("FLOW_RECORD_TZ", default_tz)
|
|
50
|
+
if tz.upper() == "NONE":
|
|
51
|
+
return None
|
|
52
|
+
try:
|
|
53
|
+
return ZoneInfo(tz)
|
|
54
|
+
except ZoneInfoNotFoundError as exc:
|
|
55
|
+
warnings.warn(f"{exc!r}, falling back to timezone.utc")
|
|
56
|
+
return UTC
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# The environment variable ``FLOW_RECORD_TZ`` affects the display of datetime fields.
|
|
60
|
+
#
|
|
61
|
+
# The timezone to use when displaying datetime fields. By default this is UTC.
|
|
62
|
+
DISPLAY_TZINFO = flow_record_tz(default_tz="UTC")
|
|
63
|
+
|
|
64
|
+
|
|
35
65
|
def defang(value: str) -> str:
|
|
36
66
|
"""Defangs the value to make URLs or ip addresses unclickable"""
|
|
37
67
|
value = re.sub("^http://", "hxxp://", value, flags=re.IGNORECASE)
|
|
@@ -238,24 +268,24 @@ class datetime(_dt, FieldType):
|
|
|
238
268
|
# String constructor is used for example in JsonRecordAdapter
|
|
239
269
|
# Note: ISO 8601 is fully implemented in fromisoformat() from Python 3.11 and onwards.
|
|
240
270
|
# Until then, we need to manually detect timezone info and handle it.
|
|
241
|
-
if any(z in arg[19:] for z in ["Z", "+", "-"]):
|
|
242
|
-
if "." in arg[19:]
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return cls.strptime(arg, "%Y-%m-%dT%H:%M:%S%z")
|
|
271
|
+
if not PY_311 and any(z in arg[19:] for z in ["Z", "+", "-"]):
|
|
272
|
+
spec = ISO_FORMAT_WITH_MS if "." in arg[19:] else ISO_FORMAT
|
|
273
|
+
try:
|
|
274
|
+
obj = cls.strptime(arg, spec)
|
|
275
|
+
except ValueError:
|
|
276
|
+
# Sometimes nanoseconds need to be stripped
|
|
277
|
+
obj = cls.strptime(re.sub(RE_STRIP_NANOSECS, "\\1", arg), spec)
|
|
249
278
|
else:
|
|
250
279
|
try:
|
|
251
|
-
|
|
280
|
+
obj = cls.fromisoformat(arg)
|
|
252
281
|
except ValueError:
|
|
253
282
|
# Sometimes nanoseconds need to be stripped
|
|
254
|
-
|
|
283
|
+
obj = cls.fromisoformat(re.sub(RE_STRIP_NANOSECS, "\\1", arg))
|
|
255
284
|
elif isinstance(arg, (int, float_type)):
|
|
256
|
-
|
|
285
|
+
obj = cls.fromtimestamp(arg, UTC)
|
|
257
286
|
elif isinstance(arg, (_dt,)):
|
|
258
|
-
|
|
287
|
+
tzinfo = arg.tzinfo or UTC
|
|
288
|
+
obj = _dt.__new__(
|
|
259
289
|
cls,
|
|
260
290
|
arg.year,
|
|
261
291
|
arg.month,
|
|
@@ -264,24 +294,24 @@ class datetime(_dt, FieldType):
|
|
|
264
294
|
arg.minute,
|
|
265
295
|
arg.second,
|
|
266
296
|
arg.microsecond,
|
|
267
|
-
|
|
297
|
+
tzinfo,
|
|
268
298
|
)
|
|
299
|
+
else:
|
|
300
|
+
obj = _dt.__new__(cls, *args, **kwargs)
|
|
269
301
|
|
|
270
|
-
return
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
# naive datetimes are treated as UTC in flow.record instead of local time
|
|
275
|
-
ts1 = self.timestamp() if self.tzinfo else self.replace(tzinfo=timezone.utc).timestamp()
|
|
276
|
-
ts2 = other.timestamp() if other.tzinfo else other.replace(tzinfo=timezone.utc).timestamp()
|
|
277
|
-
return ts1 == ts2
|
|
302
|
+
# Ensure we always return a timezone aware datetime. Treat naive datetimes as UTC
|
|
303
|
+
if obj.tzinfo is None:
|
|
304
|
+
obj = obj.replace(tzinfo=UTC)
|
|
305
|
+
return obj
|
|
278
306
|
|
|
279
307
|
def _pack(self):
|
|
280
308
|
return self
|
|
281
309
|
|
|
310
|
+
def __str__(self):
|
|
311
|
+
return self.astimezone(DISPLAY_TZINFO).isoformat(" ") if DISPLAY_TZINFO else self.isoformat(" ")
|
|
312
|
+
|
|
282
313
|
def __repr__(self):
|
|
283
|
-
|
|
284
|
-
return result
|
|
314
|
+
return str(self)
|
|
285
315
|
|
|
286
316
|
def __hash__(self):
|
|
287
317
|
return _dt.__hash__(self)
|
|
@@ -462,7 +492,7 @@ class digest(FieldType):
|
|
|
462
492
|
|
|
463
493
|
class uri(string, FieldType):
|
|
464
494
|
def __init__(self, value):
|
|
465
|
-
self._parsed = urlparse
|
|
495
|
+
self._parsed = urlparse(value)
|
|
466
496
|
|
|
467
497
|
@staticmethod
|
|
468
498
|
def normalize(path):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import datetime
|
|
2
1
|
import functools
|
|
3
2
|
import warnings
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
4
|
|
|
5
5
|
import msgpack
|
|
6
6
|
|
|
@@ -29,6 +29,8 @@ RECORD_PACK_TYPE_DATETIME = 0x10
|
|
|
29
29
|
RECORD_PACK_TYPE_VARINT = 0x11
|
|
30
30
|
RECORD_PACK_TYPE_GROUPEDRECORD = 0x12
|
|
31
31
|
|
|
32
|
+
UTC = timezone.utc
|
|
33
|
+
|
|
32
34
|
|
|
33
35
|
def identifier_to_str(identifier):
|
|
34
36
|
if isinstance(identifier, tuple) and len(identifier) == 2:
|
|
@@ -61,9 +63,11 @@ class RecordPacker:
|
|
|
61
63
|
def pack_obj(self, obj, unversioned=False):
|
|
62
64
|
packed = None
|
|
63
65
|
|
|
64
|
-
if isinstance(obj, datetime
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
if isinstance(obj, datetime):
|
|
67
|
+
if obj.tzinfo is None or obj.tzinfo == UTC:
|
|
68
|
+
packed = (RECORD_PACK_TYPE_DATETIME, (*obj.timetuple()[:6], obj.microsecond))
|
|
69
|
+
else:
|
|
70
|
+
packed = (RECORD_PACK_TYPE_DATETIME, (obj.isoformat(),))
|
|
67
71
|
|
|
68
72
|
elif isinstance(obj, int):
|
|
69
73
|
neg = obj < 0
|
|
@@ -102,8 +106,7 @@ class RecordPacker:
|
|
|
102
106
|
subtype, value = self.unpack(data)
|
|
103
107
|
|
|
104
108
|
if subtype == RECORD_PACK_TYPE_DATETIME:
|
|
105
|
-
|
|
106
|
-
return dt
|
|
109
|
+
return fieldtypes.datetime(*value)
|
|
107
110
|
|
|
108
111
|
if subtype == RECORD_PACK_TYPE_VARINT:
|
|
109
112
|
neg, h = value
|
|
@@ -191,7 +191,7 @@ class PathTemplateWriter:
|
|
|
191
191
|
|
|
192
192
|
def rotate_existing_file(self, path):
|
|
193
193
|
if os.path.exists(path):
|
|
194
|
-
now = datetime.datetime.
|
|
194
|
+
now = datetime.datetime.now(datetime.timezone.utc)
|
|
195
195
|
src = os.path.realpath(path)
|
|
196
196
|
|
|
197
197
|
src_dir = os.path.dirname(src)
|
|
@@ -226,7 +226,7 @@ class PathTemplateWriter:
|
|
|
226
226
|
return self.writer
|
|
227
227
|
|
|
228
228
|
def write(self, record):
|
|
229
|
-
ts = record._generated or datetime.datetime.
|
|
229
|
+
ts = record._generated or datetime.datetime.now(datetime.timezone.utc)
|
|
230
230
|
path = self.path_template.format(name=self.name, record=record, ts=ts)
|
|
231
231
|
rs = self.record_stream_for_path(path)
|
|
232
232
|
rs.write(record)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flow.record
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.12.dev1
|
|
4
4
|
Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Affero General Public License v3
|
|
@@ -19,7 +19,7 @@ def generate_records(count=100):
|
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
for i in range(count):
|
|
22
|
-
embedded = TestRecordEmbedded(datetime.datetime.
|
|
22
|
+
embedded = TestRecordEmbedded(datetime.datetime.now(datetime.timezone.utc))
|
|
23
23
|
yield TestRecord(number=i, record=embedded)
|
|
24
24
|
|
|
25
25
|
|
|
@@ -33,4 +33,4 @@ def generate_plain_records(count=100):
|
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
for i in range(count):
|
|
36
|
-
yield TestRecord(number=i, dt=datetime.datetime.
|
|
36
|
+
yield TestRecord(number=i, dt=datetime.datetime.now(datetime.timezone.utc))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
-
import datetime
|
|
4
3
|
import hashlib
|
|
5
4
|
import os
|
|
6
5
|
import pathlib
|
|
6
|
+
from datetime import datetime, timedelta, timezone
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
@@ -18,6 +18,8 @@ from flow.record.fieldtypes import (
|
|
|
18
18
|
from flow.record.fieldtypes import datetime as dt
|
|
19
19
|
from flow.record.fieldtypes import fieldtype_for_value, net, uri
|
|
20
20
|
|
|
21
|
+
UTC = timezone.utc
|
|
22
|
+
|
|
21
23
|
INT64_MAX = (1 << 63) - 1
|
|
22
24
|
INT32_MAX = (1 << 31) - 1
|
|
23
25
|
INT16_MAX = (1 << 15) - 1
|
|
@@ -398,29 +400,29 @@ def test_datetime():
|
|
|
398
400
|
],
|
|
399
401
|
)
|
|
400
402
|
|
|
401
|
-
now = datetime.
|
|
403
|
+
now = datetime.now(UTC)
|
|
402
404
|
r = TestRecord(now)
|
|
403
405
|
assert r.ts == now
|
|
404
406
|
|
|
405
407
|
r = TestRecord("2018-03-22T15:15:23")
|
|
406
|
-
assert r.ts == datetime
|
|
408
|
+
assert r.ts == datetime(2018, 3, 22, 15, 15, 23, tzinfo=UTC)
|
|
407
409
|
|
|
408
410
|
r = TestRecord("2018-03-22T15:15:23.000000")
|
|
409
|
-
assert r.ts == datetime
|
|
411
|
+
assert r.ts == datetime(2018, 3, 22, 15, 15, 23, tzinfo=UTC)
|
|
410
412
|
|
|
411
413
|
r = TestRecord("2018-03-22T15:15:23.123456")
|
|
412
|
-
assert r.ts == datetime
|
|
414
|
+
assert r.ts == datetime(2018, 3, 22, 15, 15, 23, 123456, tzinfo=UTC)
|
|
413
415
|
|
|
414
|
-
dt = datetime
|
|
416
|
+
dt = datetime(2018, 3, 22, 15, 15, 23, 123456, tzinfo=UTC)
|
|
415
417
|
dt_str = dt.isoformat()
|
|
416
418
|
r = TestRecord(dt_str)
|
|
417
419
|
assert r.ts == dt
|
|
418
420
|
|
|
419
421
|
r = TestRecord(1521731723)
|
|
420
|
-
assert r.ts == datetime
|
|
422
|
+
assert r.ts == datetime(2018, 3, 22, 15, 15, 23, tzinfo=UTC)
|
|
421
423
|
|
|
422
424
|
r = TestRecord(1521731723.123456)
|
|
423
|
-
assert r.ts == datetime
|
|
425
|
+
assert r.ts == datetime(2018, 3, 22, 15, 15, 23, 123456, tzinfo=UTC)
|
|
424
426
|
|
|
425
427
|
r = TestRecord("2018-03-22T15:15:23.123456")
|
|
426
428
|
test = {r.ts: "Success"}
|
|
@@ -430,18 +432,18 @@ def test_datetime():
|
|
|
430
432
|
@pytest.mark.parametrize(
|
|
431
433
|
"value,expected_dt",
|
|
432
434
|
[
|
|
433
|
-
("2023-12-31T13:37:01.123456Z", datetime
|
|
434
|
-
("2023-01-10T16:12:01+00:00", datetime
|
|
435
|
-
("2023-01-10T16:12:01", datetime
|
|
436
|
-
("2023-01-10T16:12:01Z", datetime
|
|
437
|
-
("2022-12-01T13:00:23.499460Z", datetime
|
|
438
|
-
("2019-09-26T07:58:30.996+0200", datetime
|
|
439
|
-
("2011-11-04T00:05:23+04:00", datetime
|
|
440
|
-
("2023-01-01T12:00:00+01:00", datetime
|
|
441
|
-
("2006-11-10T14:29:55.5851926", datetime
|
|
442
|
-
("2006-11-10T14:29:55.585192699999999", datetime
|
|
443
|
-
(datetime
|
|
444
|
-
(0, datetime
|
|
435
|
+
("2023-12-31T13:37:01.123456Z", datetime(2023, 12, 31, 13, 37, 1, 123456, tzinfo=UTC)),
|
|
436
|
+
("2023-01-10T16:12:01+00:00", datetime(2023, 1, 10, 16, 12, 1, tzinfo=UTC)),
|
|
437
|
+
("2023-01-10T16:12:01", datetime(2023, 1, 10, 16, 12, 1, tzinfo=UTC)),
|
|
438
|
+
("2023-01-10T16:12:01Z", datetime(2023, 1, 10, 16, 12, 1, tzinfo=UTC)),
|
|
439
|
+
("2022-12-01T13:00:23.499460Z", datetime(2022, 12, 1, 13, 0, 23, 499460, tzinfo=UTC)),
|
|
440
|
+
("2019-09-26T07:58:30.996+0200", datetime(2019, 9, 26, 5, 58, 30, 996000, tzinfo=UTC)),
|
|
441
|
+
("2011-11-04T00:05:23+04:00", datetime(2011, 11, 3, 20, 5, 23, tzinfo=UTC)),
|
|
442
|
+
("2023-01-01T12:00:00+01:00", datetime(2023, 1, 1, 11, 0, 0, tzinfo=UTC)),
|
|
443
|
+
("2006-11-10T14:29:55.5851926", datetime(2006, 11, 10, 14, 29, 55, 585192, tzinfo=UTC)),
|
|
444
|
+
("2006-11-10T14:29:55.585192699999999", datetime(2006, 11, 10, 14, 29, 55, 585192, tzinfo=UTC)),
|
|
445
|
+
(datetime(2023, 1, 1, tzinfo=UTC), datetime(2023, 1, 1, tzinfo=UTC)),
|
|
446
|
+
(0, datetime(1970, 1, 1, 0, 0, tzinfo=UTC)),
|
|
445
447
|
],
|
|
446
448
|
)
|
|
447
449
|
def test_datetime_formats(tmp_path, value, expected_dt):
|
|
@@ -740,7 +742,7 @@ def test_fieldtype_for_value():
|
|
|
740
742
|
assert fieldtype_for_value(1.337) == "float"
|
|
741
743
|
assert fieldtype_for_value(b"\r\n") == "bytes"
|
|
742
744
|
assert fieldtype_for_value("hello world") == "string"
|
|
743
|
-
assert fieldtype_for_value(datetime.
|
|
745
|
+
assert fieldtype_for_value(datetime.now()) == "datetime"
|
|
744
746
|
assert fieldtype_for_value([1, 2, 3, 4, 5]) == "string"
|
|
745
747
|
assert fieldtype_for_value([1, 2, 3, 4, 5], None) is None
|
|
746
748
|
assert fieldtype_for_value(object(), None) is None
|
|
@@ -775,7 +777,7 @@ def test_dynamic():
|
|
|
775
777
|
assert r.value == [1, 2, 3]
|
|
776
778
|
assert isinstance(r.value, flow.record.fieldtypes.stringlist)
|
|
777
779
|
|
|
778
|
-
now = datetime.
|
|
780
|
+
now = datetime.now(UTC)
|
|
779
781
|
r = TestRecord(now)
|
|
780
782
|
assert r.value == now
|
|
781
783
|
assert isinstance(r.value, flow.record.fieldtypes.datetime)
|
|
@@ -899,11 +901,63 @@ def test_datetime_handle_nanoseconds_without_timezone():
|
|
|
899
901
|
d2 = dt("2006-11-10T14:29:55")
|
|
900
902
|
assert isinstance(d1, dt)
|
|
901
903
|
assert isinstance(d2, dt)
|
|
902
|
-
assert d1 == datetime
|
|
904
|
+
assert d1 == datetime(2006, 11, 10, 14, 29, 55, 585192, tzinfo=UTC)
|
|
903
905
|
assert d1.microsecond == 585192
|
|
904
|
-
assert d2 == datetime
|
|
906
|
+
assert d2 == datetime(2006, 11, 10, 14, 29, 55, tzinfo=UTC)
|
|
905
907
|
assert d2.microsecond == 0
|
|
906
908
|
|
|
907
909
|
|
|
910
|
+
@pytest.mark.parametrize(
|
|
911
|
+
"record_filename",
|
|
912
|
+
[
|
|
913
|
+
"out.records.gz",
|
|
914
|
+
"out.records",
|
|
915
|
+
"out.json",
|
|
916
|
+
"out.jsonl",
|
|
917
|
+
],
|
|
918
|
+
)
|
|
919
|
+
def test_datetime_timezone_aware(tmp_path, record_filename):
|
|
920
|
+
TestRecord = RecordDescriptor(
|
|
921
|
+
"test/tz",
|
|
922
|
+
[
|
|
923
|
+
("datetime", "ts"),
|
|
924
|
+
],
|
|
925
|
+
)
|
|
926
|
+
tz = timezone(timedelta(hours=1))
|
|
927
|
+
stamp = datetime.now(tz)
|
|
928
|
+
|
|
929
|
+
with RecordWriter(tmp_path / record_filename) as writer:
|
|
930
|
+
record = TestRecord(stamp)
|
|
931
|
+
writer.write(record)
|
|
932
|
+
assert record.ts == stamp
|
|
933
|
+
assert record.ts.utcoffset() == timedelta(hours=1)
|
|
934
|
+
assert record._generated.tzinfo == UTC
|
|
935
|
+
|
|
936
|
+
with RecordReader(tmp_path / record_filename) as reader:
|
|
937
|
+
for record in reader:
|
|
938
|
+
assert record.ts == stamp
|
|
939
|
+
assert record.ts.utcoffset() == timedelta(hours=1)
|
|
940
|
+
assert record._generated.tzinfo == UTC
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def test_datetime_comparisions():
|
|
944
|
+
with pytest.raises(TypeError, match=".* compare .*naive"):
|
|
945
|
+
assert dt("2023-01-01") > datetime(2022, 1, 1)
|
|
946
|
+
|
|
947
|
+
with pytest.raises(TypeError, match=".* compare .*naive"):
|
|
948
|
+
assert datetime(2022, 1, 1) < dt("2023-01-01")
|
|
949
|
+
|
|
950
|
+
assert dt("2023-01-01") > datetime(2022, 1, 1, tzinfo=UTC)
|
|
951
|
+
assert dt("2023-01-01") == datetime(2023, 1, 1, tzinfo=UTC)
|
|
952
|
+
assert dt("2023-01-01") == datetime(2023, 1, 1, tzinfo=UTC)
|
|
953
|
+
assert dt("2023-01-01T13:36") <= datetime(2023, 1, 1, 13, 37, tzinfo=UTC)
|
|
954
|
+
assert dt("2023-01-01T13:37") <= datetime(2023, 1, 1, 13, 37, tzinfo=UTC)
|
|
955
|
+
assert dt("2023-01-01T13:37") >= datetime(2023, 1, 1, 13, 36, tzinfo=UTC)
|
|
956
|
+
assert dt("2023-01-01T13:37") >= datetime(2023, 1, 1, 13, 37, tzinfo=UTC)
|
|
957
|
+
assert dt("2023-01-01T13:36") < datetime(2023, 1, 1, 13, 37, tzinfo=UTC)
|
|
958
|
+
assert dt("2023-01-01T13:37") > datetime(2023, 1, 1, 13, 36, tzinfo=UTC)
|
|
959
|
+
assert dt("2023-01-02") != datetime(2023, 3, 4, tzinfo=UTC)
|
|
960
|
+
|
|
961
|
+
|
|
908
962
|
if __name__ == "__main__":
|
|
909
963
|
__import__("standalone_test").main(globals())
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from datetime import datetime
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ from flow.record.exceptions import RecordDescriptorNotFound
|
|
|
9
9
|
|
|
10
10
|
def test_record_in_record():
|
|
11
11
|
packer = JsonRecordPacker()
|
|
12
|
-
dt = datetime.
|
|
12
|
+
dt = datetime.now(timezone.utc)
|
|
13
13
|
|
|
14
14
|
RecordA = RecordDescriptor(
|
|
15
15
|
"test/record_a",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import datetime
|
|
1
|
+
from datetime import datetime, timedelta, timezone
|
|
2
2
|
|
|
3
3
|
from flow.record import RecordDescriptor, iter_timestamped_records
|
|
4
4
|
from flow.record.base import merge_record_descriptors
|
|
5
5
|
|
|
6
|
+
UTC = timezone.utc
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
def test_multi_timestamp():
|
|
8
10
|
TestRecord = RecordDescriptor(
|
|
@@ -15,22 +17,22 @@ def test_multi_timestamp():
|
|
|
15
17
|
)
|
|
16
18
|
|
|
17
19
|
test_record = TestRecord(
|
|
18
|
-
ctime=datetime
|
|
19
|
-
atime=datetime
|
|
20
|
+
ctime=datetime(2020, 1, 1, 1, 1, 1),
|
|
21
|
+
atime=datetime(2022, 11, 22, 13, 37, 37),
|
|
20
22
|
data="test",
|
|
21
23
|
)
|
|
22
24
|
|
|
23
25
|
ts_records = list(iter_timestamped_records(test_record))
|
|
24
26
|
|
|
25
27
|
for rec in ts_records:
|
|
26
|
-
assert rec.ctime == datetime
|
|
27
|
-
assert rec.atime == datetime
|
|
28
|
+
assert rec.ctime == datetime(2020, 1, 1, 1, 1, 1, tzinfo=UTC)
|
|
29
|
+
assert rec.atime == datetime(2022, 11, 22, 13, 37, 37, tzinfo=UTC)
|
|
28
30
|
assert rec.data == "test"
|
|
29
31
|
|
|
30
|
-
assert ts_records[0].ts == datetime
|
|
32
|
+
assert ts_records[0].ts == datetime(2020, 1, 1, 1, 1, 1, tzinfo=UTC)
|
|
31
33
|
assert ts_records[0].ts_description == "ctime"
|
|
32
34
|
|
|
33
|
-
assert ts_records[1].ts == datetime
|
|
35
|
+
assert ts_records[1].ts == datetime(2022, 11, 22, 13, 37, 37, tzinfo=UTC)
|
|
34
36
|
assert ts_records[1].ts_description == "atime"
|
|
35
37
|
|
|
36
38
|
|
|
@@ -58,7 +60,7 @@ def test_multi_timestamp_single_datetime():
|
|
|
58
60
|
)
|
|
59
61
|
|
|
60
62
|
test_record = TestRecord(
|
|
61
|
-
ctime=datetime
|
|
63
|
+
ctime=datetime(2020, 1, 1, 1, 1, 1),
|
|
62
64
|
data="test",
|
|
63
65
|
)
|
|
64
66
|
ts_records = list(iter_timestamped_records(test_record))
|
|
@@ -77,7 +79,7 @@ def test_multi_timestamp_ts_fieldname():
|
|
|
77
79
|
)
|
|
78
80
|
|
|
79
81
|
test_record = TestRecord(
|
|
80
|
-
ts=datetime
|
|
82
|
+
ts=datetime(2020, 1, 1, 1, 1, 1),
|
|
81
83
|
data="test",
|
|
82
84
|
)
|
|
83
85
|
ts_records = list(iter_timestamped_records(test_record))
|
|
@@ -95,7 +97,7 @@ def test_multi_timestamp_timezone():
|
|
|
95
97
|
],
|
|
96
98
|
)
|
|
97
99
|
|
|
98
|
-
correct_ts = datetime
|
|
100
|
+
correct_ts = datetime(2023, 12, 31, 13, 37, 1, 123456, tzinfo=UTC)
|
|
99
101
|
|
|
100
102
|
ts_notations = [
|
|
101
103
|
correct_ts,
|
|
@@ -127,8 +129,8 @@ def test_multi_timestamp_descriptor_cache():
|
|
|
127
129
|
merge_record_descriptors.cache_clear()
|
|
128
130
|
for i in range(10):
|
|
129
131
|
test_record = TestRecord(
|
|
130
|
-
ctime=datetime.
|
|
131
|
-
atime=datetime.
|
|
132
|
+
ctime=datetime.now(UTC) + timedelta(hours=69),
|
|
133
|
+
atime=datetime.now(UTC) + timedelta(hours=420),
|
|
132
134
|
count=i,
|
|
133
135
|
data=f"test {i}",
|
|
134
136
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import datetime
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
@@ -7,6 +7,8 @@ from flow.record.exceptions import RecordDescriptorNotFound
|
|
|
7
7
|
from flow.record.fieldtypes import uri
|
|
8
8
|
from flow.record.packer import RECORD_PACK_EXT_TYPE
|
|
9
9
|
|
|
10
|
+
UTC = timezone.utc
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
def test_uri_packing():
|
|
12
14
|
packer = RecordPacker()
|
|
@@ -151,7 +153,7 @@ def test_dynamic_packer():
|
|
|
151
153
|
assert r.value == [1, True, b"b", "u"]
|
|
152
154
|
assert isinstance(r.value, fieldtypes.stringlist)
|
|
153
155
|
|
|
154
|
-
now = datetime.
|
|
156
|
+
now = datetime.now(UTC)
|
|
155
157
|
t = TestRecord(now)
|
|
156
158
|
data = packer.pack(t)
|
|
157
159
|
r = packer.unpack(data)
|
|
@@ -195,7 +197,7 @@ def test_pack_digest():
|
|
|
195
197
|
|
|
196
198
|
def test_record_in_record():
|
|
197
199
|
packer = RecordPacker()
|
|
198
|
-
dt = datetime.
|
|
200
|
+
dt = datetime.now(UTC)
|
|
199
201
|
|
|
200
202
|
RecordA = RecordDescriptor(
|
|
201
203
|
"test/record_a",
|
|
@@ -4,10 +4,14 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import platform
|
|
6
6
|
import subprocess
|
|
7
|
+
from datetime import timezone
|
|
8
|
+
from unittest import mock
|
|
7
9
|
|
|
8
10
|
import pytest
|
|
9
11
|
|
|
12
|
+
import flow.record.fieldtypes
|
|
10
13
|
from flow.record import RecordDescriptor, RecordReader, RecordWriter
|
|
14
|
+
from flow.record.fieldtypes import flow_record_tz
|
|
11
15
|
from flow.record.tools import rdump
|
|
12
16
|
|
|
13
17
|
|
|
@@ -509,3 +513,77 @@ def test_rdump_count_and_skip(tmp_path, capsysbinary, total_records, count, skip
|
|
|
509
513
|
with RecordReader(subset_path) as reader:
|
|
510
514
|
numbers = [rec.number for rec in reader]
|
|
511
515
|
assert numbers == expected_numbers
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
@pytest.mark.parametrize(
|
|
519
|
+
"date_str,tz,expected_date_str",
|
|
520
|
+
[
|
|
521
|
+
("2023-08-02T22:28:06.12345+01:00", None, "2023-08-02 21:28:06.123450+00:00"),
|
|
522
|
+
("2023-08-02T22:28:06.12345+01:00", "NONE", "2023-08-02 22:28:06.123450+01:00"),
|
|
523
|
+
("2023-08-02T22:28:06.12345-08:00", "NONE", "2023-08-02 22:28:06.123450-08:00"),
|
|
524
|
+
("2023-08-02T20:51:32.123456+00:00", "Europe/Amsterdam", "2023-08-02 22:51:32.123456+02:00"),
|
|
525
|
+
("2023-08-02T20:51:32.123456+00:00", "America/New_York", "2023-08-02 16:51:32.123456-04:00"),
|
|
526
|
+
],
|
|
527
|
+
)
|
|
528
|
+
@pytest.mark.parametrize(
|
|
529
|
+
"rdump_params",
|
|
530
|
+
[
|
|
531
|
+
[],
|
|
532
|
+
["--mode=csv"],
|
|
533
|
+
["--mode=line"],
|
|
534
|
+
],
|
|
535
|
+
)
|
|
536
|
+
def test_flow_record_tz_output(tmp_path, capsys, date_str, tz, expected_date_str, rdump_params):
|
|
537
|
+
TestRecord = RecordDescriptor(
|
|
538
|
+
"test/flow_record_tz",
|
|
539
|
+
[
|
|
540
|
+
("datetime", "stamp"),
|
|
541
|
+
],
|
|
542
|
+
)
|
|
543
|
+
with RecordWriter(tmp_path / "test.records") as writer:
|
|
544
|
+
writer.write(TestRecord(stamp=date_str))
|
|
545
|
+
|
|
546
|
+
env_dict = {}
|
|
547
|
+
if tz is not None:
|
|
548
|
+
env_dict["FLOW_RECORD_TZ"] = tz
|
|
549
|
+
|
|
550
|
+
with mock.patch.dict(os.environ, env_dict, clear=True):
|
|
551
|
+
# Reconfigure DISPLAY_TZINFO
|
|
552
|
+
flow.record.fieldtypes.DISPLAY_TZINFO = flow_record_tz(default_tz="UTC")
|
|
553
|
+
|
|
554
|
+
rdump.main([str(tmp_path / "test.records")] + rdump_params)
|
|
555
|
+
captured = capsys.readouterr()
|
|
556
|
+
assert captured.err == ""
|
|
557
|
+
assert expected_date_str in captured.out
|
|
558
|
+
|
|
559
|
+
# restore DISPLAY_TZINFO just in case
|
|
560
|
+
flow.record.fieldtypes.DISPLAY_TZINFO = flow_record_tz(default_tz="UTC")
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def test_flow_record_invalid_tz(tmp_path, capsys):
|
|
564
|
+
TestRecord = RecordDescriptor(
|
|
565
|
+
"test/flow_record_tz",
|
|
566
|
+
[
|
|
567
|
+
("datetime", "stamp"),
|
|
568
|
+
],
|
|
569
|
+
)
|
|
570
|
+
with RecordWriter(tmp_path / "test.records") as writer:
|
|
571
|
+
writer.write(TestRecord(stamp="2023-08-16T17:46:55.390691+02:00"))
|
|
572
|
+
|
|
573
|
+
env_dict = {
|
|
574
|
+
"FLOW_RECORD_TZ": "invalid",
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
with mock.patch.dict(os.environ, env_dict, clear=True):
|
|
578
|
+
# Reconfigure DISPLAY_TZINFO
|
|
579
|
+
with pytest.warns(UserWarning, match=".* falling back to timezone.utc"):
|
|
580
|
+
flow.record.fieldtypes.DISPLAY_TZINFO = flow_record_tz()
|
|
581
|
+
|
|
582
|
+
rdump.main([str(tmp_path / "test.records")])
|
|
583
|
+
captured = capsys.readouterr()
|
|
584
|
+
assert captured.err == ""
|
|
585
|
+
assert "2023-08-16 15:46:55.390691+00:00" in captured.out
|
|
586
|
+
assert flow.record.fieldtypes.DISPLAY_TZINFO == timezone.utc
|
|
587
|
+
|
|
588
|
+
# restore DISPLAY_TZINFO just in case
|
|
589
|
+
flow.record.fieldtypes.DISPLAY_TZINFO = flow_record_tz(default_tz="UTC")
|
|
@@ -203,7 +203,7 @@ def test_record_writer_stdout():
|
|
|
203
203
|
def test_record_adapter_archive(tmpdir):
|
|
204
204
|
# archive some records, using "testing" as name
|
|
205
205
|
writer = RecordWriter("archive://{}?name=testing".format(tmpdir))
|
|
206
|
-
dt = datetime.datetime.
|
|
206
|
+
dt = datetime.datetime.now(datetime.timezone.utc)
|
|
207
207
|
count = 0
|
|
208
208
|
for rec in generate_records():
|
|
209
209
|
writer.write(rec)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import codecs
|
|
2
|
-
import datetime
|
|
3
2
|
import json
|
|
4
3
|
import os
|
|
5
4
|
import pathlib
|
|
6
5
|
import subprocess
|
|
7
6
|
import sys
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
8
|
from unittest.mock import mock_open, patch
|
|
9
9
|
|
|
10
10
|
import msgpack
|
|
@@ -32,7 +32,7 @@ from flow.record.utils import is_stdout
|
|
|
32
32
|
def test_datetime_serialization():
|
|
33
33
|
packer = RecordPacker()
|
|
34
34
|
|
|
35
|
-
now = datetime.
|
|
35
|
+
now = datetime.now(timezone.utc)
|
|
36
36
|
|
|
37
37
|
for tz in ["UTC", "Europe/Amsterdam"]:
|
|
38
38
|
os.environ["TZ"] = tz
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from datetime import datetime
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
@@ -449,7 +449,7 @@ def test_record_in_records():
|
|
|
449
449
|
)
|
|
450
450
|
|
|
451
451
|
test_str = "this is a test"
|
|
452
|
-
dt = datetime.
|
|
452
|
+
dt = datetime.now(timezone.utc)
|
|
453
453
|
record_a = RecordA(some_dt=dt, field=test_str)
|
|
454
454
|
record_b = RecordB(record=record_a, some_dt=dt)
|
|
455
455
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|