Skip to content

feat(core): decode DOS-packed date/time fields on Fat12Entry#8

Open
evanofficial wants to merge 2 commits into
mainfrom
feat/fat-datetime-helpers
Open

feat(core): decode DOS-packed date/time fields on Fat12Entry#8
evanofficial wants to merge 2 commits into
mainfrom
feat/fat-datetime-helpers

Conversation

@evanofficial

@evanofficial evanofficial commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Closes #2.

What

Fat12Entry exposes three raw DOS-packed integers — createdDate, modifiedDate, modifiedTime — straight from the on-disk directory entry. This adds pure helpers to decode them into java.time types, so callers no longer need to know the FAT bit-packing.

fun decodeFatDate(packed: Int): LocalDate?   // null for the 0 sentinel / invalid dates
fun decodeFatTime(packed: Int): LocalTime    // 2-second resolution

fun Fat12Entry.createdDateOrNull(): LocalDate?
fun Fat12Entry.modifiedDateOrNull(): LocalDate?
fun Fat12Entry.modifiedDateTimeOrNull(): LocalDateTime?

Decisions worth a look

  • Invalid input. decodeFatDate returns null for the 0x0000 sentinel and for any bit pattern that isn't a valid calendar date (corrupt month/day) — directory listings shouldn't throw on a bad entry. decodeFatTime is strict: a corrupt field (e.g. a seconds-field of 30/31 → 60/62s) throws DateTimeException, documented in the KDoc. The composite modifiedDateTimeOrNull() swallows that into null so the entry-level accessor never throws.
  • Naming uses the …OrNull() suffix to match Kotlin convention and avoid colliding with the raw Int properties. Open to other names.

Changes

  • New FatDateTime.kt (free functions + Fat12Entry extensions), KDoc citing the bit layout.
  • Raw Int fields on Fat12Entry unchanged (additive only).
  • FatDateTimeTest: hand-computed fixtures (2026-06-18, the engine DEFAULT_DOS_DATE/TIME, year-1980 offset 0), a full round-trip over the representable time space, and edge cases (zero date → null, invalid month → null, even-seconds, high-bit masking).

Testing

./gradlew :core:test green.

Summary by CodeRabbit

  • New Features

    • Added FAT date/time decoding utilities to convert packed FAT fields into standard date/time values.
    • Entry timestamp accessors now return blank/nullable results when raw date/time values are missing or invalid.
  • Tests

    • Added unit tests covering valid decoding, sentinel handling, invalid component behavior, high-bit ignoring, and accessor null-safety.

Add pure helpers that decode the raw DOS-packed 16-bit date/time integers
carried on Fat12Entry (createdDate, modifiedDate, modifiedTime) into
java.time types:

- decodeFatDate(packed): LocalDate?  — null for the 0 sentinel and any
  bit pattern that is not a valid calendar date
- decodeFatTime(packed): LocalTime   — 2-second resolution
- Fat12Entry.createdDateOrNull() / modifiedDateOrNull() / modifiedDateTimeOrNull()

KDoc cites the FAT bit layout. The raw Int fields on Fat12Entry are
unchanged (additive only). Adds FatDateTimeTest with hand-computed fixtures
(2026-06-18, the engine DEFAULT_DOS_DATE/TIME, year-1980 offset 0), a
full round-trip over the representable time space, and edge cases (zero
date -> null, invalid month -> null, even-seconds, high-bit masking).

Fixes #2
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Too big: expected string to have <=250 characters at "tone_instructions"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5880e953-a7fb-40f0-91db-aa537bc8c609

📥 Commits

Reviewing files that changed from the base of the PR and between cb38e88 and 6271e7b.

📒 Files selected for processing (1)
  • core/src/test/kotlin/com/ams/fat12ex/core/FatDateTimeTest.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/src/test/kotlin/com/ams/fat12ex/core/FatDateTimeTest.kt

📝 Walkthrough

Walkthrough

Adds pure Kotlin helpers to decode FAT12 packed date and time fields into java.time values, plus Fat12Entry accessors for created, modified, and modified date-time values. A new test suite covers decoding, edge cases, and null handling.

FAT12 date/time decoding

Layer / File(s) Summary
Decode functions and Fat12Entry accessors
core/src/main/kotlin/com/ams/fat12ex/core/FatDateTime.kt
decodeFatDate masks to 16 bits, returns null for 0x0000 and invalid dates, and decodeFatTime extracts FAT bitfields with 2-second resolution. Fat12Entry extensions decode created and modified dates, plus modified date-time with null handling for missing or invalid values.
Test suite
core/src/test/kotlin/com/ams/fat12ex/core/FatDateTimeTest.kt
Covers decodeFatDate, decodeFatTime, and Fat12Entry accessor behavior for valid packed values, zero sentinels, invalid components, high-bit masking, and corrupted modified-time handling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I hopped through bits of date and time,
and turned old FAT fields into rhyme.
A nibble here, a shift there too,
now dates and clocks come shining through!
Thump-thump, the bunny approves ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: decoding DOS-packed FAT date/time fields on Fat12Entry.
Linked Issues check ✅ Passed The PR adds the requested pure decode helpers, additive entry accessors, KDoc, and tests for the specified edge cases.
Out of Scope Changes check ✅ Passed All changes are directly related to FAT date/time decoding and its tests; no unrelated scope is evident.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fat-datetime-helpers

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core/src/test/kotlin/com/ams/fat12ex/core/FatDateTimeTest.kt (1)

102-139: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for corrupt modifiedTime values.

The suite never exercises the branch that differentiates decodeFatTime() from modifiedDateTimeOrNull(): an invalid packed time should throw in the decoder but yield null in the entry accessor. A case like modifiedTime = 0x001E (seconds-field 30 → 60s) would pin that contract down.

Proposed test
     `@Test`
     fun entryAccessors_zeroDateFieldsAreNull() {
         val entry = Fat12Entry(
             name = "EMPTY.TXT",
             shortName = "EMPTY   TXT",
             isDirectory = false,
             size = 0,
             firstCluster = 0,
             attributes = 0,
             createdDate = 0,
             modifiedDate = 0,
             modifiedTime = 0,
         )
         assertNull(entry.createdDateOrNull())
         assertNull(entry.modifiedDateOrNull())
         assertNull(entry.modifiedDateTimeOrNull())
     }
+
+    `@Test`
+    fun entryAccessors_invalidModifiedTimeIsNull() {
+        val entry = Fat12Entry(
+            name = "BROKEN.TXT",
+            shortName = "BROKEN  TXT",
+            isDirectory = false,
+            size = 1,
+            firstCluster = 2,
+            attributes = 0x20,
+            createdDate = 0x5CD2,
+            modifiedDate = 0x58C1,
+            modifiedTime = 0x001E,
+        )
+
+        assertNull(entry.modifiedDateTimeOrNull())
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/src/test/kotlin/com/ams/fat12ex/core/FatDateTimeTest.kt` around lines
102 - 139, Add a test in FatDateTimeTest that covers a corrupt packed
modifiedTime value and verifies the contract between decodeFatTime() and
Fat12Entry.modifiedDateTimeOrNull(). Use a Fat12Entry with a valid date but an
invalid modifiedTime such as 0x001E, assert that the low-level decoder rejects
it, and assert that modifiedDateTimeOrNull() returns null for the same entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@core/src/test/kotlin/com/ams/fat12ex/core/FatDateTimeTest.kt`:
- Around line 102-139: Add a test in FatDateTimeTest that covers a corrupt
packed modifiedTime value and verifies the contract between decodeFatTime() and
Fat12Entry.modifiedDateTimeOrNull(). Use a Fat12Entry with a valid date but an
invalid modifiedTime such as 0x001E, assert that the low-level decoder rejects
it, and assert that modifiedDateTimeOrNull() returns null for the same entry.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3301914f-011e-4a13-b6e2-47bf918bd854

📥 Commits

Reviewing files that changed from the base of the PR and between d5f998f and cb38e88.

📒 Files selected for processing (2)
  • core/src/main/kotlin/com/ams/fat12ex/core/FatDateTime.kt
  • core/src/test/kotlin/com/ams/fat12ex/core/FatDateTimeTest.kt

Add the case that distinguishes decodeFatTime() from modifiedDateTimeOrNull():
a packed time of 0x001E (seconds-field 30 -> 60s) is rejected by the
low-level decoder (DateTimeException) but yields null from the entry-level
accessor. Pins down that contract.

Addresses CodeRabbit review feedback on this PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add helpers to decode DOS-packed date/time fields on Fat12Entry

1 participant