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.
dfdatetime/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ """Digital Forensics Date and Time (dfDateTime).
2
+
3
+ dfDateTime, or Digital Forensics date and time, provides date and time objects to
4
+ preserve accuracy and precision.
5
+ """
6
+
7
+ # Imports for date time values factory.
8
+ from dfdatetime import apfs_time
9
+ from dfdatetime import cocoa_time
10
+ from dfdatetime import delphi_date_time
11
+ from dfdatetime import dotnet_datetime
12
+ from dfdatetime import fat_date_time
13
+ from dfdatetime import filetime
14
+ from dfdatetime import hfs_time
15
+ from dfdatetime import golang_time
16
+ from dfdatetime import java_time
17
+ from dfdatetime import ole_automation_date
18
+ from dfdatetime import posix_time
19
+ from dfdatetime import rfc2579_date_time
20
+ from dfdatetime import semantic_time
21
+ from dfdatetime import systemtime
22
+ from dfdatetime import time_elements
23
+ from dfdatetime import uuid_time
24
+ from dfdatetime import webkit_time
25
+
26
+ __version__ = "20260730"
@@ -0,0 +1,86 @@
1
+ """Apple File System (APFS) time implementation."""
2
+
3
+ import decimal
4
+
5
+ from dfdatetime import definitions
6
+ from dfdatetime import factory
7
+ from dfdatetime import posix_time
8
+
9
+
10
+ class APFSTime(posix_time.PosixTimeInNanoseconds):
11
+ """Apple File System (APFS) timestamp.
12
+
13
+ The APFS timestamp is a signed 64-bit integer that contains the number of
14
+ nanoseconds since 1970-01-01 00:00:00.
15
+
16
+ Attributes:
17
+ is_local_time (bool): True if the date and time value is in local time.
18
+ """
19
+
20
+ def _GetNormalizedTimestamp(self):
21
+ """Retrieves the normalized timestamp.
22
+
23
+ Returns:
24
+ decimal.Decimal: normalized timestamp, which contains the number of
25
+ seconds since January 1, 1970 00:00:00 and a fraction of second used
26
+ for increased precision, or None if the normalized timestamp cannot be
27
+ determined.
28
+ """
29
+ if self._normalized_timestamp is None:
30
+ if (
31
+ self._timestamp is not None
32
+ and self._timestamp >= self._INT64_MIN
33
+ and self._timestamp <= self._INT64_MAX
34
+ ):
35
+ self._normalized_timestamp = (
36
+ decimal.Decimal(self._timestamp)
37
+ / definitions.NANOSECONDS_PER_SECOND
38
+ )
39
+
40
+ if self._time_zone_offset:
41
+ self._normalized_timestamp -= self._time_zone_offset * 60
42
+
43
+ return self._normalized_timestamp
44
+
45
+ def CopyFromDateTimeString(self, time_string):
46
+ """Copies a APFS timestamp from a date and time string.
47
+
48
+ Args:
49
+ time_string (str): date and time value formatted as:
50
+ YYYY-MM-DD hh:mm:ss.######[+-]##:##
51
+
52
+ Where # are numeric digits ranging from 0 to 9 and the seconds
53
+ fraction can be either 3, 6 or 9 digits. The time of day, seconds
54
+ fraction and time zone offset are optional. The default time zone
55
+ is UTC.
56
+
57
+ Raises:
58
+ ValueError: if the date and time value is not supported.
59
+ """
60
+ super().CopyFromDateTimeString(time_string)
61
+
62
+ if (
63
+ self._timestamp is None
64
+ or self._timestamp < self._INT64_MIN
65
+ or self._timestamp > self._INT64_MAX
66
+ ):
67
+ raise ValueError("Date time value not supported.")
68
+
69
+ def CopyToDateTimeString(self):
70
+ """Copies the APFS timestamp to a date and time string.
71
+
72
+ Returns:
73
+ str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#########" or
74
+ None if the timestamp is missing or invalid.
75
+ """
76
+ if (
77
+ self._timestamp is None
78
+ or self._timestamp < self._INT64_MIN
79
+ or self._timestamp > self._INT64_MAX
80
+ ):
81
+ return None
82
+
83
+ return super().CopyToDateTimeString()
84
+
85
+
86
+ factory.Factory.RegisterDateTimeValues(APFSTime)
@@ -0,0 +1,149 @@
1
+ """Cocoa timestamp 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 CocoaTimeEpoch(interface.DateTimeEpoch):
11
+ """Cocoa time epoch."""
12
+
13
+ def __init__(self):
14
+ """Initializes a Cocoa time epoch."""
15
+ super().__init__(2001, 1, 1)
16
+
17
+
18
+ class CocoaTime(interface.DateTimeValues):
19
+ """Cocoa timestamp.
20
+
21
+ The Cocoa timestamp is a floating point value that contains the number of
22
+ seconds since 2001-01-01 00:00:00 (also known as the Cocoa epoch).
23
+ Negative values represent date and times predating the Cocoa epoch.
24
+
25
+ Attributes:
26
+ is_local_time (bool): True if the date and time value is in local time.
27
+ """
28
+
29
+ # The difference between January 1, 2001 and January 1, 1970 in seconds.
30
+ _COCOA_TO_POSIX_BASE = -978307200
31
+
32
+ _EPOCH = CocoaTimeEpoch()
33
+
34
+ def __init__(self, precision=None, time_zone_offset=None, timestamp=None):
35
+ """Initializes a Cocoa timestamp.
36
+
37
+ Args:
38
+ precision (Optional[str]): precision of the date and time value, which
39
+ should be one of the PRECISION_VALUES in definitions.
40
+ time_zone_offset (Optional[int]): time zone offset in number of minutes
41
+ from UTC or None if not set.
42
+ timestamp (Optional[float]): Cocoa timestamp.
43
+ """
44
+ super().__init__(
45
+ precision=precision or definitions.PRECISION_1_SECOND,
46
+ time_zone_offset=time_zone_offset,
47
+ )
48
+ self._timestamp = timestamp
49
+
50
+ @property
51
+ def timestamp(self):
52
+ """float: Cocoa timestamp or None if not set."""
53
+ return self._timestamp
54
+
55
+ def _GetNormalizedTimestamp(self):
56
+ """Retrieves the normalized timestamp.
57
+
58
+ Returns:
59
+ decimal.Decimal: normalized timestamp, which contains the number of
60
+ seconds since January 1, 1970 00:00:00 and a fraction of second used
61
+ for increased precision, or None if the normalized timestamp cannot be
62
+ determined.
63
+ """
64
+ if self._normalized_timestamp is None:
65
+ if self._timestamp is not None:
66
+ self._normalized_timestamp = (
67
+ decimal.Decimal(self._timestamp) - self._COCOA_TO_POSIX_BASE
68
+ )
69
+
70
+ if self._time_zone_offset:
71
+ self._normalized_timestamp -= self._time_zone_offset * 60
72
+
73
+ return self._normalized_timestamp
74
+
75
+ def CopyFromDateTimeString(self, time_string):
76
+ """Copies a Cocoa timestamp from a date and time string.
77
+
78
+ Args:
79
+ time_string (str): date and time value formatted as:
80
+ YYYY-MM-DD hh:mm:ss.######[+-]##:##
81
+
82
+ Where # are numeric digits ranging from 0 to 9 and the seconds
83
+ fraction can be either 3, 6 or 9 digits. The time of day, seconds
84
+ fraction and time zone offset are optional. The default time zone
85
+ is UTC.
86
+
87
+ Raises:
88
+ ValueError: if the time string is invalid or not supported.
89
+ """
90
+ date_time_values = self._CopyDateTimeFromString(time_string)
91
+
92
+ year = date_time_values.get("year", 0)
93
+ month = date_time_values.get("month", 0)
94
+ day_of_month = date_time_values.get("day_of_month", 0)
95
+ hours = date_time_values.get("hours", 0)
96
+ minutes = date_time_values.get("minutes", 0)
97
+ seconds = date_time_values.get("seconds", 0)
98
+ nanoseconds = date_time_values.get("nanoseconds", 0)
99
+ time_zone_offset = date_time_values.get("time_zone_offset")
100
+
101
+ timestamp = self._GetNumberOfSecondsFromElements(
102
+ year, month, day_of_month, hours, minutes, seconds
103
+ )
104
+ timestamp += self._COCOA_TO_POSIX_BASE
105
+
106
+ timestamp = float(timestamp)
107
+ timestamp += float(nanoseconds) / definitions.NANOSECONDS_PER_SECOND
108
+
109
+ self._normalized_timestamp = None
110
+ self._timestamp = timestamp
111
+ self._time_zone_offset = time_zone_offset
112
+
113
+ def CopyToDateTimeString(self):
114
+ """Copies the Cocoa timestamp to a date and time string.
115
+
116
+ Returns:
117
+ str: date and time value formatted as: YYYY-MM-DD hh:mm:ss.###### or
118
+ None if the timestamp cannot be copied to a date and time string.
119
+ """
120
+ if self._timestamp is None:
121
+ return None
122
+
123
+ number_of_days, hours, minutes, seconds = self._GetTimeValues(
124
+ int(self._timestamp)
125
+ )
126
+ year, month, day_of_month = self._GetDateValuesWithEpoch(
127
+ number_of_days, self._EPOCH
128
+ )
129
+ microseconds = int((self._timestamp % 1) * definitions.MICROSECONDS_PER_SECOND)
130
+
131
+ return (
132
+ f"{year:04d}-{month:02d}-{day_of_month:02d} "
133
+ f"{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}"
134
+ )
135
+
136
+ def CopyToSerializableDict(self):
137
+ """Copies the date time value to a serializable dictionary.
138
+
139
+ Returns:
140
+ dict[str, object]: serializable dictionary.
141
+ """
142
+ serializable_dict = self._CreateSerializableDict()
143
+
144
+ serializable_dict["timestamp"] = self._timestamp
145
+
146
+ return serializable_dict
147
+
148
+
149
+ factory.Factory.RegisterDateTimeValues(CocoaTime)
@@ -0,0 +1,23 @@
1
+ """Function decorators."""
2
+
3
+ import warnings
4
+
5
+
6
+ def deprecated(function): # pylint: disable=invalid-name
7
+ """Decorator to mark functions or methods as deprecated."""
8
+
9
+ def IssueDeprecationWarning(*args, **kwargs):
10
+ """Issue a deprecation warning."""
11
+ warnings.simplefilter("default", DeprecationWarning)
12
+ warnings.warn(
13
+ f"Call to deprecated function: {function.__name__:s}.",
14
+ category=DeprecationWarning,
15
+ stacklevel=2,
16
+ )
17
+
18
+ return function(*args, **kwargs)
19
+
20
+ IssueDeprecationWarning.__name__ = function.__name__
21
+ IssueDeprecationWarning.__doc__ = function.__doc__
22
+ IssueDeprecationWarning.__dict__.update(function.__dict__)
23
+ return IssueDeprecationWarning
@@ -0,0 +1,97 @@
1
+ """The date and time definitions.
2
+
3
+ Also see:
4
+ https://en.wikipedia.org/wiki/Day
5
+ https://en.wikipedia.org/wiki/Hour
6
+ https://en.wikipedia.org/wiki/Minute
7
+ """
8
+
9
+ DAYS_PER_MONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
10
+
11
+ SECONDS_PER_DAY = 86400
12
+
13
+ DECISECONDS_PER_SECOND = 10
14
+
15
+ CENTISECONDS_PER_SECOND = 100
16
+
17
+ MILLISECONDS_PER_SECOND = 1000
18
+
19
+ DECIMICROSECONDS_PER_SECOND = 10000
20
+
21
+ MICROSECONDS_PER_DAY = 86400000000
22
+ MICROSECONDS_PER_SECOND = 1000000
23
+ MICROSECONDS_PER_DECISECOND = 100000
24
+ MICROSECONDS_PER_MILLISECOND = 1000
25
+
26
+ NANOSECONDS_PER_DAY = 86400000000000
27
+ NANOSECONDS_PER_SECOND = 1000000000
28
+ NANOSECONDS_PER_DECISECOND = 100000000
29
+ NANOSECONDS_PER_CENTISECOND = 10000000
30
+ NANOSECONDS_PER_MILLISECOND = 1000000
31
+ NANOSECONDS_PER_DECIMILISECOND = 100000
32
+ NANOSECONDS_PER_MICROSECOND = 1000
33
+
34
+ PRECISION_1_DAY = "1d"
35
+ PRECISION_1_HOUR = "1h"
36
+ PRECISION_1_NANOSECOND = "1ns"
37
+ PRECISION_10_NANOSECONDS = "10s"
38
+ PRECISION_100_NANOSECONDS = "100ns"
39
+ PRECISION_1_MICROSECOND = "1us"
40
+ PRECISION_10_MICROSECONDS = "10us"
41
+ PRECISION_100_MICROSECONDS = "100us"
42
+ PRECISION_1_MILLISECOND = "1ms"
43
+ PRECISION_10_MILLISECONDS = "10ms"
44
+ PRECISION_100_MILLISECONDS = "100ms"
45
+ PRECISION_1_MINUTE = "1min"
46
+ PRECISION_1_SECOND = "1s"
47
+ PRECISION_2_SECONDS = "2s"
48
+
49
+ PRECISION_VALUES = frozenset(
50
+ [
51
+ PRECISION_1_DAY,
52
+ PRECISION_1_HOUR,
53
+ PRECISION_1_NANOSECOND,
54
+ PRECISION_10_NANOSECONDS,
55
+ PRECISION_100_NANOSECONDS,
56
+ PRECISION_1_MICROSECOND,
57
+ PRECISION_10_MICROSECONDS,
58
+ PRECISION_100_MICROSECONDS,
59
+ PRECISION_1_MILLISECOND,
60
+ PRECISION_10_MILLISECONDS,
61
+ PRECISION_100_MILLISECONDS,
62
+ PRECISION_1_MINUTE,
63
+ PRECISION_1_SECOND,
64
+ PRECISION_2_SECONDS,
65
+ ]
66
+ )
67
+
68
+ # Create a days per century lookup table.
69
+ DAYS_PER_CENTURY = {}
70
+ for year in range(-10000, 10000, 100):
71
+ if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
72
+ number_of_days = 36525
73
+ else:
74
+ number_of_days = 36524
75
+ DAYS_PER_CENTURY[year] = number_of_days
76
+
77
+ # Create a days per year lookup table.
78
+ DAYS_PER_YEAR = {}
79
+ for year in range(-10000, 10000, 1):
80
+ if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
81
+ number_of_days = 366
82
+ else:
83
+ number_of_days = 365
84
+ DAYS_PER_YEAR[year] = number_of_days
85
+
86
+ # Create a days per year in POSIX epoch lookup table.
87
+ DAYS_PER_YEAR_IN_POSIX_EPOCH = {}
88
+
89
+ number_of_days = 0
90
+ for year in range(1969, -10000, -1):
91
+ number_of_days -= DAYS_PER_YEAR[year]
92
+ DAYS_PER_YEAR_IN_POSIX_EPOCH[year] = number_of_days
93
+
94
+ number_of_days = 0
95
+ for year in range(1970, 10000, 1):
96
+ DAYS_PER_YEAR_IN_POSIX_EPOCH[year] = number_of_days
97
+ number_of_days += DAYS_PER_YEAR[year]
@@ -0,0 +1,166 @@
1
+ """Delphi TDateTime 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 DelphiDateTimeEpoch(interface.DateTimeEpoch):
11
+ """Delphi TDateTime epoch."""
12
+
13
+ def __init__(self):
14
+ """Initializes a Delphi TDateTime epoch."""
15
+ super().__init__(1899, 12, 30)
16
+
17
+
18
+ class DelphiDateTime(interface.DateTimeValues):
19
+ """Delphi TDateTime timestamp.
20
+
21
+ The Delphi TDateTime timestamp is a floating point value that contains
22
+ the number of days since 1899-12-30 00:00:00 (also known as the epoch).
23
+ Negative values represent date and times predating the epoch.
24
+
25
+ The maximal correct date supported by TDateTime values is limited to:
26
+ 9999-12-31 23:59:59.999
27
+
28
+ Attributes:
29
+ is_local_time (bool): True if the date and time value is in local time.
30
+ """
31
+
32
+ # The difference between December 30, 1899 and January 1, 1970 in days.
33
+ _DELPHI_TO_POSIX_BASE = 25569
34
+
35
+ _EPOCH = DelphiDateTimeEpoch()
36
+
37
+ def __init__(self, precision=None, time_zone_offset=None, timestamp=None):
38
+ """Initializes a Delphi TDateTime timestamp.
39
+
40
+ Args:
41
+ precision (Optional[str]): precision of the date and time value, which
42
+ should be one of the PRECISION_VALUES in definitions.
43
+ time_zone_offset (Optional[int]): time zone offset in number of minutes
44
+ from UTC or None if not set.
45
+ timestamp (Optional[float]): Delphi TDateTime timestamp.
46
+ """
47
+ super().__init__(
48
+ precision=precision or definitions.PRECISION_1_MILLISECOND,
49
+ time_zone_offset=time_zone_offset,
50
+ )
51
+ self._timestamp = timestamp
52
+
53
+ @property
54
+ def timestamp(self):
55
+ """float: Delphi TDateTime timestamp or None if not set."""
56
+ return self._timestamp
57
+
58
+ def _GetNormalizedTimestamp(self):
59
+ """Retrieves the normalized timestamp.
60
+
61
+ Returns:
62
+ decimal.Decimal: normalized timestamp, which contains the number of
63
+ seconds since January 1, 1970 00:00:00 and a fraction of second used
64
+ for increased precision, or None if the normalized timestamp cannot be
65
+ determined.
66
+ """
67
+ if self._normalized_timestamp is None:
68
+ if self._timestamp is not None:
69
+ self._normalized_timestamp = (
70
+ decimal.Decimal(self._timestamp) - self._DELPHI_TO_POSIX_BASE
71
+ )
72
+ self._normalized_timestamp *= definitions.SECONDS_PER_DAY
73
+
74
+ if self._time_zone_offset:
75
+ self._normalized_timestamp -= self._time_zone_offset * 60
76
+
77
+ return self._normalized_timestamp
78
+
79
+ def CopyFromDateTimeString(self, time_string):
80
+ """Copies a Delphi TDateTime timestamp from a string.
81
+
82
+ Args:
83
+ time_string (str): date and time value formatted as:
84
+ YYYY-MM-DD hh:mm:ss.######[+-]##:##
85
+
86
+ Where # are numeric digits ranging from 0 to 9 and the seconds
87
+ fraction can be either 3, 6 or 9 digits. The time of day, seconds
88
+ fraction and time zone offset are optional. The default time zone
89
+ is UTC.
90
+
91
+ Raises:
92
+ ValueError: if the time string is invalid or not supported.
93
+ """
94
+ date_time_values = self._CopyDateTimeFromString(time_string)
95
+
96
+ year = date_time_values.get("year", 0)
97
+ month = date_time_values.get("month", 0)
98
+ day_of_month = date_time_values.get("day_of_month", 0)
99
+ hours = date_time_values.get("hours", 0)
100
+ minutes = date_time_values.get("minutes", 0)
101
+ seconds = date_time_values.get("seconds", 0)
102
+ nanoseconds = date_time_values.get("nanoseconds", 0)
103
+ time_zone_offset = date_time_values.get("time_zone_offset")
104
+
105
+ if year > 9999:
106
+ raise ValueError(f"Unsupported year value: {year:d}.")
107
+
108
+ timestamp = self._GetNumberOfSecondsFromElements(
109
+ year, month, day_of_month, hours, minutes, seconds
110
+ )
111
+ timestamp = float(timestamp) / definitions.SECONDS_PER_DAY
112
+ timestamp += self._DELPHI_TO_POSIX_BASE
113
+ timestamp += float(nanoseconds) / definitions.NANOSECONDS_PER_DAY
114
+
115
+ self._normalized_timestamp = None
116
+ self._timestamp = timestamp
117
+ self._time_zone_offset = time_zone_offset
118
+
119
+ def CopyToDateTimeString(self):
120
+ """Copies the Delphi TDateTime timestamp to a date and time string.
121
+
122
+ Returns:
123
+ str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or
124
+ None if the timestamp is missing.
125
+ """
126
+ if self._timestamp is None:
127
+ return None
128
+
129
+ number_of_seconds = self._timestamp * definitions.SECONDS_PER_DAY
130
+
131
+ number_of_days, hours, minutes, seconds = self._GetTimeValues(
132
+ int(number_of_seconds)
133
+ )
134
+ # The maximum date supported by TDateTime values is limited to:
135
+ # 9999-12-31 23:59:59.999 (approximate 2958465 days since epoch).
136
+ # The minimum date is unknown hence assuming it is limited to:
137
+ # 0001-01-01 00:00:00.000 (approximate -693593 days since epoch).
138
+
139
+ if number_of_days < -693593 or number_of_days > 2958465:
140
+ return None
141
+
142
+ year, month, day_of_month = self._GetDateValuesWithEpoch(
143
+ number_of_days, self._EPOCH
144
+ )
145
+ microseconds = int(
146
+ (number_of_seconds % 1) * definitions.MICROSECONDS_PER_SECOND
147
+ )
148
+ return (
149
+ f"{year:04d}-{month:02d}-{day_of_month:02d} "
150
+ f"{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}"
151
+ )
152
+
153
+ def CopyToSerializableDict(self):
154
+ """Copies the date time value to a serializable dictionary.
155
+
156
+ Returns:
157
+ dict[str, object]: serializable dictionary.
158
+ """
159
+ serializable_dict = self._CreateSerializableDict()
160
+
161
+ serializable_dict["timestamp"] = self._timestamp
162
+
163
+ return serializable_dict
164
+
165
+
166
+ factory.Factory.RegisterDateTimeValues(DelphiDateTime)