Read this in other languages: Русский
Duration
is an immutable wrapper class for working with time durations with ISO 8601 support. It provides a comprehensive API for creating, manipulating, formatting, and comparing time durations in a type-safe and consistent manner.
- Features
- Installation
- Basic Usage
- Factory Methods
- ISO 8601 Support
- Conversion Methods
- Arithmetic Operations
- Comparison Operations
- Increment and Decrement Operations
- Formatting Methods
- Serialization
- DateInterval Conversion
- Examples
- Testing
- Contributing
- License
- Immutable Design: All operations return new instances, preserving the original
- Fluent Interface: Method chaining for cleaner code
- ISO 8601 Support: Parse and generate ISO 8601 duration strings (e.g.,
P1Y2M3DT4H5M6S
) - Comprehensive Time Units: Convert between seconds, minutes, hours, days, weeks, months, and years
- Advanced Comparison: Compare durations with support for ALL/ANY modes when working with arrays
- Increment/Decrement: Convenient methods for increasing or decreasing durations
- Multiple Formatting Options: Human-readable, custom format templates, and component arrays
- Serialization Support: JSON serialization built-in
- DateInterval Integration: Convert to and from PHP's DateInterval class
composer require bermudaphp/duration
use Bermuda\Stdlib\Duration;
// Create a duration of 1 hour and 30 minutes
$duration = new Duration(5400);
// Get total seconds
echo $duration->toSeconds(); // 5400
// Convert to minutes
echo $duration->toMinutes(); // 90
// Convert to hours
echo $duration->toHours(); // 1
// Get the ISO 8601 representation
echo $duration->toISO8601(); // "PT1H30M"
// Get a human-readable representation
echo $duration->toHumanReadable(); // "01:30:00"
Duration
provides multiple factory methods for creating instances:
// From specific time units
$fromSeconds = Duration::fromSeconds(60); // 60 seconds
$fromMinutes = Duration::fromMinutes(5); // 5 minutes
$fromHours = Duration::fromHours(2); // 2 hours
$fromDays = Duration::fromDays(1); // 1 day
$fromWeeks = Duration::fromWeeks(2); // 2 weeks
$fromMonths = Duration::fromMonths(3); // 3 months
$fromYears = Duration::fromYears(1); // 1 year
// From ISO 8601 string
$fromISO = Duration::fromISO8601('P1Y2M3DT4H5M6S');
// From DateInterval
$interval = new \DateInterval('P1DT6H');
$fromInterval = Duration::fromDateInterval($interval);
Duration
fully supports the ISO 8601 duration format:
// Validate ISO 8601 strings
$isValid = Duration::validate('P1Y2M3DT4H5M6S'); // true
$isInvalid = Duration::validate('P1X'); // false
// Create from ISO 8601 string
$duration = Duration::fromISO8601('P1Y2M3DT4H5M6S');
// Convert to ISO 8601 string
echo $duration->toISO8601(); // "P1Y2M3DT4H5M6S"
- Format:
P[n]Y[n]M[n]DT[n]H[n]M[n]S
P
is the duration designator (for period) placed at the startY
is the year designatorM
is the month designatorD
is the day designatorT
is the time designator (required if any time components are used)H
is the hour designatorM
is the minute designatorS
is the second designator
Example: P3Y6M4DT12H30M5S
represents a duration of 3 years, 6 months, 4 days, 12 hours, 30 minutes, and 5 seconds.
Convert a duration to different time units:
$duration = Duration::fromISO8601('P1Y2M3DT4H5M6S');
echo $duration->toSeconds(); // Total seconds
echo $duration->toMinutes(); // Total minutes (rounded down)
echo $duration->toHours(); // Total hours (rounded down)
echo $duration->toDays(); // Total days (rounded down)
echo $duration->toWeeks(); // Total weeks (rounded down)
echo $duration->toMonths(); // Total months (rounded down)
echo $duration->toYears(); // Total years (rounded down)
Perform arithmetic operations on durations:
$duration1 = new Duration(3600); // 1 hour
$duration2 = new Duration(1800); // 30 minutes
// Addition
$sum = $duration1->add($duration2); // 1 hour 30 minutes
// Adding specific time units
$plusOneHour = $duration1->addHours(1); // Add 1 hour
$plusTenMinutes = $duration1->addMinutes(10); // Add 10 minutes
$plusYear = $duration1->addYears(1); // Add 1 year
// Subtraction (throws exception if result would be negative)
$difference = $duration1->strictSubtract($duration2); // 30 minutes
// Safe subtraction (returns zero if result would be negative)
$safeDifference = $duration1->subtract($duration2); // 30 minutes
// Subtracting specific time units (throws exception if result would be negative)
$minusOneHour = $duration2->subtractHours(1); // Exception!
$minusTenMinutes = $duration2->subtractMinutes(10); // 20 minutes
// Multiplication
$doubled = $duration1->multiply(2); // 2 hours
// Division
$halved = $duration1->divide(2); // 30 minutes
Compare durations with various methods:
$duration1 = new Duration(3600); // 1 hour
$duration2 = new Duration(7200); // 2 hours
$duration3 = new Duration(3600); // 1 hour
// Compare two durations
$comparisonResult = $duration1->compareTo($duration2); // -1 (less than)
// Equality
$isEqual = $duration1->equals($duration3); // true
$isNotEqual = $duration1->equals($duration2); // false
// Greater/less than
$isLess = $duration1->lessThan($duration2); // true
$isLessOrEqual = $duration1->lessThanOrEqual($duration3); // true
$isGreater = $duration2->greaterThan($duration1); // true
$isGreaterOrEqual = $duration1->greaterThanOrEqual($duration3); // true
// Check if between two durations
$isBetween = $duration1->between(
new Duration(1800), // 30 minutes
new Duration(10800), // 3 hours
true // inclusive (default)
); // true
Compare a duration against multiple durations using ALL or ANY modes:
$duration = new Duration(3600); // 1 hour
$durations = [
new Duration(1800), // 30 minutes
new Duration(7200), // 2 hours
new Duration(10800) // 3 hours
];
// ALL mode (default) - the condition must be true for ALL durations in the array
$isLessThanAll = $duration->lessThan($durations, Duration::COMPARE_ALL); // false
$isGreaterThanAll = $duration->greaterThan($durations, Duration::COMPARE_ALL); // false
// ANY mode - the condition must be true for AT LEAST ONE duration in the array
$isLessThanAny = $duration->lessThan($durations, Duration::COMPARE_ANY); // false
$isGreaterThanAny = $duration->greaterThan($durations, Duration::COMPARE_ANY); // true
Increment or decrement durations:
$duration = Duration::fromMinutes(5); // 5 minutes
// Increment by 1 second
$incremented = $duration->increment(); // 5 minutes 1 second
// Increment by specific amount
$incrementedByMinute = $duration->incrementBy(60); // 6 minutes
// Increment by another duration
$anotherDuration = Duration::fromMinutes(10);
$combined = $duration->incrementByDuration($anotherDuration); // 15 minutes
// Decrement (throws exception if result would be negative)
$decremented = $duration->decrement(); // 4 minutes 59 seconds
// Decrement by specific amount (throws exception if result would be negative)
$decrementedByMinute = $duration->decrementBy(60); // 4 minutes
// Safe decrement (returns zero if result would be negative)
$safeDecremented = $duration->safeDecrement(); // 4 minutes 59 seconds
// Safe decrement by specific amount (returns zero if result would be negative)
$safeDecrementedByTooMuch = $duration->safeDecrementBy(600); // 0 seconds
Format durations in different ways:
$duration = Duration::fromISO8601('P1Y2M3DT4H5M6S');
// Convert to array of components
$array = $duration->toArray();
/*
[
'years' => 1,
'months' => 2,
'days' => 3,
'hours' => 4,
'minutes' => 5,
'seconds' => 6
]
*/
// Format using a custom template
$formatted = $duration->format('Years: %Y, Months: %M, Days: %D, Time: %H:%I:%S');
// "Years: 01, Months: 02, Days: 03, Time: 04:05:06"
// Human-readable format
$readable = $duration->toHumanReadable(); // "01:02:03:04:05:06"
The format()
method supports the following placeholders:
Placeholder | Description |
---|---|
%Y |
Years (zero-padded) |
%M |
Months (zero-padded) |
%D |
Days (zero-padded) |
%H |
Hours (zero-padded) |
%I |
Minutes (zero-padded) |
%S |
Seconds (zero-padded) |
%T |
Total seconds |
The Duration
class implements JsonSerializable
and Stringable
interfaces:
$duration = Duration::fromISO8601('P1Y2M3DT4H5M6S');
// Convert to string (ISO 8601 format)
echo (string)$duration; // "P1Y2M3DT4H5M6S"
// JSON serialization
$json = json_encode($duration);
/*
{
"seconds": 37090998,
"iso8601": "P1Y2M3DT4H5M6S",
"components": {
"years": 1,
"months": 2,
"days": 3,
"hours": 4,
"minutes": 5,
"seconds": 6
}
}
*/
Convert between Duration
and PHP's DateInterval
:
// Create from DateInterval
$interval = new \DateInterval('P1YT6H');
$duration = Duration::fromDateInterval($interval);
// Convert to DateInterval
$newInterval = $duration->toDateInterval();
echo $newInterval->format('%y years, %h hours'); // "1 years, 6 hours"
// Create a 5-minute countdown timer
$countdownDuration = Duration::fromMinutes(5);
// Simulate countdown (in a real application, this would be in a loop with sleep)
for ($i = 0; $i < 5; $i++) {
echo "Remaining: " . $countdownDuration->toHumanReadable() . PHP_EOL;
$countdownDuration = $countdownDuration->decrementBy(60); // Decrease by 1 minute
}
echo "Time's up!";
// Event duration
$eventDuration = Duration::fromHours(2);
// Check if event is longer than 1 hour but shorter than 3 hours
$isReasonableLength = $eventDuration->between(
Duration::fromHours(1),
Duration::fromHours(3)
);
// Create a 15-minute break
$breakDuration = Duration::fromMinutes(15);
// Add 15-minute break at the end
$totalDuration = $eventDuration->add($breakDuration);
echo "Total event duration: " . $totalDuration->toHumanReadable();
// Start time stored in database as seconds
$taskTimeSeconds = 3600; // 1 hour already tracked
$taskDuration = new Duration($taskTimeSeconds);
// User works for 45 more minutes
$additionalTime = Duration::fromMinutes(45);
$updatedTaskDuration = $taskDuration->add($additionalTime);
// Get task time components for display
$components = $updatedTaskDuration->toArray();
$hours = $components['hours'] ?? 0;
$minutes = $components['minutes'] ?? 0;
echo "Task time: {$hours}h {$minutes}m";
Run the PHPUnit tests:
composer test
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.