Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 118 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,131 @@
# Thesis TimeSpan
# TimeSpan

[![PHP Version Requirement](https://img.shields.io/packagist/dependency-v/thesis/time-span/php)](https://packagist.org/packages/thesis/time-span)
[![GitHub Release](https://img.shields.io/github/v/release/thesis-php/time-span)](https://github.com/thesis-php/time-span/releases)
[![Code Coverage](https://codecov.io/gh/thesis-php/time-span/branch/0.2.x/graph/badge.svg)](https://codecov.io/gh/thesis-php/time-span/tree/0.2.x)
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fthesis-php%2Ftime-span%2F0.2.x)](https://dashboard.stryker-mutator.io/reports/github.com/thesis-php/time-span/0.2.x)
`TimeSpan` is an immutable value object representing a time interval with **nanosecond precision**. It provides a robust way to handle time durations, perform arithmetic operations, and format time strings without the common pitfalls of floating-point math or mutable state.

## Installation

```shell
```bash
composer require thesis/time-span
```

## Usage

You can create a `TimeSpan` instance using various static factory methods depending on your source unit.

```php
use YourVendor\TimeSpan;

// From specific units
$span = TimeSpan::fromSeconds(90);
$span = TimeSpan::fromMinutes(5.5);
$span = TimeSpan::fromHours(1);
$span = TimeSpan::fromDays(2);

// From a combination of units
$span = TimeSpan::from(
days: 1,
hours: 12,
minutes: 30
);

// From a DateInterval
$span = TimeSpan::fromInterval(new \DateInterval('P1DT12H'));
```

### ⚠️ Important: Nanoseconds and IEEE 754

The core method `TimeSpan::fromNanoseconds(int|float $nanoseconds)` accepts both integers and floating-point numbers. However, users must be aware of **IEEE 754 floating-point limitations**.

When passing large nanosecond values as `float`, you may encounter precision loss. This is not a bug in the library but a fundamental property of how computers handle floating-point numbers.

* **Safe:** Integers (up to `PHP_INT_MAX`) are always precise.
* **Risky:** Large floats (e.g., converting huge values from other units) may be rounded to the nearest representable number.

If the provided float value exceeds the safe integer range for nanoseconds, the library will throw an `OutOfBoundsException` to prevent silent overflow errors.

```php
// Safe (Integer)
$span = TimeSpan::fromNanoseconds(1000);

// Caution (Float): Very large floats may lose 1-2 nanoseconds of precision
$span = TimeSpan::fromNanoseconds(1000.5); // Rounded to nearest integer
```

## Formatting

The `format()` method allows you to generate human-readable strings from the time interval.

This method is **context-aware**: it automatically calculates values based on the largest unit you request. For example, if you only ask for minutes (`%i`), it will show the *total* minutes. If you ask for hours and minutes (`%h:%i`), it will show total hours and the remaining minutes.

```php
public function format(string $format = '%h:%i:%s'): string
```

### Available Placeholders

| Placeholder | Description | Example Output |
| :--- | :--- | :--- |
| `%d` | Days | `1`, `106751` |
| `%h` | Hours (00-23 usually, or total if largest) | `05`, `150` |
| `%i` | Minutes (00-59 usually, or total if largest) | `09`, `90` |
| `%s` | Seconds (00-59 usually, or total if largest) | `30`, `120` |
| `%ms` | Milliseconds (000-999) | `050` |
| `%us` | Microseconds (000-999) | `001` |
| `%ns` | Nanoseconds (000-999) | `999` |

### Formatting Examples

Assuming a time span of **1 day, 7 hours, 7 minutes, 3 seconds, 56ms, 89us, 23ns**:

```php
// Full detail
echo $span->format('%d %h:%i:%s.%ms_%us_%ns');
// Output: "1 07:07:03.056_089_023"
// or
echo $span->format('%d %h:%i:%s.%ns');
// Output: "1 07:07:03.056089023"

// Total hours (Days are converted to hours)
echo $span->format('%h:%i:%s');
// Output: "31:07:03" (1 day + 7 hours = 31 hours)

// Total seconds
echo $span->format('%s');
// Output: "112023" (Total seconds in the span)

// Just minutes and seconds
echo $span->format('%i:%s');
// Output: "1867:03"
```

## Math Operations

The library includes a comprehensive set of immutable mathematical operations. Since `TimeSpan` is a value object, these methods always return a **new instance**.

* **Arithmetic:** `add()`, `sub()`, `mul()`, `div()`
* **Helpers:** `abs()`, `negated()`

```php
use Thesis\Time\TimeSpan;
$span1 = TimeSpan::fromMinutes(10);
$span2 = TimeSpan::fromMinutes(5);

$delay = TimeSpan::fromSeconds(25.123);
$total = $span1->add($span2); // 15 minutes
$diff = $span1->sub($span2); // 5 minutes
$double = $span1->mul(2); // 20 minutes
```

## Comparison

You can compare `TimeSpan` objects directly without converting them manually:

echo $delay->toMilliseconds(); // 25123
* `compareTo()`
* `isEqualTo()`
* `isGreaterThan()` / `isGreaterThanOrEqualTo()`
* `isLessThan()` / `isLessThanOrEqualTo()`
* `isZero()`, `isPositive()`, `isNegative()`

```php
if ($span1->isGreaterThan($span2)) {
// ...
}
```