Weave 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

Relativetime Laravel Package

mpratt/relativetime

A lightweight PHP library for converting timestamps and DateTime objects into human-readable relative time strings like "5 minutes ago" or "in 2 days", with localization support and simple integration into Laravel or any PHP project.

Deep Wiki
Context7

Getting Started

Install with Composer — the package is framework-agnostic, so nothing else is required:

composer require mpratt/relativetime

The entry point is RelativeTime\RelativeTime. Instantiate it (optionally with a config array) and call one of the public methods with a date string, timestamp, or DateTime:

use RelativeTime\RelativeTime;

$rt = new RelativeTime();

echo $rt->timeAgo('2024-01-15 09:30:00');   // "5 minutes ago"
echo $rt->timeLeft('2024-01-15 09:30:00');  // "5 minutes left"

The fastest day-to-day win is rendering human-readable timestamps in a view or API payload, e.g. $rt->timeAgo($post->created_at) inline in a Blade template or as a computed field in an Eloquent accessor.

Where to look first:

  1. README.md in the package root — documents the constructor options and the four public methods.
  2. src/RelativeTime.php — the default config array lives at the top of the class; read it before relying on any behaviour.
  3. src/Languages/ — the shipped language classes (English, Spanish, German, plus others). This is the extension point for custom wording.

The config array is the primary lever. The keys worth knowing on day one are truncate, language, separator, suffix, futureSuffix, and pastSuffix:

$rt = new RelativeTime([
    'truncate'     => 2,
    'language'     => '\RelativeTime\Languages\English',
    'separator'    => ', ',
    'suffix'       => true,
    'pastSuffix'   => 'ago',
    'futureSuffix' => 'left',
]);

Implementation Patterns

Bind it once as a singleton in Laravel. There is no service provider shipped, so add a tiny one (or a closure binding in AppServiceProvider) so the config is defined in one place and the object is reused across a request:

// AppServiceProvider::register()
$this->app->singleton(\RelativeTime\RelativeTime::class, function () {
    return new \RelativeTime\RelativeTime([
        'truncate'   => 2,
        'language'   => \RelativeTime\Languages\English::class,
        'separator'  => ' ',
        'pastSuffix' => 'ago',
    ]);
});

Then resolve it via the container or inject it into controllers, jobs, and API resources.

Blade helper. Wrap the call in a small helper so templates stay readable:

// app/helpers.php
function time_ago($date): string
{
    return app(\RelativeTime\RelativeTime::class)->timeAgo($date);
}
<span title="{{ $post->created_at }}">{{ time_ago($post->created_at) }}</span>

API resources. Prefer a dedicated field over mutating the raw timestamp so clients can choose:

public function toArray($request)
{
    return [
        'created_at'       => $this->created_at->toIso8601String(),
        'created_at_human' => app(RelativeTime::class)->timeAgo($this->created_at),
    ];
}

Driving UI logic with calculate(). When you need the pieces separately — e.g. to apply your own markup, icons, or a title attribute — use the structured output rather than string-parsing timeAgo():

$parts = $rt->calculate($date);
// inspect value / unit / suffix and render your own template

Batch formatting on collections. Map over a collection once at the serialization boundary rather than calling into the library in nested loops. If a page renders dozens of relative strings, resolve the singleton once and reuse it:

$rt = app(\RelativeTime\RelativeTime::class);

$posts->each(function ($post) use ($rt) {
    $post->human_date = $rt->timeAgo($post->created_at);
});

Custom language / wording. Create a class under app/Support/RelativeTime/ mirroring the bundled language class you want to base it on, override the strings, and point the language config key at your class. This is how you handle brand voice ("just now", "moments ago"), regional variants, or a language the package doesn't ship.

Two ways to use it side by side. timeAgo() is direction-aware (past produces "ago", future produces the future suffix), while timeLeft() forces the future wording and timeDifference() gives the bare difference. Choose per context: activity feeds want timeAgo(), countdowns and due dates want timeLeft().

Gotchas and Tips

  • Timezone is the caller's problem. The library compares against PHP's current time, so date_default_timezone_set() (or Laravel's app.timezone) must match the timezone of the stored date. A UTC database value rendered under Europe/Berlin produces an off-by-hours relative string that looks plausible but is wrong.
  • truncate drops units, it does not round. With truncate => 1, "1 hour 59 minutes" renders as "1 hour ago". This reads as much older than it is. Use truncate => 2 for feeds, and be explicit in tests about the expected boundary behaviour.
  • suffix => false is not the same as "no grammar". You still get the joined units and separator; only the trailing word is dropped. If you want "5m" style output you must build it yourself from calculate().
  • Integer timestamps can be ambiguous. Pass a formatted string ($model->created_at->toDateTimeString()) or a DateTime instance rather than a raw Unix integer, which date-parsing layers can misinterpret. This is the single most common cause of "it says 1970".
  • Deterministic tests need a frozen clock. The comparison is against "now", so tests that assert on exact strings will flake near unit boundaries. Either assert on calculate()'s unit/value instead of the full sentence, or wrap the call in a class you can mock that pins the reference date. Avoid asserting "5 minutes ago" against Carbon::now() without Carbon::setTestNow().
  • Carbon already does most of this in Laravel. Before reaching for the package, check whether diffForHumans() with parts/short covers your case. Justify the dependency by the things Carbon doesn't give you cheaply: cross-language output independent of app.locale, the separator config, and clean truncation control.
  • Don't reach for it inside a loop over thousands of rows. Each call re-parses dates and builds strings; on large exports, format once in a resource layer or precompute at write time if the value is cacheable.
  • Translations are per-class, not per-locale key. Setting language at runtime based on the request locale means resolving a different class per request — do that in a small factory rather than hardcoding the class in config if the app is multilingual.
  • Pin the version. The package is small and stable but version-agnostic about Laravel; a ^2.0-style constraint in composer.json protects you from behavioural changes to defaults.
  • Extension points are the config array and the language classes. There is no event system or formatter pipeline — if you need pluralisation rules or gendered suffixes beyond what a language class offers, subclass it rather than post-processing the returned string.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle