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

Clock Laravel Package

symfony/clock

Symfony Clock decouples your app from the system clock. Inject ClockInterface to get deterministic time via now() and control behavior like sleep() and time zones. Ideal for testable, time-sensitive code without relying on global time functions.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require symfony/clock
    
  2. Basic Usage: Replace direct time calls with ClockInterface in your service constructor:

    use Symfony\Component\Clock\NativeClock;
    use Symfony\Component\Clock\ClockInterface;
    
    class MyService {
        public function __construct(private ClockInterface $clock) {}
    }
    
  3. First Use Case: Replace new DateTimeImmutable() with $clock->now() in a time-sensitive method:

    public function checkExpiry(DateTimeInterface $expiryDate) {
        return $this->clock->now() > $expiryDate;
    }
    
  4. Dependency Injection: Register the clock in Laravel’s service container (e.g., AppServiceProvider):

    $this->app->bind(ClockInterface::class, function ($app) {
        return new NativeClock();
    });
    
  5. Testing: Use MockClock to simulate time:

    use Symfony\Component\Clock\MockClock;
    
    $clock = new MockClock('2024-01-01T00:00:00+00:00');
    $service = new MyService($clock);
    

Where to Look First

  • Symfony Clock Documentation for API reference.
  • ClockInterface for method signatures (now(), sleep(), withTimeZone()).
  • NativeClock and MockClock for production and testing implementations.
  • ClockSensitiveTrait for quick adoption in existing classes.

Implementation Patterns

Core Workflows

1. Time-Agnostic Services

Refactor services to depend on ClockInterface:

class SubscriptionService {
    public function __construct(
        private ClockInterface $clock,
        private SubscriptionRepository $repository
    ) {}

    public function isExpired(Subscription $subscription) {
        return $this->clock->now() > $subscription->expires_at;
    }
}
  • Benefit: Easily swap NativeClock for MockClock in tests.

2. Timezone Management

Enforce a global timezone (e.g., UTC) or override per service:

$clock = (new NativeClock())->withTimeZone('UTC');
$service = new MyService($clock);
  • Laravel Tip: Bind a configured clock in AppServiceProvider:
    $this->app->bind(ClockInterface::class, fn() =>
        (new NativeClock())->withTimeZone(config('app.timezone'))
    );
    

3. Testing Time Manipulation

Simulate time progression without real delays:

$clock = new MockClock('2024-01-01T00:00:00+00:00');
$service = new SubscriptionService($clock, $repository);

// Fast-forward 30 days
$clock->advance(new DateInterval('P30D'));
$this->assertTrue($service->isExpired($subscription));
  • Use Case: Test scheduled jobs, rate limits, or subscription logic in seconds.

4. Sleeping in Tests

Replace sleep() with controlled delays:

$clock = new MockClock();
$service = new RateLimiter($clock);

// Simulate a 2-second delay without waiting
$clock->sleep(2.0);
$this->assertTrue($service->isRateLimited());

5. Integration with Laravel Components

  • Queues/Jobs: Inject ClockInterface into jobs for deterministic time checks:
    class SendReminderJob implements ShouldQueue {
        public function __construct(private ClockInterface $clock) {}
    
        public function handle() {
            if ($this->clock->now() > $this->dueDate) {
                // Send reminder
            }
        }
    }
    
  • Messenger: Use ClockInterface in message handlers for time-based routing.
  • HTTP Client: Simulate timeouts or delays in tests:
    $clock = new MockClock();
    $client = new SymfonyHttpClient($clock);
    

6. Feature Flags with Time

Isolate time-sensitive features (e.g., A/B tests) by injecting clocks:

class NewCheckoutFlow {
    public function __construct(private ClockInterface $clock) {}

    public function isEnabled() {
        return $this->clock->now() > config('features.new_checkout_date');
    }
}
  • Benefit: Test features in isolation without affecting production time.

Laravel-Specific Patterns

1. Artisan Commands

Inject ClockInterface to make commands testable:

class SendScheduledEmails extends Command {
    protected $signature = 'emails:send-scheduled';
    protected $description = 'Send emails with scheduled timestamps';

    public function __construct(private ClockInterface $clock) {
        parent::__construct();
    }

    public function handle() {
        $now = $this->clock->now();
        // Logic using $now
    }
}

2. Middleware

Use clocks for time-based access control:

class TimeBasedMiddleware {
    public function __construct(private ClockInterface $clock) {}

    public function handle(Request $request, Closure $next) {
        if ($this->clock->now() > now()->startOfDay()->addHours(20)) {
            abort(429, 'Maintenance mode');
        }
        return $next($request);
    }
}

3. Eloquent Models

Avoid now() in model methods:

class Subscription extends Model {
    public function isActive(ClockInterface $clock) {
        return $clock->now() < $this->expires_at;
    }
}
  • Tip: Use Laravel’s Clock facade (if available) or bind ClockInterface globally.

4. Cron Jobs

Replace now() in scheduled tasks:

class CleanupOldLogs {
    public function __construct(private ClockInterface $clock) {}

    public function handle() {
        $threshold = $this->clock->now()->subDays(30);
        Log::where('created_at', '<', $threshold)->delete();
    }
}

5. API Responses

Standardize timestamps using the clock:

class ApiController {
    public function __construct(private ClockInterface $clock) {}

    public function index() {
        return response()->json([
            'data' => [...],
            'generated_at' => $this->clock->now()->format('c'),
        ]);
    }
}

Gotchas and Tips

Pitfalls

1. Negative sleep() Values

  • Issue: MockClock::sleep(-1) throws an exception (unlike NativeClock).
  • Fix: Use MockClock::advance() for time adjustments:
    $clock->advance(new DateInterval('PT-1S')); // Rewind 1 second
    

2. Timezone Mismatches

  • Issue: Forgetting to set a timezone can lead to inconsistent now() results.
  • Fix: Always configure the timezone explicitly:
    $clock = (new NativeClock())->withTimeZone('UTC');
    

3. Static Time Calls

  • Issue: Legacy code using now(), Carbon::now(), or time() will bypass the clock.
  • Fix: Use a find-and-replace tool to identify and refactor static calls:
    grep -r "new DateTime" --include="*.php" app/
    grep -r "now()" --include="*.php" app/
    

4. Thread Safety

  • Issue: NativeClock is not thread-safe (uses time() under the hood).
  • Fix: Use a single NativeClock instance per process (Laravel’s container handles this).

5. Microsecond Precision

  • Issue: NativeClock may lose microsecond precision on some systems.
  • Fix: Use ClockInterface methods that return DateTimeImmutable (which supports microseconds).

6. Testing Edge Cases

  • Issue: Tests may fail if MockClock isn’t properly advanced.
  • Fix: Always reset the clock between tests:
    $clock = new MockClock('2024-01-01T00:00:00+00:00');
    // Test logic...
    $clock = new MockClock(); // Reset for next test
    

Debugging Tips

1. Verify Clock Usage

Use Laravel’s DI container to inspect bound clocks:

$clock = app(ClockInterface::class);
dump($clock::class); // Should
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport