Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Date Value Objects Laravel Package

apie/date-value-objects

Date-related value objects for PHP/Apie that model only the date/time parts you need (LocalDate, Time, HourAndMinutes, UnixTimestamp, DateWithTimezone). Helps validate expected formats without relying on full DateTimeImmutable.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Package

    composer require apie/date-value-objects
    

    Ensure your composer.json targets PHP 8.1+ and Laravel 9+.

  2. First Use Case: LocalDate for User-Facing Dates Replace a Carbon instance with a LocalDate in a Laravel controller:

    use Apie\DateValueObjects\LocalDate;
    
    public function showEvent(DateTimeInterface $eventDate)
    {
        $localDate = LocalDate::fromDateTime($eventDate);
        return view('events.show', ['date' => $localDate]);
    }
    
  3. Key Starting Points

    • LocalDate: For dates without time (e.g., birthdays, event dates).
      $date = LocalDate::fromString('2023-12-31');
      $nextMonth = $date->nextMonth(); // Handles Feb 28/29 automatically
      
    • DateWithTimezone: For timezone-aware operations (e.g., scheduling).
      $tzDate = DateWithTimezone::fromAtom('2023-12-31T14:30:00+02:00');
      $utcTime = $tzDate->inTimezone('UTC');
      
    • UnixTimestamp: For immutable event timestamps (e.g., logs).
      $timestamp = UnixTimestamp::fromDateTime(new DateTime());
      
  4. Where to Look First

    • Documentation: README.md for class APIs.
    • Interfaces: Check WorksWithDays, WorksWithMonths for domain-specific methods.
    • Tests: Monorepo tests for edge cases (e.g., leap years).

Implementation Patterns

Core Workflows

1. Domain Modeling with Value Objects

  • Pattern: Replace DateTime in domain entities with value objects.
    // Before: Ambiguous
    class Event {
        public function __construct(public DateTimeImmutable $date) {}
    }
    
    // After: Explicit
    class Event {
        public function __construct(public LocalDate $date) {}
    }
    
  • Laravel Integration:
    • Use accessors to return value objects from Eloquent models:
      class Event extends Model {
          public function scheduledAt(): DateWithTimezone {
              return DateWithTimezone::fromDateTime($this->attributes['scheduled_at']);
          }
      }
      

2. Validation Layer

  • Form Requests: Validate inputs as value objects.
    use Apie\DateValueObjects\LocalDate;
    use Illuminate\Validation\Rule;
    
    public function rules(): array {
        return [
            'event_date' => ['required', Rule::function('date')->make(function ($attribute, $value) {
                return LocalDate::fromString($value); // Fails on invalid dates
            })],
        ];
    }
    
  • API Resources: Serialize value objects to JSON.
    public function toArray($request) {
        return [
            'date' => $this->event->date->toIsoString(),
        ];
    }
    

3. Timezone Handling

  • Pattern: Use DateWithTimezone for user-provided times.
    $userTimezone = 'America/New_York';
    $eventTime = DateWithTimezone::fromAtom('2023-12-31T14:00:00-05:00');
    $utcEvent = $eventTime->inTimezone('UTC'); // Convert for storage
    
  • Laravel Sync: Align with config('app.timezone'):
    $localTime = $utcEvent->inTimezone(config('app.timezone'));
    

4. Recurring Logic

  • Pattern: Leverage interfaces for recurring operations.
    $date = LocalDate::fromString('2023-01-31');
    $nextMonth = $date->nextMonth(); // Auto-adjusts for Feb 28/29
    $nextYear = $date->nextYear();
    

5. Database Integration

  • Storage: Store raw values (e.g., UnixTimestamp as BIGINT).
    // Migration
    Schema::create('events', function (Blueprint $table) {
        $table->id();
        $table->bigInteger('scheduled_at'); // UnixTimestamp
    });
    
    // Model
    public function setScheduledAtAttribute($value) {
        $this->attributes['scheduled_at'] = UnixTimestamp::fromDateTime($value)->value();
    }
    

Laravel-Specific Patterns

1. Eloquent Casting

  • Cast attributes to value objects:
    protected $casts = [
        'scheduled_at' => DateWithTimezone::class,
    ];
    
  • Custom Casting: Extend CastsAttributes for complex logic:
    use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
    
    class DateWithTimezoneCaster implements CastsAttributes {
        public function get($model, string $key, $value, array $attributes) {
            return DateWithTimezone::fromDateTime($value);
        }
        public function set($model, string $key, $value, array $attributes) {
            return $value->toDateTime();
        }
    }
    

2. API Request/Response

  • Request Parsing: Convert request data to value objects.
    public function __construct(
        private LocalDate $eventDate,
        private HourAndMinutes $startTime
    ) {
        $this->eventDate = LocalDate::fromString($this->request->event_date);
        $this->startTime = HourAndMinutes::fromString($this->request->start_time);
    }
    
  • Response Formatting: Use toIsoString() or toAtom() for consistency.
    return response()->json([
        'event' => [
            'date' => $event->date->toIsoString(),
            'time' => $event->time->toString(),
        ],
    ]);
    

3. Queue Jobs

  • Serialize value objects to arrays for queue storage:
    public function handle() {
        $timestamp = UnixTimestamp::fromDateTime(new DateTime());
        // Store $timestamp->value() in DB or queue
    }
    

4. Testing

  • Unit Tests: Assert value object behavior.
    public function testNextMonth() {
        $date = LocalDate::fromString('2023-01-31');
        $next = $date->nextMonth();
        $this->assertEquals('2023-02-28', $next->toIsoString());
    }
    
  • Feature Tests: Validate API responses.
    $response = $this->get('/events/1');
    $response->assertJsonStructure([
        'event' => [
            'date' => 'string', // ISO format
            'time' => 'string', // HH:MM format
        ],
    ]);
    

Gotchas and Tips

Pitfalls

1. Timezone Mismatches

  • Issue: DateWithTimezone assumes ATOM format (YYYY-MM-DDTHH:MM:SS±HH:MM). Invalid formats (e.g., YYYY-MM-DD HH:MM:SS) will throw exceptions.
  • Fix: Use DateWithTimezone::fromDateTime() with a pre-parsed DateTime:
    $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2023-12-31 14:30:00');
    $tzDate = DateWithTimezone::fromDateTime($dateTime->setTimezone(new DateTimeZone('UTC')));
    

2. Day Overflow in Months

  • Issue: nextMonth() on LocalDate with 2023-01-31 returns 2023-02-28 (correct), but chaining nextMonth() repeatedly may not handle year boundaries intuitively.
  • Fix: Normalize after multiple operations:
    $date = LocalDate::fromString('2023-01-31');
    $date = $date->nextMonth()->nextMonth(); // 2023-03-31 (invalid)
    $date = $date->normalize(); // 2023-03-31 → 2023-04-01 (if supported)
    
  • Workaround: Use `toDateTime()->modify('+2 months')->format('Y-m-d')
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui