Skip to content
Merged
Show file tree
Hide file tree
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
63 changes: 63 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: CI/CD

on:
push:
branches: [ main, dev ]
pull_request:
branches: [ main, dev ]
types: [ opened, synchronize, reopened, closed ]

permissions:
contents: write
pull-requests: write

jobs:
tests:
name: PHPUnit tests
runs-on: ubuntu-latest
if: github.event.action != 'closed' && !contains(github.event.head_commit.message, '--no-change')
strategy:
matrix:
php: [8.3, 8.4, 8.5]
steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: mbstring, bcmath, pdo, sqlite
coverage: xdebug

- name: Install Dependencies
run: composer install --prefer-dist --no-progress --no-interaction

- name: Run Tests
env:
XDEBUG_MODE: coverage
run: vendor/bin/phpunit --colors=never --coverage-text

release-please:
name: Release Please
needs: tests
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v4
with:
release-type: php
token: ${{ secrets.GITHUB_TOKEN }}

cleanup-release-please:
name: Cleanup release-please branch
if: >
github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.head.ref, 'release-please')
runs-on: ubuntu-latest
steps:
- name: Delete release-please branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api -X DELETE "/repos/${{ github.repository }}/git/refs/heads/${{ github.event.pull_request.head.ref }}"
32 changes: 0 additions & 32 deletions .github/workflows/phpunit.yml

This file was deleted.

20 changes: 20 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
default_install_hook_types:
- pre-commit
- commit-msg

repos:
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v4.4.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
args: []

- repo: local
hooks:
- id: phpunit
name: phpunit
entry: vendor/bin/phpunit
language: system
pass_filenames: false
always_run: true
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}
},
"require": {
"php": ">=8.2"
"php": ">=8.3"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
Expand Down
26 changes: 26 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ $duration = DurationImmutable::hoursAndMinutes(1, 30);

// From Carbon
$duration = DurationImmutable::fromCarbon(\Carbon\CarbonInterval::hours(2));

// From a string (similar to Carbon::parse)
$duration = DurationImmutable::parse('1h 30m');
$duration = DurationImmutable::parse('2 days');
$duration = DurationImmutable::parse('PT1H30M');
```

### Accessing Units
Expand Down Expand Up @@ -112,6 +117,18 @@ $duration->toShortHuman(); // "1d 2h 3m"

// String conversion
(string) $duration; // "02:03" (hh:mm)

// JSON Serialization
$json = json_encode($duration);
/*
{
"seconds": 5400,
"human": "1 hour 30 minutes",
"short_human": "1h 30m",
"formatted": "01:30",
"iso8601": "PT1H30M"
}
*/
```

### TimeDelta (Negative Durations)
Expand Down Expand Up @@ -150,6 +167,15 @@ $task = Task::find(1);
$task->duration_in_seconds; // Returns DurationImmutable instance
```

## Development

This package uses [pre-commit](https://pre-commit.com/) to maintain code quality. To set up your local development environment:

1. Install pre-commit: `pip install pre-commit` (or your preferred method)
2. Install the git hooks: `pre-commit install --hook-type pre-commit --hook-type commit-msg`

The hooks will automatically run PHPUnit and validate your commit messages against [Conventional Commits](https://www.conventionalcommits.org/).

## Testing

```bash
Expand Down
2 changes: 2 additions & 0 deletions src/Casts/Days.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Casts;

final class Days extends DurationCast
Expand Down
2 changes: 2 additions & 0 deletions src/Casts/Hours.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Casts;

final class Hours extends DurationCast
Expand Down
2 changes: 2 additions & 0 deletions src/Casts/Minutes.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Casts;

final class Minutes extends DurationCast
Expand Down
2 changes: 2 additions & 0 deletions src/Casts/Seconds.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Casts;

final class Seconds extends DurationCast
Expand Down
2 changes: 2 additions & 0 deletions src/Casts/Years.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Casts;

final class Years extends DurationCast
Expand Down
7 changes: 6 additions & 1 deletion src/Duration.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace AyupCreative\Duration;

final class Duration implements \JsonSerializable, DurationInterface
final class Duration implements \JsonSerializable, DurationInterface, Wireable
{
use Features\Arithmetic;
use Features\Builders;
Expand All @@ -13,6 +13,7 @@ final class Duration implements \JsonSerializable, DurationInterface
use Features\Formatting;
use Features\MagicProperties;
use Features\TemporalUnits;
use Features\Wireable;

protected int $totalSeconds;

Expand Down Expand Up @@ -89,6 +90,10 @@ public function multiply(float $factor): self
*/
public function ceilTo(int $seconds): self
{
if ($seconds === 0) {
return $this;
}

$seconds = (int)(ceil($this->totalSeconds / $seconds) * $seconds);

$this->totalSeconds = (new self($seconds))->totalSeconds;
Expand Down
3 changes: 2 additions & 1 deletion src/DurationImmutable.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace AyupCreative\Duration;

final class DurationImmutable implements \JsonSerializable, DurationInterface
final class DurationImmutable implements \JsonSerializable, DurationInterface, Wireable
{
use Features\Arithmetic;
use Features\Builders;
Expand All @@ -13,6 +13,7 @@ final class DurationImmutable implements \JsonSerializable, DurationInterface
use Features\Formatting;
use Features\MagicProperties;
use Features\TemporalUnits;
use Features\Wireable;

protected int $totalSeconds;

Expand Down
2 changes: 2 additions & 0 deletions src/DurationInterface.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration;

interface DurationInterface
Expand Down
2 changes: 2 additions & 0 deletions src/Features/Arithmetic.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Features;

use AyupCreative\Duration\DurationInterface;
Expand Down
15 changes: 15 additions & 0 deletions src/Features/Builders.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Features;

use Carbon\CarbonInterval;
Expand Down Expand Up @@ -123,6 +125,19 @@ public static function make(int $days = 0, int $hours = 0, int $minutes = 0, int
return new self($seconds);
}

/**
* Create a duration from a string.
*
* Usage: $duration = Duration::parse('1h 30m');
*
* @param string $string
* @return self
*/
public static function parse(string $string): self
{
return new self((int) CarbonInterval::make($string)->totalSeconds);
}

/**
* Create a duration from a CarbonInterval.
*
Expand Down
4 changes: 3 additions & 1 deletion src/Features/Constants.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Features;

trait Constants
Expand All @@ -9,7 +11,7 @@ trait Constants
public const SECONDS_PER_DAY = 86400;
public const SECONDS_PER_WEEK = 604800;
public const SECONDS_PER_MONTH = 2629800; // 30.44 days
public const SECONDS_PER_YEAR = 31557600; // 365.25 days
public const SECONDS_PER_YEAR = 31536000; // 365 days

/**
* Decompose the duration into days, hours, minutes, and seconds.
Expand Down
13 changes: 10 additions & 3 deletions src/Features/Conversion.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Features;

use DateInterval;
Expand Down Expand Up @@ -125,10 +127,15 @@ public function toDateInterval(): DateInterval
/**
* Serialize the duration to JSON.
*
* @return int
* @return array<string, mixed>
*/
public function jsonSerialize(): int
public function jsonSerialize(): array
{
return $this->totalSeconds;
return [
'seconds' => $this->toSeconds(),
'human' => $this->toHuman(),
'short_human' => $this->toShortHuman(),
'iso8601' => $this->toCarbonInterval()->spec(),
];
}
}
2 changes: 2 additions & 0 deletions src/Features/Formatting.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Features;

trait Formatting
Expand Down
2 changes: 2 additions & 0 deletions src/Features/MagicProperties.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Features;

/**
Expand Down
2 changes: 2 additions & 0 deletions src/Features/TemporalUnits.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace AyupCreative\Duration\Features;

trait TemporalUnits
Expand Down
Loading
Loading