Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use nanoseconds in string to date time conversions #270

Merged
merged 3 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <log2timeline-maintainers@googlegroups.com> Tue, 05 Dec 2023 05:27:13 +0100
-- Log2Timeline maintainers <log2timeline-maintainers@googlegroups.com> 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.######[+-]##:##
joachimmetz marked this conversation as resolved.
Show resolved Hide resolved

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)
joachimmetz marked this conversation as resolved.
Show resolved Hide resolved

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
joachimmetz marked this conversation as resolved.
Show resolved Hide resolved

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)
joachimmetz marked this conversation as resolved.
Show resolved Hide resolved

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