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 +26 -0
- dfdatetime/apfs_time.py +86 -0
- dfdatetime/cocoa_time.py +149 -0
- dfdatetime/decorators.py +23 -0
- dfdatetime/definitions.py +97 -0
- dfdatetime/delphi_date_time.py +166 -0
- dfdatetime/dotnet_datetime.py +157 -0
- dfdatetime/factory.py +59 -0
- dfdatetime/fake_time.py +139 -0
- dfdatetime/fat_date_time.py +337 -0
- dfdatetime/filetime.py +164 -0
- dfdatetime/golang_time.py +234 -0
- dfdatetime/hfs_time.py +150 -0
- dfdatetime/interface.py +1071 -0
- dfdatetime/java_time.py +66 -0
- dfdatetime/ole_automation_date.py +158 -0
- dfdatetime/posix_time.py +501 -0
- dfdatetime/precisions.py +425 -0
- dfdatetime/rfc2579_date_time.py +289 -0
- dfdatetime/semantic_time.py +329 -0
- dfdatetime/serializer.py +154 -0
- dfdatetime/systemtime.py +253 -0
- dfdatetime/time_elements.py +1732 -0
- dfdatetime/uuid_time.py +170 -0
- dfdatetime/webkit_time.py +159 -0
- dfdatetime-20260730.dist-info/METADATA +25 -0
- dfdatetime-20260730.dist-info/RECORD +32 -0
- dfdatetime-20260730.dist-info/WHEEL +5 -0
- dfdatetime-20260730.dist-info/licenses/ACKNOWLEDGEMENTS +16 -0
- dfdatetime-20260730.dist-info/licenses/AUTHORS +14 -0
- dfdatetime-20260730.dist-info/licenses/LICENSE +202 -0
- dfdatetime-20260730.dist-info/top_level.txt +1 -0
dfdatetime/serializer.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""The date and time values serializer."""
|
|
2
|
+
|
|
3
|
+
from dfdatetime import factory
|
|
4
|
+
from dfdatetime import interface
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Serializer:
|
|
8
|
+
"""Date and time values serializer."""
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def ConvertDictToDateTimeValues(cls, json_dict):
|
|
12
|
+
"""Converts a JSON dict into a date time values object.
|
|
13
|
+
|
|
14
|
+
This method is deprecated use ConvertJSONToDateTimeValues instead.
|
|
15
|
+
|
|
16
|
+
The dictionary of the JSON serialized objects consists of:
|
|
17
|
+
{
|
|
18
|
+
'__type__': 'DateTimeValues'
|
|
19
|
+
'__class_name__': 'RFC2579DateTime'
|
|
20
|
+
...
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Here '__type__' indicates the object base type. In this case this should
|
|
24
|
+
be 'DateTimeValues'. The rest of the elements of the dictionary make up the
|
|
25
|
+
date time values object properties.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
json_dict (dict[str, object]): JSON serialized objects.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
dfdatetime.DateTimeValues: date and time values.
|
|
32
|
+
"""
|
|
33
|
+
return cls.ConvertJSONToDateTimeValues(json_dict)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def ConvertDateTimeValuesToDict(cls, date_time_values):
|
|
37
|
+
"""Converts a date and time values object into a JSON dictionary.
|
|
38
|
+
|
|
39
|
+
This method is deprecated use ConvertDateTimeValuesToJSON instead.
|
|
40
|
+
|
|
41
|
+
The resulting dictionary of the JSON serialized objects consists of:
|
|
42
|
+
{
|
|
43
|
+
'__type__': 'DateTimeValues'
|
|
44
|
+
'__class_name__': 'RFC2579DateTime'
|
|
45
|
+
...
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Here '__type__' indicates the object base type. In this case
|
|
49
|
+
'DateTimeValues'. The rest of the elements of the dictionary make up the
|
|
50
|
+
date and time value object properties.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
date_time_values (dfdatetime.DateTimeValues): date and time values.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
dict[str, object]: JSON serialized objects.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
TypeError: if object is not an instance of DateTimeValues.
|
|
60
|
+
"""
|
|
61
|
+
if not isinstance(date_time_values, interface.DateTimeValues):
|
|
62
|
+
raise TypeError
|
|
63
|
+
|
|
64
|
+
return cls.ConvertDateTimeValuesToJSON(date_time_values)
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def ConvertDateTimeValuesToJSON(cls, date_time_values):
|
|
68
|
+
"""Converts a date and time values object into a JSON dictionary.
|
|
69
|
+
|
|
70
|
+
The resulting dictionary of the JSON serialized objects consists of:
|
|
71
|
+
{
|
|
72
|
+
'__type__': 'DateTimeValues'
|
|
73
|
+
'__class_name__': 'RFC2579DateTime'
|
|
74
|
+
...
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Here '__type__' indicates the object base type. In this case
|
|
78
|
+
'DateTimeValues'. The rest of the elements of the dictionary make up the
|
|
79
|
+
date and time value object properties.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
date_time_values (dfdatetime.DateTimeValues): date and time values.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
dict[str, object]: JSON serialized objects.
|
|
86
|
+
"""
|
|
87
|
+
return date_time_values.CopyToSerializableDict()
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def ConvertJSONToDateTimeValues(cls, json_dict):
|
|
91
|
+
"""Converts a JSON dict into a date time values object.
|
|
92
|
+
|
|
93
|
+
The dictionary of the JSON serialized objects consists of:
|
|
94
|
+
{
|
|
95
|
+
'__type__': 'DateTimeValues'
|
|
96
|
+
'__class_name__': 'RFC2579DateTime'
|
|
97
|
+
...
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Here '__type__' indicates the object base type. In this case this should
|
|
101
|
+
be 'DateTimeValues'. The rest of the elements of the dictionary make up the
|
|
102
|
+
date time values object properties.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
json_dict (dict[str, object]): JSON serialized objects.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
dfdatetime.DateTimeValues: date and time values.
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
KeyError: If date and time values type is not supported by factory.
|
|
112
|
+
"""
|
|
113
|
+
class_name = json_dict.get("__class_name__")
|
|
114
|
+
if class_name:
|
|
115
|
+
del json_dict["__class_name__"]
|
|
116
|
+
|
|
117
|
+
# Remove the class type from the JSON dict since we cannot pass it.
|
|
118
|
+
del json_dict["__type__"]
|
|
119
|
+
|
|
120
|
+
if class_name not in (
|
|
121
|
+
"TimeElements",
|
|
122
|
+
"TimeElementsInMilliseconds",
|
|
123
|
+
"TimeElementsInMicroseconds",
|
|
124
|
+
"TimeElementsInNanoseconds",
|
|
125
|
+
):
|
|
126
|
+
is_delta = json_dict.get("is_delta")
|
|
127
|
+
if is_delta is not None:
|
|
128
|
+
del json_dict["is_delta"]
|
|
129
|
+
|
|
130
|
+
is_local_time = json_dict.get("is_local_time")
|
|
131
|
+
if is_local_time is not None:
|
|
132
|
+
del json_dict["is_local_time"]
|
|
133
|
+
|
|
134
|
+
time_zone_hint = json_dict.get("time_zone_hint")
|
|
135
|
+
if time_zone_hint is not None:
|
|
136
|
+
del json_dict["time_zone_hint"]
|
|
137
|
+
|
|
138
|
+
if class_name in ("InvalidTime", "Never", "NotSet"):
|
|
139
|
+
string = json_dict.get("string")
|
|
140
|
+
if string is not None:
|
|
141
|
+
del json_dict["string"]
|
|
142
|
+
|
|
143
|
+
if class_name in ("GolangTime", "RFC2579DateTime"):
|
|
144
|
+
time_zone_offset = json_dict.get("time_zone_offset")
|
|
145
|
+
if time_zone_offset is not None:
|
|
146
|
+
del json_dict["time_zone_offset"]
|
|
147
|
+
|
|
148
|
+
date_time = factory.Factory.NewDateTimeValues(class_name, **json_dict)
|
|
149
|
+
if is_local_time:
|
|
150
|
+
date_time.is_local_time = is_local_time
|
|
151
|
+
if time_zone_hint:
|
|
152
|
+
date_time.time_zone_hint = time_zone_hint
|
|
153
|
+
|
|
154
|
+
return date_time
|
dfdatetime/systemtime.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""SYSTEMTIME structure 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 Systemtime(interface.DateTimeValues):
|
|
11
|
+
"""SYSTEMTIME structure.
|
|
12
|
+
|
|
13
|
+
The SYSTEMTIME structure is 16 bytes of size and contains:
|
|
14
|
+
|
|
15
|
+
struct {
|
|
16
|
+
WORD year,
|
|
17
|
+
WORD month,
|
|
18
|
+
WORD day_of_week,
|
|
19
|
+
WORD day_of_month,
|
|
20
|
+
WORD hour,
|
|
21
|
+
WORD minute,
|
|
22
|
+
WORD second,
|
|
23
|
+
WORD millisecond
|
|
24
|
+
}
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, precision=None, system_time_tuple=None, time_zone_offset=None):
|
|
28
|
+
"""Initializes a SYSTEMTIME structure.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
precision (Optional[str]): precision of the date and time value, which
|
|
32
|
+
should be one of the PRECISION_VALUES in definitions.
|
|
33
|
+
system_time_tuple
|
|
34
|
+
(Optional[tuple[int, int, int, int, int, int, int, int]]):
|
|
35
|
+
system time, contains year, month, day of week, day of month,
|
|
36
|
+
hours, minutes, seconds and milliseconds.
|
|
37
|
+
time_zone_offset (Optional[int]): time zone offset in number of minutes
|
|
38
|
+
from UTC or None if not set.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: if the system time is invalid.
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(
|
|
44
|
+
precision=precision or definitions.PRECISION_1_MILLISECOND,
|
|
45
|
+
time_zone_offset=time_zone_offset,
|
|
46
|
+
)
|
|
47
|
+
self._day_of_month = None
|
|
48
|
+
self._day_of_week = None
|
|
49
|
+
self._hours = None
|
|
50
|
+
self._milliseconds = None
|
|
51
|
+
self._minutes = None
|
|
52
|
+
self._month = None
|
|
53
|
+
self._number_of_seconds = None
|
|
54
|
+
self._seconds = None
|
|
55
|
+
self._year = None
|
|
56
|
+
|
|
57
|
+
if system_time_tuple:
|
|
58
|
+
if len(system_time_tuple) < 8:
|
|
59
|
+
raise ValueError("Invalid system time tuple 8 elements required.")
|
|
60
|
+
|
|
61
|
+
if system_time_tuple[0] < 1601 or system_time_tuple[0] > 30827:
|
|
62
|
+
raise ValueError("Year value out of bounds.")
|
|
63
|
+
|
|
64
|
+
if system_time_tuple[1] not in range(1, 13):
|
|
65
|
+
raise ValueError("Month value out of bounds.")
|
|
66
|
+
|
|
67
|
+
if system_time_tuple[2] not in range(0, 7):
|
|
68
|
+
raise ValueError("Day of week value out of bounds.")
|
|
69
|
+
|
|
70
|
+
days_per_month = self._GetDaysPerMonth(
|
|
71
|
+
system_time_tuple[0], system_time_tuple[1]
|
|
72
|
+
)
|
|
73
|
+
if system_time_tuple[3] < 1 or system_time_tuple[3] > days_per_month:
|
|
74
|
+
raise ValueError("Day of month value out of bounds.")
|
|
75
|
+
|
|
76
|
+
if system_time_tuple[4] not in range(0, 24):
|
|
77
|
+
raise ValueError("Hours value out of bounds.")
|
|
78
|
+
|
|
79
|
+
if system_time_tuple[5] not in range(0, 60):
|
|
80
|
+
raise ValueError("Minutes value out of bounds.")
|
|
81
|
+
|
|
82
|
+
# TODO: support a leap second?
|
|
83
|
+
if system_time_tuple[6] not in range(0, 60):
|
|
84
|
+
raise ValueError("Seconds value out of bounds.")
|
|
85
|
+
|
|
86
|
+
if system_time_tuple[7] < 0 or system_time_tuple[7] > 999:
|
|
87
|
+
raise ValueError("Milliseconds value out of bounds.")
|
|
88
|
+
|
|
89
|
+
self._day_of_month = system_time_tuple[3]
|
|
90
|
+
self._day_of_week = system_time_tuple[2]
|
|
91
|
+
self._hours = system_time_tuple[4]
|
|
92
|
+
self._milliseconds = system_time_tuple[7]
|
|
93
|
+
self._minutes = system_time_tuple[5]
|
|
94
|
+
self._month = system_time_tuple[1]
|
|
95
|
+
self._seconds = system_time_tuple[6]
|
|
96
|
+
self._year = system_time_tuple[0]
|
|
97
|
+
|
|
98
|
+
self._number_of_seconds = self._GetNumberOfSecondsFromElements(
|
|
99
|
+
self._year,
|
|
100
|
+
self._month,
|
|
101
|
+
self._day_of_month,
|
|
102
|
+
self._hours,
|
|
103
|
+
self._minutes,
|
|
104
|
+
self._seconds,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def day_of_month(self):
|
|
109
|
+
"""day_of_month (int): day of month, 1 through 31."""
|
|
110
|
+
return self._day_of_month
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def day_of_week(self):
|
|
114
|
+
"""day_of_week (int): day of week, 0 through 6."""
|
|
115
|
+
return self._day_of_week
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def hours(self):
|
|
119
|
+
"""Hours (int): hours, 0 through 23."""
|
|
120
|
+
return self._hours
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def milliseconds(self):
|
|
124
|
+
"""Milliseconds (int): milliseconds, 0 through 999."""
|
|
125
|
+
return self._milliseconds
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def minutes(self):
|
|
129
|
+
"""Minutes (int): minutes, 0 through 59."""
|
|
130
|
+
return self._minutes
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def month(self):
|
|
134
|
+
"""Month (int): month of year, 1 through 12."""
|
|
135
|
+
return self._month
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def seconds(self):
|
|
139
|
+
"""Seconds (int): seconds, 0 through 59."""
|
|
140
|
+
return self._seconds
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def year(self):
|
|
144
|
+
"""Year (int): year, 1601 through 30827."""
|
|
145
|
+
return self._year
|
|
146
|
+
|
|
147
|
+
def _GetNormalizedTimestamp(self):
|
|
148
|
+
"""Retrieves the normalized timestamp.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
decimal.Decimal: normalized timestamp, which contains the number of
|
|
152
|
+
seconds since January 1, 1970 00:00:00 and a fraction of second used
|
|
153
|
+
for increased precision, or None if the normalized timestamp cannot be
|
|
154
|
+
determined.
|
|
155
|
+
"""
|
|
156
|
+
if self._normalized_timestamp is None:
|
|
157
|
+
if self._number_of_seconds is not None:
|
|
158
|
+
self._normalized_timestamp = (
|
|
159
|
+
decimal.Decimal(self._milliseconds)
|
|
160
|
+
/ definitions.MILLISECONDS_PER_SECOND
|
|
161
|
+
)
|
|
162
|
+
self._normalized_timestamp += decimal.Decimal(self._number_of_seconds)
|
|
163
|
+
|
|
164
|
+
if self._time_zone_offset:
|
|
165
|
+
self._normalized_timestamp -= self._time_zone_offset * 60
|
|
166
|
+
|
|
167
|
+
return self._normalized_timestamp
|
|
168
|
+
|
|
169
|
+
def CopyFromDateTimeString(self, time_string):
|
|
170
|
+
"""Copies a SYSTEMTIME structure from a date and time string.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
time_string (str): date and time value formatted as:
|
|
174
|
+
YYYY-MM-DD hh:mm:ss.######[+-]##:##
|
|
175
|
+
|
|
176
|
+
Where # are numeric digits ranging from 0 to 9 and the seconds
|
|
177
|
+
fraction can be either 3, 6 or 9 digits. The time of day, seconds
|
|
178
|
+
fraction and time zone offset are optional. The default time zone
|
|
179
|
+
is UTC.
|
|
180
|
+
|
|
181
|
+
Raises:
|
|
182
|
+
ValueError: if the date string is invalid or not supported.
|
|
183
|
+
"""
|
|
184
|
+
date_time_values = self._CopyDateTimeFromString(time_string)
|
|
185
|
+
|
|
186
|
+
year = date_time_values.get("year", 0)
|
|
187
|
+
month = date_time_values.get("month", 0)
|
|
188
|
+
day_of_month = date_time_values.get("day_of_month", 0)
|
|
189
|
+
hours = date_time_values.get("hours", 0)
|
|
190
|
+
minutes = date_time_values.get("minutes", 0)
|
|
191
|
+
seconds = date_time_values.get("seconds", 0)
|
|
192
|
+
nanoseconds = date_time_values.get("nanoseconds", 0)
|
|
193
|
+
time_zone_offset = date_time_values.get("time_zone_offset")
|
|
194
|
+
|
|
195
|
+
milliseconds, _ = divmod(nanoseconds, definitions.NANOSECONDS_PER_MILLISECOND)
|
|
196
|
+
|
|
197
|
+
if year < 1601 or year > 30827:
|
|
198
|
+
raise ValueError(f"Unsupported year value: {year:d}.")
|
|
199
|
+
|
|
200
|
+
self._normalized_timestamp = None
|
|
201
|
+
self._number_of_seconds = self._GetNumberOfSecondsFromElements(
|
|
202
|
+
year, month, day_of_month, hours, minutes, seconds
|
|
203
|
+
)
|
|
204
|
+
self._time_zone_offset = time_zone_offset
|
|
205
|
+
|
|
206
|
+
self._year = year
|
|
207
|
+
self._month = month
|
|
208
|
+
self._day_of_month = day_of_month
|
|
209
|
+
# TODO: calculate day of week on demand.
|
|
210
|
+
self._day_of_week = None
|
|
211
|
+
self._hours = hours
|
|
212
|
+
self._minutes = minutes
|
|
213
|
+
self._seconds = seconds
|
|
214
|
+
self._milliseconds = milliseconds
|
|
215
|
+
|
|
216
|
+
def CopyToDateTimeString(self):
|
|
217
|
+
"""Copies the SYSTEMTIME structure to a date and time string.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.###" or
|
|
221
|
+
None if the number of seconds is missing.
|
|
222
|
+
"""
|
|
223
|
+
if self._number_of_seconds is None:
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
f"{self._year:04d}-{self._month:02d}-{self._day_of_month:02d} "
|
|
228
|
+
f"{self._hours:02d}:{self._minutes:02d}:{self._seconds:02d}"
|
|
229
|
+
f".{self._milliseconds:03d}"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
def CopyToSerializableDict(self):
|
|
233
|
+
"""Copies the date time value to a serializable dictionary.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
dict[str, object]: serializable dictionary.
|
|
237
|
+
"""
|
|
238
|
+
serializable_dict = self._CreateSerializableDict()
|
|
239
|
+
|
|
240
|
+
serializable_dict["system_time_tuple"] = (
|
|
241
|
+
self._year,
|
|
242
|
+
self._month,
|
|
243
|
+
self._day_of_week,
|
|
244
|
+
self._day_of_month,
|
|
245
|
+
self._hours,
|
|
246
|
+
self._minutes,
|
|
247
|
+
self._seconds,
|
|
248
|
+
self._milliseconds,
|
|
249
|
+
)
|
|
250
|
+
return serializable_dict
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
factory.Factory.RegisterDateTimeValues(Systemtime)
|