Skip to content
Open
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
8 changes: 4 additions & 4 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v6

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -53,7 +53,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v4

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
Expand All @@ -67,4 +67,4 @@ jobs:
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v4
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Build wheels
uses: pypa/cibuildwheel@v2.22
Expand All @@ -30,8 +30,8 @@ jobs:
needs: [tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
- run: uv build --sdist
- uses: actions/upload-artifact@v4
with:
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v7
with:
cache-suffix: ${{ matrix.python-version }}

- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
Expand All @@ -28,5 +30,5 @@ jobs:
run: uv run --python ${{ matrix.python-version }} pytest --cov=iscc_core --cov-report=xml -q tests

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v6
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [1.3.1] - 2026-04-30

- Improved human-readable representation of ISCC-IDv1 in `iscc_explain` to use ISO 8601 UTC timestamp and labeled `HUB_<id>` field (e.g. `ID-REALM_1-V1-64-2026-04-30T08:38:19.376583Z-HUB_1`)

## [1.3.0] - 2026-03-02

- Added `meta_trim_meta` option to limit decoded `meta` payload size in `gen_meta_code_v0` (Fixes #132)
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [1.3.1] - 2026-04-30

- Improved human-readable representation of ISCC-IDv1 in `iscc_explain` to use ISO 8601 UTC timestamp and labeled `HUB_<id>` field (e.g. `ID-REALM_1-V1-64-2026-04-30T08:38:19.376583Z-HUB_1`)

## [1.3.0] - 2026-03-02

- Added `meta_trim_meta` option to limit decoded `meta` payload size in `gen_meta_code_v0` (Fixes #132)
Expand Down
12 changes: 9 additions & 3 deletions iscc_core/codec.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import datetime
import math
import uvarint
from typing import List, Tuple
Expand Down Expand Up @@ -513,12 +514,17 @@ def iscc_explain(iscc):
if fields[0] == MT.ID:
# Special handling for ISCC-IDv1
if fields[2] == VS.V1:
# For IDv1, format as ID-REALM_<id>-V1-64-<timestamp>-<serverid>
# For IDv1, format as ID-REALM_<id>-V1-64-<iso8601-timestamp>-HUB_<hub_id>
realm_id = fields[1]
digest_int = int.from_bytes(fields[-1], byteorder="big")
server_id = digest_int & 0xFFF # Extract server_id (last 12 bits)
hub_id = digest_int & 0xFFF # Extract HUB-ID (last 12 bits)
timestamp = digest_int >> 12 # Extract timestamp (first 52 bits)
return f"ID-REALM_{realm_id}-V1-64-{timestamp}-{server_id}"
seconds, micros = divmod(timestamp, 1_000_000)
dt = datetime.datetime.fromtimestamp(seconds, datetime.timezone.utc).replace(
microsecond=micros
)
iso = dt.isoformat(timespec="microseconds").replace("+00:00", "Z")
return f"ID-REALM_{realm_id}-V1-64-{iso}-HUB_{hub_id}"

# Regular handling for ISCC-IDv0 with counter
counter_bytes = fields[-1][8:]
Expand Down
8 changes: 4 additions & 4 deletions tests/test_codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,13 +720,13 @@ def test_explain_maintype_iscc_id_counter():

def test_explain_maintype_iscc_idv1():
# Create a valid ISCC-IDv1 with known values
timestamp = 1647312000000000 # 2022-03-15 12:00:00 UTC in microseconds
timestamp = 1647312000000000 # 2022-03-15T02:40:00 UTC in microseconds
hub_id = 42
iscc_idv1 = ic.gen_iscc_id_v1(timestamp, hub_id, realm_id=0)["iscc"]

# Test explain function with IDv1
explanation = ic.iscc_explain(iscc_idv1)
assert explanation == f"ID-REALM_0-V1-64-{timestamp}-{hub_id}"
assert explanation == "ID-REALM_0-V1-64-2022-03-15T02:40:00.000000Z-HUB_42"


def test_encode_base32hex():
Expand Down Expand Up @@ -869,10 +869,10 @@ def test_iscc_validate_mscdi():


def test_explain_iscc_idv1():
timestamp = 1647312000000000 # 2022-03-15 12:00:00 UTC in microseconds
timestamp = 1647312000000000 # 2022-03-15T02:40:00 UTC in microseconds
hub_id = 42
iscc_idv1 = ic.gen_iscc_id_v1(timestamp, hub_id, realm_id=0)["iscc"]
expected = f"ID-REALM_0-V1-64-{timestamp}-{hub_id}"
expected = "ID-REALM_0-V1-64-2022-03-15T02:40:00.000000Z-HUB_42"
assert ic.iscc_explain(iscc_idv1) == expected


Expand Down
2 changes: 1 addition & 1 deletion tests/test_iscc_id_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_gen_iscc_id_v1_basic():
# Test with realm_id=0 (test network)
iscc_id = ic.gen_iscc_id_v1(timestamp=timestamp, hub_id=hub_id, realm_id=0)
assert iscc_id == {"iscc": "ISCC:MAIGC5KN3I6TCUBK"}
assert ic.iscc_explain(iscc_id["iscc"]) == "ID-REALM_0-V1-64-1714503123456789-42"
assert ic.iscc_explain(iscc_id["iscc"]) == "ID-REALM_0-V1-64-2024-04-30T18:52:03.456789Z-HUB_42"

# Test with realm_id=1 (operational network) - now the default
iscc_id_default = ic.gen_iscc_id(timestamp=timestamp, hub_id=hub_id)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def test_code_iscc_idv1():
# Test type identification
assert code.type_id == "ID-REALM_0-V1-64"
assert "ID-REALM_0-V1-64" in code.explain
assert f"{timestamp}-{hub_id}" in code.explain
assert code.explain.endswith(f"-HUB_{hub_id}")

# Test hash properties contain correct data
# Body should be 8 bytes: 52 bits timestamp + 12 bits HUB-ID
Expand Down
Loading
Loading