Skip to content

Commit

Permalink
Changes to use nanoseconds in string to date time conversions (log2ti…
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimmetz authored Dec 27, 2023
1 parent 651a305 commit eae9726
Show file tree
Hide file tree
Showing 26 changed files with 277 additions and 162 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test_tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ jobs:
strategy:
matrix:
include:
- python-version: '3.11'
toxenv: 'py311,coverage'
- python-version: '3.10'
toxenv: 'coverage'
container:
image: ubuntu:22.04
steps:
Expand Down
4 changes: 2 additions & 2 deletions config/dpkg/changelog
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
dfdatetime (20231205-1) unstable; urgency=low
dfdatetime (20231210-1) unstable; urgency=low

* Auto-generated

-- Log2Timeline maintainers <[email protected]> Tue, 05 Dec 2023 05:27:13 +0100
-- Log2Timeline maintainers <[email protected]> Sun, 10 Dec 2023 10:02:43 +0100
2 changes: 1 addition & 1 deletion dfdatetime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@
from dfdatetime import webkit_time


__version__ = '20231205'
__version__ = '20231210'
2 changes: 1 addition & 1 deletion dfdatetime/apfs_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand Down
7 changes: 3 additions & 4 deletions dfdatetime/cocoa_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand All @@ -93,16 +93,15 @@ def CopyFromDateTimeString(self, time_string):
hours = date_time_values.get('hours', 0)
minutes = date_time_values.get('minutes', 0)
seconds = date_time_values.get('seconds', 0)
microseconds = date_time_values.get('microseconds', None)
nanoseconds = date_time_values.get('nanoseconds', 0)
time_zone_offset = date_time_values.get('time_zone_offset', 0)

timestamp = self._GetNumberOfSecondsFromElements(
year, month, day_of_month, hours, minutes, seconds)
timestamp += self._COCOA_TO_POSIX_BASE

timestamp = float(timestamp)
if microseconds is not None:
timestamp += float(microseconds) / definitions.MICROSECONDS_PER_SECOND
timestamp += float(nanoseconds) / definitions.NANOSECONDS_PER_SECOND

self._normalized_timestamp = None
self._timestamp = timestamp
Expand Down
7 changes: 5 additions & 2 deletions dfdatetime/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

DAYS_PER_MONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)

SECONDS_PER_DAY = 24 * 60 * 60
SECONDS_PER_DAY = 86400

DECISECONDS_PER_SECOND = 10

Expand All @@ -20,8 +20,11 @@
MICROSECONDS_PER_DECISECOND = 100000
MICROSECONDS_PER_MILLISECOND = 1000

NANOSECONDS_PER_MICROSECOND = 1000
NANOSECONDS_PER_DAY = 86400000000000
NANOSECONDS_PER_SECOND = 1000000000
NANOSECONDS_PER_DECISECOND = 100000000
NANOSECONDS_PER_MILLISECOND = 1000000
NANOSECONDS_PER_MICROSECOND = 1000

PRECISION_1_DAY = '1d'
PRECISION_1_HOUR = '1h'
Expand Down
7 changes: 3 additions & 4 deletions dfdatetime/delphi_date_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand All @@ -101,7 +101,7 @@ def CopyFromDateTimeString(self, time_string):
hours = date_time_values.get('hours', 0)
minutes = date_time_values.get('minutes', 0)
seconds = date_time_values.get('seconds', 0)
microseconds = date_time_values.get('microseconds', None)
nanoseconds = date_time_values.get('nanoseconds', 0)
time_zone_offset = date_time_values.get('time_zone_offset', 0)

if year > 9999:
Expand All @@ -112,8 +112,7 @@ def CopyFromDateTimeString(self, time_string):

timestamp = float(timestamp) / definitions.SECONDS_PER_DAY
timestamp += self._DELPHI_TO_POSIX_BASE
if microseconds is not None:
timestamp += float(microseconds) / definitions.MICROSECONDS_PER_DAY
timestamp += float(nanoseconds) / definitions.NANOSECONDS_PER_DAY

self._normalized_timestamp = None
self._timestamp = timestamp
Expand Down
11 changes: 6 additions & 5 deletions dfdatetime/dotnet_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand All @@ -94,18 +94,19 @@ def CopyFromDateTimeString(self, time_string):
hours = date_time_values.get('hours', 0)
minutes = date_time_values.get('minutes', 0)
seconds = date_time_values.get('seconds', 0)
microseconds = date_time_values.get('microseconds', 0)
nanoseconds = date_time_values.get('nanoseconds', 0)
time_zone_offset = date_time_values.get('time_zone_offset', 0)

if year > 9999:
raise ValueError(f'Unsupported year value: {year:d}.')

nanoseconds, _ = divmod(nanoseconds, 100)

timestamp = self._GetNumberOfSecondsFromElements(
year, month, day_of_month, hours, minutes, seconds)
timestamp += self._DOTNET_TO_POSIX_BASE
timestamp *= definitions.MICROSECONDS_PER_SECOND
timestamp += microseconds
timestamp *= self._100_NANOSECONDS_PER_MICROSECOND
timestamp *= self._100_NANOSECONDS_PER_SECOND
timestamp += nanoseconds

self._normalized_timestamp = None
self._timestamp = timestamp
Expand Down
10 changes: 8 additions & 2 deletions dfdatetime/fake_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
"""
Expand All @@ -81,12 +81,18 @@ def CopyFromDateTimeString(self, time_string):
hours = date_time_values.get('hours', 0)
minutes = date_time_values.get('minutes', 0)
seconds = date_time_values.get('seconds', 0)
nanoseconds = date_time_values.get('nanoseconds', None)
time_zone_offset = date_time_values.get('time_zone_offset', 0)

self._normalized_timestamp = None
self._number_of_seconds = self._GetNumberOfSecondsFromElements(
year, month, day_of_month, hours, minutes, seconds)
self._microseconds = date_time_values.get('microseconds', None)

if nanoseconds is None:
self._microseconds = None
else:
self._microseconds, _ = divmod(nanoseconds, 1000)

self._time_zone_offset = time_zone_offset

def CopyToDateTimeString(self):
Expand Down
13 changes: 6 additions & 7 deletions dfdatetime/fat_date_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand Down Expand Up @@ -250,7 +250,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand All @@ -265,20 +265,19 @@ def CopyFromDateTimeString(self, time_string):
hours = date_time_values.get('hours', 0)
minutes = date_time_values.get('minutes', 0)
seconds = date_time_values.get('seconds', 0)
microseconds = date_time_values.get('microseconds', 0)
nanoseconds = date_time_values.get('nanoseconds', 0)
time_zone_offset = date_time_values.get('time_zone_offset', 0)

if year < 1980 or year > (1980 + 0x7f):
raise ValueError(f'Year value not supported: {year!s}.')

milliseconds, _ = divmod(nanoseconds, 10000000)

timestamp = self._GetNumberOfSecondsFromElements(
year, month, day_of_month, hours, minutes, seconds)
timestamp -= self._FAT_DATE_TO_POSIX_BASE
timestamp *= 100

if microseconds:
milliseconds, _ = divmod(microseconds, 10000)
timestamp += milliseconds
timestamp += milliseconds

self._timestamp = timestamp
self._time_zone_offset = time_zone_offset
Expand Down
10 changes: 6 additions & 4 deletions dfdatetime/filetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand All @@ -98,17 +98,19 @@ def CopyFromDateTimeString(self, time_string):
hours = date_time_values.get('hours', 0)
minutes = date_time_values.get('minutes', 0)
seconds = date_time_values.get('seconds', 0)
nanoseconds = date_time_values.get('nanoseconds', 0)
time_zone_offset = date_time_values.get('time_zone_offset', 0)

if year < 1601:
raise ValueError(f'Year value not supported: {year!s}.')

nanoseconds, _ = divmod(nanoseconds, 100)

timestamp = self._GetNumberOfSecondsFromElements(
year, month, day_of_month, hours, minutes, seconds)
timestamp += self._FILETIME_TO_POSIX_BASE
timestamp *= definitions.MICROSECONDS_PER_SECOND
timestamp += date_time_values.get('microseconds', 0)
timestamp *= self._100_NANOSECONDS_PER_MICROSECOND
timestamp *= self._100_NANOSECONDS_PER_SECOND
timestamp += nanoseconds

self._normalized_timestamp = None
self._timestamp = timestamp
Expand Down
5 changes: 2 additions & 3 deletions dfdatetime/golang_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand All @@ -175,7 +175,7 @@ def CopyFromDateTimeString(self, time_string):
hours = date_time_values.get('hours', 0)
minutes = date_time_values.get('minutes', 0)
seconds = date_time_values.get('seconds', 0)
microseconds = date_time_values.get('microseconds', 0)
nanoseconds = date_time_values.get('nanoseconds', 0)
time_zone_offset = date_time_values.get('time_zone_offset', 0)

if year < 0:
Expand All @@ -185,7 +185,6 @@ def CopyFromDateTimeString(self, time_string):
year, month, day_of_month, hours, minutes, seconds)

seconds += self._GOLANG_TO_POSIX_BASE
nanoseconds = microseconds * definitions.NANOSECONDS_PER_MICROSECOND

self._normalized_timestamp = None
self._number_of_seconds = seconds
Expand Down
2 changes: 1 addition & 1 deletion dfdatetime/hfs_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand Down
26 changes: 14 additions & 12 deletions dfdatetime/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,13 +342,13 @@ def _CopyDateTimeFromString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Returns:
dict[str, int]: date and time values, such as year, month, day of month,
hours, minutes, seconds, microseconds, time zone offset in minutes.
hours, minutes, seconds, nanoseconds, time zone offset in minutes.
Raises:
ValueError: if the time string is invalid or not supported.
Expand All @@ -372,7 +372,7 @@ def _CopyDateTimeFromString(self, time_string):
raise ValueError(
'Invalid time string - space missing as date and time separator.')

hours, minutes, seconds, microseconds, time_zone_offset = (
hours, minutes, seconds, nanoseconds, time_zone_offset = (
self._CopyTimeFromString(time_string[11:]))

date_time_values = {
Expand All @@ -383,8 +383,8 @@ def _CopyDateTimeFromString(self, time_string):
'minutes': minutes,
'seconds': seconds}

if microseconds is not None:
date_time_values['microseconds'] = microseconds
if nanoseconds is not None:
date_time_values['nanoseconds'] = nanoseconds
if time_zone_offset is not None:
date_time_values['time_zone_offset'] = time_zone_offset

Expand All @@ -398,11 +398,11 @@ def _CopyTimeFromString(self, time_string):
hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The seconds fraction and
fraction can be either 3, 6 or 9 digits. The seconds fraction and
time zone offset are optional.
Returns:
tuple[int, int, int, int, int]: hours, minutes, seconds, microseconds,
tuple[int, int, int, int, int]: hours, minutes, seconds, nanoseconds,
time zone offset in minutes.
Raises:
Expand Down Expand Up @@ -442,7 +442,7 @@ def _CopyTimeFromString(self, time_string):
if seconds not in range(0, 60):
raise ValueError(f'Seconds value: {seconds:d} out of bounds.')

microseconds = None
nanoseconds = None
time_zone_offset = None

time_zone_string_index = 8
Expand All @@ -459,7 +459,7 @@ def _CopyTimeFromString(self, time_string):

if time_string_length > 8 and time_string[8] == '.':
time_fraction_length = time_zone_string_index - 9
if time_fraction_length not in (3, 6):
if time_fraction_length not in (3, 6, 9):
raise ValueError('Invalid time string.')

try:
Expand All @@ -469,9 +469,11 @@ def _CopyTimeFromString(self, time_string):
raise ValueError('Unable to parse time fraction.')

if time_fraction_length == 3:
time_fraction *= 1000000
elif time_fraction_length == 6:
time_fraction *= 1000

microseconds = time_fraction
nanoseconds = time_fraction

if time_zone_string_index < time_string_length:
if (time_string_length - time_zone_string_index != 6 or
Expand Down Expand Up @@ -502,7 +504,7 @@ def _CopyTimeFromString(self, time_string):
if time_string[time_zone_string_index] == '-':
time_zone_offset = -time_zone_offset

return hours, minutes, seconds, microseconds, time_zone_offset
return hours, minutes, seconds, nanoseconds, time_zone_offset

def _GetDateValues(
self, number_of_days, epoch_year, epoch_month, epoch_day_of_month):
Expand Down Expand Up @@ -870,7 +872,7 @@ def CopyFromDateTimeString(self, time_string):
YYYY-MM-DD hh:mm:ss.######[+-]##:##
Where # are numeric digits ranging from 0 to 9 and the seconds
fraction can be either 3 or 6 digits. The time of day, seconds
fraction can be either 3, 6 or 9 digits. The time of day, seconds
fraction and time zone offset are optional. The default time zone
is UTC.
Expand Down
Loading

0 comments on commit eae9726

Please sign in to comment.