|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +A Symfony bundle (`digital-craftsman/date-time-precision`) providing precise date/time value objects as thin wrappers over PHP's `DateTimeImmutable`. The core idea: instead of using `DateTime` for everything, use specific value objects (`Moment`, `Date`, `Time`, `Month`, `Year`, `Day`, `Weekday`, `Weekdays`) that carry only the data they represent. |
| 8 | + |
| 9 | +All value objects are `final readonly` and immutable. The system assumes UTC internally; timezone-aware operations convert temporarily to the target timezone for the operation, then back to UTC. |
| 10 | + |
| 11 | +## Commands |
| 12 | + |
| 13 | +All commands run via Docker Compose (PHP 8.4 and 8.5 containers). Use `make` to see all targets. |
| 14 | + |
| 15 | +```bash |
| 16 | +# Run tests (both PHP versions) |
| 17 | +make php-tests |
| 18 | + |
| 19 | +# Run tests for a single PHP version |
| 20 | +make php-8.4-tests |
| 21 | +make php-8.5-tests |
| 22 | + |
| 23 | +# Run a single test file directly |
| 24 | +docker compose run --rm php-8.5 ./vendor/bin/phpunit tests/Moment/CompareToTest.php |
| 25 | + |
| 26 | +# Run a single test method |
| 27 | +docker compose run --rm php-8.5 ./vendor/bin/phpunit --filter test_method_name tests/Moment/CompareToTest.php |
| 28 | + |
| 29 | +# Code style fix + Psalm static analysis |
| 30 | +make php-code-validation |
| 31 | + |
| 32 | +# Mutation testing (requires 100% MSI) |
| 33 | +make php-mutation-testing |
| 34 | + |
| 35 | +# Full verification (code validation + tests + mutation testing) |
| 36 | +make verify |
| 37 | + |
| 38 | +# Install dependencies |
| 39 | +make install |
| 40 | +``` |
| 41 | + |
| 42 | +## Architecture |
| 43 | + |
| 44 | +### Value Object Hierarchy |
| 45 | + |
| 46 | +- **`Moment`** — wraps `DateTimeImmutable`, represents a specific point in time (always UTC). Primary entry point with `fromString()`, `fromStringInTimeZone()`, `fromDateTime()`. |
| 47 | +- **`Date`** — composed of `Month` + `Day`. No timezone, no time component. |
| 48 | +- **`Month`** — composed of `Year` + month int (1-12). |
| 49 | +- **`Year`** — single int value. |
| 50 | +- **`Day`** — single int value (1-31). |
| 51 | +- **`Time`** — hour/minute/second/microsecond. |
| 52 | +- **`Weekday`** / **`Weekdays`** — day-of-week enum-like values. |
| 53 | + |
| 54 | +All value objects implement `StringNormalizable` / `NullableStringDenormalizable` (from `digital-craftsman/self-aware-normalizers`) for Symfony serializer integration, and `NormalizableTypeWithSQLDeclaration` for Doctrine type integration. |
| 55 | + |
| 56 | +### Comparison Pattern |
| 57 | + |
| 58 | +Each value object has comparison methods (`isAfter`, `isBefore`, `isEqualTo`, etc.) and assertion methods (`mustBeAfter`, `mustBeBefore`, etc.) that throw typed exceptions from `src/Exception/`. The exception classes follow the naming pattern `{Type}Is{Condition}` (e.g., `MomentIsAfter`, `DateIsBefore`). Assertion methods accept a custom exception class parameter to allow domain-specific exceptions. |
| 59 | + |
| 60 | +### Timezone-Aware Operations |
| 61 | + |
| 62 | +`Moment` provides `*InTimeZone` method variants (e.g., `modifyInTimeZone`, `isBeforeInTimeZone`) that accept a `\DateTimeZone` parameter. These convert internally for the operation, keeping the result in UTC. |
| 63 | + |
| 64 | +### Integrations |
| 65 | + |
| 66 | +- **Doctrine types** in `src/Doctrine/` — one type per value object, auto-registered via `DoctrineTypeRegisterCompilerPass`. |
| 67 | +- **Symfony bundle** — `DateTimePrecisionBundle` with `DateTimePrecisionExtension`. |
| 68 | +- **Clock** — `Clock` interface with `SystemClock` (production) and `FrozenClock` (testing), autowired by the bundle. |
| 69 | + |
| 70 | +## Code Style |
| 71 | + |
| 72 | +- PHP CS Fixer with `@Symfony` ruleset (config in `.php-cs-fixer.dist.php`) |
| 73 | +- Test method names use **snake_case** (enforced by php-cs-fixer) |
| 74 | +- No Yoda comparisons |
| 75 | +- Psalm for static analysis (config in `psalm.xml`) |
| 76 | +- Mutation testing via Infection with **100% MSI required** |
| 77 | + |
| 78 | +## Test Structure |
| 79 | + |
| 80 | +Tests mirror the source structure: `tests/{ValueObject}/{MethodName}Test.php`. Custom test exception classes live in `tests/Test/Exception/`. |
0 commit comments