Skip to content
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
51 changes: 51 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Check source.
name: lint
on:
pull_request:
branches:
- main
push:
branches:
- main
permissions: read-all
jobs:
black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check format of Python code
uses: psf/black@stable
with:
options: "--check"
src: "."
pylint:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.14']
container:
image: ubuntu:26.04
steps:
- uses: actions/checkout@v6
- name: Set up container
env:
DEBIAN_FRONTEND: noninteractive
run: |
apt-get update -q
apt-get install -y libterm-readline-gnu-perl locales software-properties-common
locale-gen en_US.UTF-8
ln -f -s /usr/share/zoneinfo/UTC /etc/localtime
- name: Install dependencies
env:
DEBIAN_FRONTEND: noninteractive
run: |
add-apt-repository -y universe
add-apt-repository -y ppa:deadsnakes/ppa
add-apt-repository -y ppa:gift/dev
apt-get update -q
apt-get install -y build-essential git pkg-config python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-pip python3-setuptools tox
- name: Run linter
env:
LANG: en_US.UTF-8
run: |
tox -e pylint
31 changes: 0 additions & 31 deletions .github/workflows/test_tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,34 +85,3 @@ jobs:
uses: codecov/codecov-action@v6
with:
token: ${{ secrets.CODECOV_TOKEN }}
lint:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.14']
container:
image: ubuntu:26.04
steps:
- uses: actions/checkout@v6
- name: Set up container
env:
DEBIAN_FRONTEND: noninteractive
run: |
apt-get update -q
apt-get install -y libterm-readline-gnu-perl locales software-properties-common
locale-gen en_US.UTF-8
ln -f -s /usr/share/zoneinfo/UTC /etc/localtime
- name: Install dependencies
env:
DEBIAN_FRONTEND: noninteractive
run: |
add-apt-repository -y universe
add-apt-repository -y ppa:deadsnakes/ppa
add-apt-repository -y ppa:gift/dev
apt-get update -q
apt-get install -y build-essential git pkg-config python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-pip python3-setuptools tox
- name: Run linter
env:
LANG: en_US.UTF-8
run: |
tox -e lint
7 changes: 3 additions & 4 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -358,12 +358,11 @@ indent-after-paren=4

# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
# indent-string=' '
indent-string=' '
indent-string=' '

# Maximum number of characters on a single line.
# max-line-length=100
max-line-length=80
max-line-length=88

# Maximum number of lines in a module.
max-module-lines=1000
Expand Down Expand Up @@ -599,7 +598,7 @@ spelling-store-unknown-words=no

# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
check-quote-consistency=yes

# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
Expand Down
3 changes: 1 addition & 2 deletions dfdatetime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@
from dfdatetime import uuid_time
from dfdatetime import webkit_time


__version__ = '20260513'
__version__ = "20260513"
128 changes: 69 additions & 59 deletions dfdatetime/apfs_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,69 +8,79 @@


class APFSTime(posix_time.PosixTimeInNanoseconds):
"""Apple File System (APFS) timestamp.
"""Apple File System (APFS) timestamp.

The APFS timestamp is a signed 64-bit integer that contains the number of
nanoseconds since 1970-01-01 00:00:00.
The APFS timestamp is a signed 64-bit integer that contains the number of
nanoseconds since 1970-01-01 00:00:00.

Attributes:
is_local_time (bool): True if the date and time value is in local time.
"""

def _GetNormalizedTimestamp(self):
"""Retrieves the normalized timestamp.

Returns:
decimal.Decimal: normalized timestamp, which contains the number of
seconds since January 1, 1970 00:00:00 and a fraction of second used
for increased precision, or None if the normalized timestamp cannot be
determined.
"""
if self._normalized_timestamp is None:
if (self._timestamp is not None and self._timestamp >= self._INT64_MIN and
self._timestamp <= self._INT64_MAX):
self._normalized_timestamp = (
decimal.Decimal(self._timestamp) /
definitions.NANOSECONDS_PER_SECOND)

if self._time_zone_offset:
self._normalized_timestamp -= self._time_zone_offset * 60

return self._normalized_timestamp

def CopyFromDateTimeString(self, time_string):
"""Copies a APFS timestamp from a date and time string.

Args:
time_string (str): date and time value formatted as:
YYYY-MM-DD hh:mm:ss.######[+-]##:##

Where # are numeric digits ranging from 0 to 9 and the 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.

Raises:
ValueError: if the date and time value is not supported.
"""
super()._CopyFromDateTimeString(time_string)

if (self._timestamp is None or self._timestamp < self._INT64_MIN or
self._timestamp > self._INT64_MAX):
raise ValueError('Date time value not supported.')

def CopyToDateTimeString(self):
"""Copies the APFS timestamp to a date and time string.

Returns:
str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#########" or
None if the timestamp is missing or invalid.
Attributes:
is_local_time (bool): True if the date and time value is in local time.
"""
if (self._timestamp is None or self._timestamp < self._INT64_MIN or
self._timestamp > self._INT64_MAX):
return None

return super()._CopyToDateTimeString()
def _GetNormalizedTimestamp(self):
"""Retrieves the normalized timestamp.

Returns:
decimal.Decimal: normalized timestamp, which contains the number of
seconds since January 1, 1970 00:00:00 and a fraction of second used
for increased precision, or None if the normalized timestamp cannot be
determined.
"""
if self._normalized_timestamp is None:
if (
self._timestamp is not None
and self._timestamp >= self._INT64_MIN
and self._timestamp <= self._INT64_MAX
):
self._normalized_timestamp = (
decimal.Decimal(self._timestamp)
/ definitions.NANOSECONDS_PER_SECOND
)

if self._time_zone_offset:
self._normalized_timestamp -= self._time_zone_offset * 60

return self._normalized_timestamp

def CopyFromDateTimeString(self, time_string):
"""Copies a APFS timestamp from a date and time string.

Args:
time_string (str): date and time value formatted as:
YYYY-MM-DD hh:mm:ss.######[+-]##:##

Where # are numeric digits ranging from 0 to 9 and the 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.

Raises:
ValueError: if the date and time value is not supported.
"""
super()._CopyFromDateTimeString(time_string)

if (
self._timestamp is None
or self._timestamp < self._INT64_MIN
or self._timestamp > self._INT64_MAX
):
raise ValueError("Date time value not supported.")

def CopyToDateTimeString(self):
"""Copies the APFS timestamp to a date and time string.

Returns:
str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#########" or
None if the timestamp is missing or invalid.
"""
if (
self._timestamp is None
or self._timestamp < self._INT64_MIN
or self._timestamp > self._INT64_MAX
):
return None

return super()._CopyToDateTimeString()


factory.Factory.RegisterDateTimeValues(APFSTime)
Loading
Loading