├── .styleci.yml ├── .gitignore ├── src ├── Facades │ └── CountdownFacade.php ├── Exceptions │ ├── InvalidArgumentToCountdown.php │ ├── InvalidDateFormatToCountdown.php │ └── InvalidPropertyStringForHumanException.php ├── CountdownServiceProvider.php ├── Traits │ └── CalculateTimeDiff.php └── Countdown.php ├── tests ├── User.php ├── LaravelInstanceTest.php ├── TestCase.php ├── CountdownEloquentTraitTest.php └── CountdownTest.php ├── .scrutinizer.yml ├── .github └── FUNDING.yml ├── .travis.yml ├── LICENSE.md ├── phpunit.xml ├── changelog.MD ├── composer.json ├── CODE_OF_CONDUCT.md └── README.md /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: psr2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | vendor 3 | .idea/ 4 | build/ 5 | composer.lock 6 | coverage.clover -------------------------------------------------------------------------------- /src/Facades/CountdownFacade.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf( 14 | Countdown::class, 15 | $instance 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: ["tests/*"] 3 | 4 | checks: 5 | php: 6 | remove_extra_empty_lines: true 7 | remove_php_closing_tag: true 8 | remove_trailing_whitespace: true 9 | fix_use_statements: 10 | remove_unused: true 11 | preserve_multiple: false 12 | preserve_blanklines: true 13 | order_alphabetically: true 14 | fix_php_opening_tag: true 15 | fix_linefeed: true 16 | fix_line_ending: true 17 | fix_identation_4spaces: true 18 | fix_doc_comments: true 19 | 20 | tools: 21 | external_code_coverage: 22 | timeout: 600 23 | runs: 3 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: jpmurray 4 | #patreon: # Replace with a single Patreon username 5 | #open_collective: # Replace with a single Open Collective username 6 | #ko_fi: # Replace with a single Ko-fi username 7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | #liberapay: # Replace with a single Liberapay username 10 | #issuehunt: # Replace with a single IssueHunt username 11 | #otechie: # Replace with a single Otechie username 12 | #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | \jpmurray\LaravelCountdown\Facades\CountdownFacade::class, 20 | ]; 21 | } 22 | 23 | /** 24 | * Provider data for tests 25 | * @return array $dates 26 | */ 27 | public function providerDates() 28 | { 29 | $startDate = Carbon::parse('2000-01-01 14:00:35'); 30 | $endDate = Carbon::parse('2007-08-14 08:05:10'); 31 | 32 | return [[$startDate, $endDate]]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/CountdownServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->bind('jpmurray.countdown', function ($app) { 29 | $carbon = new Carbon; 30 | $timezone = $app->config->get('app.timezone'); 31 | 32 | return new Countdown($timezone, $carbon); 33 | }); 34 | 35 | $this->app->alias('jpmurray.countdown', Countdown::class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | 7 | # This triggers builds to run on the new TravisCI infrastructure. 8 | # See: http://docs.travis-ci.com/user/workers/container-based-infrastructure/ 9 | sudo: false 10 | 11 | addons: 12 | code_climate: 13 | repo_token: 3682237329cefba0e6403fb257e547cdc4608bbffdcac5e3d2dbd23fd3b2042d 14 | 15 | ## Cache composer 16 | cache: 17 | directories: 18 | - $HOME/.composer/cache 19 | 20 | before_script: 21 | - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-dist 22 | 23 | script: 24 | - vendor/bin/phpcs --standard=psr2 src/ 25 | - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover 26 | 27 | after_script: 28 | - | 29 | if [[ "$TRAVIS_PHP_VERSION" != 'hhvm' && "$TRAVIS_PHP_VERSION" != '7.0' ]]; then 30 | wget https://scrutinizer-ci.com/ocular.phar 31 | php ocular.phar code-coverage:upload --format=php-clover coverage.clover 32 | fi -------------------------------------------------------------------------------- /src/Traits/CalculateTimeDiff.php: -------------------------------------------------------------------------------- 1 | {$attribute}; 19 | $now = Carbon::now(); 20 | 21 | return $countdown->from($attribute) 22 | ->to($now)->get(); 23 | } 24 | 25 | /** 26 | * Return until time based in model attribite 27 | * 28 | * @param string $attribute 29 | * @return jpmurray\LaravelCountdown\Countdown $countdown 30 | */ 31 | public function until($attribute) 32 | { 33 | $countdown = app('jpmurray.countdown'); 34 | $attribute = $this->{$attribute}; 35 | $now = Carbon::now(); 36 | 37 | return $countdown->from($now) 38 | ->to($attribute)->get(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/CountdownEloquentTraitTest.php: -------------------------------------------------------------------------------- 1 | loadLaravelMigrations(['--database' => 'testing']); 20 | 21 | $this->faker = Faker\Factory::create(); 22 | } 23 | 24 | /** 25 | * Test running migration. 26 | * @dataProvider providerDates 27 | * @test 28 | */ 29 | public function testEloquentTrait($date) 30 | { 31 | $user = User::create([ 32 | 'name' => $this->faker->name, 33 | 'email' => $this->faker->email, 34 | 'password' => $this->faker->password, 35 | 'created_at' => $date, 36 | 'updated_at' => $date, 37 | ]); 38 | 39 | $this->assertInstanceOf(Countdown::class, $user->elapsed('created_at')); 40 | $this->assertInstanceOf(Countdown::class, $user->until('created_at')); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Jean-Philippe Murray 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /changelog.MD: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All Notable changes to `laravel-countdown` will be documented in this file. 4 | 5 | Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. 6 | 7 | ## 3.0.0 8 | 9 | - Dropping support for Laravel <= 5. Those users should be using earlier versions of the package. 10 | 11 | ## 2.0.0 - 2020-04-07 12 | 13 | - Updated dependencies to work for more recent version of Laravel. 14 | - Updated Carbon dependency to `^2.0`. 15 | 16 | ## 1.3.0 - 2018-06-29 17 | 18 | - Should insure compatibility with Laravel up to 5.6. 19 | 20 | ## 1.2.0 - 2017-08-02 21 | 22 | ### Added 23 | 24 | - Added a `toHuman` method to return a string that is human readable (@juniorb2ss) 25 | - The `toHuman` method accepts a custom string to parse so the returned readable string can be customized / localized (@juniorb2ss) 26 | 27 | ## 1.1.0 -2017-08-01 28 | 29 | ### Added 30 | 31 | - Tests! Thank you [Junior](https://github.com/juniorb2ss)! 32 | 33 | ## 1.0.1 - 2017-07-31 34 | 35 | - Fixed namespace in the trait, thanks to @svenluijten 36 | 37 | ## 1.0.0 - 2017-07-30 38 | 39 | First release! 40 | 41 | ## Please ignore below this line! 42 | 43 | ## Unreleased - YYYY-MM-DD 44 | 45 | ### Added 46 | 47 | ### Deprecated 48 | 49 | ### Fixed 50 | 51 | ### Removed 52 | 53 | ### Security 54 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jpmurray/laravel-countdown", 3 | "type": "library", 4 | "description": "Provide an easy class easy way to get the time difference between two dates, with an extra bonus trait for eloquent", 5 | "keywords": [ 6 | "jpmurray", 7 | "countdown", 8 | "time", 9 | "dates", 10 | "elapsed", 11 | "until" 12 | ], 13 | "homepage": "https://github.com/jpmurray/laravel-countdown", 14 | "license": "MIT", 15 | "authors": [ 16 | { 17 | "name": "Jean-Philippe Murray", 18 | "email": "himself@jpmurray.net", 19 | "homepage": "https://github.com/jpmurray/", 20 | "role": "Developer" 21 | } 22 | ], 23 | "require": { 24 | "php": "^7.0", 25 | "ext-bcmath": "*", 26 | "nesbot/carbon": "^2.0", 27 | "illuminate/support": "6.* || 7.* || 8.*" 28 | }, 29 | "require-dev": { 30 | "orchestra/testbench": "~3.0", 31 | "phpunit/phpunit": "~4.0||~5.0", 32 | "squizlabs/php_codesniffer": "^2.3", 33 | "nesbot/carbon": "^2.0", 34 | "mockery/mockery": "dev-master@dev" 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "jpmurray\\LaravelCountdown\\": "src/" 39 | } 40 | }, 41 | "autoload-dev": { 42 | "psr-4": { 43 | "Tests\\": "tests/" 44 | } 45 | }, 46 | "minimum-stability": "dev", 47 | "extra": { 48 | "laravel": { 49 | "providers": [ 50 | "jpmurray\\LaravelCountdown\\CountdownServiceProvider" 51 | ], 52 | "aliases": { 53 | "Countdown": "jpmurray\\LaravelCountdown\\Facades\\CountdownFacade" 54 | } 55 | } 56 | }, 57 | "scripts": { 58 | "test": "phpunit", 59 | "coverage": "phpunit --coverage-text --coverage-clover=coverage.clover", 60 | "check-style": "phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests", 61 | "fix-style": "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests", 62 | "build": "composer run test && composer run check-style && composer run fix-style && composer run coverage" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at himself@jpmurray.net. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel countdown 2 | 3 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/jpmurray/laravel-countdown/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/jpmurray/laravel-countdown/?branch=master) 4 | [![Build Status](https://scrutinizer-ci.com/g/jpmurray/laravel-countdown/badges/build.png?b=master)](https://scrutinizer-ci.com/g/jpmurray/laravel-countdown/build-status/master) 5 | 6 | The `jpmurray/laravel-countdown` and easy way to get the time difference between two dates, with an extra bonus trait for eloquent. 7 | 8 | I needed to get the diffrence of time, and while the [very good Carbon](https://github.com/briannesbitt/carbon) gives me helper to retreive difference in time in different time unit (hours, minutes, etc), there is no method to calculate it all at the same time. Carbon's `diffForHumans` is pretty close, but there is no control over how it displays information, and what information it displays. 9 | 10 | ## Install 11 | 12 | You can install this package via composer: 13 | 14 | ```bash 15 | $ composer require jpmurray/laravel-countdown 16 | ``` 17 | 18 | ## Usage with Laravel <= 5.5 19 | 20 | You should be using versions of this package that are <= 3.0.0. 21 | 22 | If you are using a version of Laravel that doesn't support package autodiscovery, you will have to add the service provider and facade to your `config/app.php` file. 23 | 24 | Edit file: `config/app.php` 25 | 26 | ```php 27 | 'providers' => [ 28 | // ... 29 | jpmurray\LaravelCountdown\CountdownServiceProvider::class, 30 | // ... 31 | ]; 32 | 33 | // ... 34 | 35 | 'aliases' => [ 36 | // ... 37 | 'Countdown' => jpmurray\LaravelCountdown\Facades\CountdownFacade::class, 38 | // ... 39 | ]; 40 | ``` 41 | 42 | ## Usage 43 | 44 | ```php 45 | 46 | use jpmurray\LaravelCountdown\Countdown; 47 | 48 | // To get time from 5 years ago until now, you can do the following. 49 | // Note that you can send a string to the from and to methods, we will 50 | // try to parse it with Carbon behind the scene 51 | $now = Carbon::now(); 52 | 53 | $countdown = Countdown::from($now->copy()->subYears(5)) 54 | ->to($now)->get(); 55 | 56 | // The above will return the Countdown class where you can access the following values. 57 | // Those mean that from 5 years ago to now, there is 5 years, 1 week, 1 day, 2 hours 15 minutes and 23 seconds 58 | 59 | $countdown->years; // 5 60 | $countdown->weeks; // 1 61 | $countdown->days; // 1 62 | $countdown->hours; // 2 63 | $countdown->minutes; // 15 64 | $countdown->seconds; // 23 65 | 66 | // It will of course, also work in reverse order of time. 67 | // This will get the time between now and some future date 68 | $countdown = Countdown::from($now) 69 | ->to($now->copy()->addYears(5)) 70 | ->get(); 71 | 72 | // To return to humans string 73 | $countdown->toHuman(); // 18 years, 33 weeks, 2 days, 18 hours, 4 minutes and 35 seconds 74 | 75 | // You to can pass custom string to parse in method toHuman, like this: 76 | $countdown->toHuman('{days} days, {hours} hours and {minutes} minutes'); // 2 days, 18 hours, 4 minutes 77 | ``` 78 | 79 | ## Eloquent Trait 80 | 81 | ```php 82 | // For convenience, we provide a trait that you can add to any model in your Laravel app that provides 83 | // quick methods to get the values of time between dates. For example: 84 | 85 | use jpmurray\LaravelCountdown\Traits\CalculateTimeDiff; 86 | 87 | class User extends Authenticatable 88 | { 89 | use Notifiable, CalculateTimeDiff; 90 | //... 91 | } 92 | ``` 93 | 94 | #### Example to use Trait: 95 | 96 | ```php 97 | // This enables the following: 98 | // You should have casted your attributes to dates beforehand 99 | $user = User::find(1); 100 | $user->elapsed('trial_ends_at'); // get the time elapsed between the date in attribute trial_ends_at to now 101 | $user->until('trial_ends_at'); // get the time from now until the date in attribute trial_ends_at 102 | ``` 103 | 104 | ## Tests 105 | 106 | ```bash 107 | composer run test 108 | ``` 109 | 110 | ## Change log 111 | 112 | Please see [CHANGELOG](changelog.MD) for more information on what has changed recently. 113 | 114 | ## Credits 115 | 116 | - [Jean-Philippe Murray](https://github.com/jpmurray) 117 | - [All Contributors](../../contributors) 118 | 119 | ## License 120 | 121 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 122 | -------------------------------------------------------------------------------- /tests/CountdownTest.php: -------------------------------------------------------------------------------- 1 | toDateTimeString()) 17 | ->to($end->toDateTimeString()) 18 | ->get(); 19 | 20 | $this->makeAsserts($countdown); 21 | } 22 | 23 | /** 24 | * @dataProvider providerDates 25 | */ 26 | public function testGetStringForHumansRead($start, $end) 27 | { 28 | $countdown = Countdown::from($start->toDateTimeString()) 29 | ->to($end->toDateTimeString()) 30 | ->get(); 31 | 32 | $this->assertEquals( 33 | '18 years, 33 weeks, 2 days, 18 hours, 4 minutes and 35 seconds', 34 | $countdown->toHuman() 35 | ); 36 | } 37 | 38 | /** 39 | * @dataProvider providerDates 40 | * @expectedException jpmurray\LaravelCountdown\Exceptions\InvalidPropertyStringForHumanException 41 | */ 42 | public function testExceptionStringForHumansHasInvalidProperty($start, $end) 43 | { 44 | $countdown = Countdown::from($start->toDateTimeString()) 45 | ->to($end->toDateTimeString()) 46 | ->get(); 47 | 48 | $customStringForHuman = '{hours} years, {invalid} weeks, {days} days'; 49 | 50 | $countdown->toHuman($customStringForHuman); 51 | } 52 | 53 | /** 54 | * @dataProvider providerDates 55 | */ 56 | public function testGetStringForHumansReadWithCustomSentence($start, $end) 57 | { 58 | $countdown = Countdown::from($start->toDateTimeString()) 59 | ->to($end->toDateTimeString()) 60 | ->get(); 61 | 62 | $customStringForHuman = '{hours} years, {weeks} weeks, {days} days'; 63 | 64 | $this->assertEquals( 65 | '18 years, 33 weeks, 2 days', 66 | $countdown->toHuman($customStringForHuman) 67 | ); 68 | } 69 | 70 | /** 71 | * [makeAsserts description] 72 | * @param \jpmurray\LaravelCountdown\Countdown $countdown [description] 73 | * @return [type] [description] 74 | */ 75 | public function makeAsserts(\jpmurray\LaravelCountdown\Countdown $countdown) 76 | { 77 | $this->assertEquals(7, $countdown->years); 78 | $this->assertEquals(33, $countdown->weeks); 79 | $this->assertEquals(2, $countdown->days); 80 | $this->assertEquals(18, $countdown->hours); 81 | $this->assertEquals(4, $countdown->minutes); 82 | $this->assertEquals(35, $countdown->seconds); 83 | } 84 | 85 | /** 86 | * @dataProvider providerDates 87 | */ 88 | public function testCountdownFromCarbonInstance($start, $end) 89 | { 90 | $countdown = Countdown::from($start) 91 | ->to($end) 92 | ->get(); 93 | 94 | $this->makeAsserts($countdown); 95 | } 96 | 97 | /** 98 | * @dataProvider providerDates 99 | * @expectedException jpmurray\LaravelCountdown\Exceptions\InvalidDateFormatToCountdown 100 | */ 101 | public function testWithInvalidDateToParse($start) 102 | { 103 | $countdown = Countdown::from((new \stdClass())) // invalid object or 104 | ->to('any invalid string') // invalid string 105 | ->get(); 106 | 107 | $this->makeAsserts($countdown); 108 | } 109 | 110 | /** 111 | * @dataProvider providerDates 112 | */ 113 | public function testWithDateTimeInterface($start) 114 | { 115 | $end = new \DateTime('2007-08-14 08:05:10'); 116 | 117 | $countdown = Countdown::from($start) // invalid object or 118 | ->to($end) // invalid string 119 | ->get(); 120 | 121 | $this->makeAsserts($countdown); 122 | } 123 | 124 | /** 125 | * @dataProvider providerDates 126 | */ 127 | public function testWithTimestampDate($start) 128 | { 129 | $countdown = Countdown::from($start) 130 | ->to(1187078710) 131 | ->get(); 132 | 133 | $this->makeAsserts($countdown); 134 | } 135 | 136 | /** 137 | * @dataProvider providerDates 138 | */ 139 | public function testWithStandardDateFormat($start) 140 | { 141 | $end = '2007-08-14'; 142 | 143 | $countdown = Countdown::from($start) 144 | ->to($end) 145 | ->get(); 146 | 147 | $this->assertEquals(7, $countdown->years); 148 | $this->assertEquals(33, $countdown->weeks); 149 | $this->assertEquals(2, $countdown->days); 150 | $this->assertEquals(9, $countdown->hours); 151 | $this->assertEquals(59, $countdown->minutes); 152 | $this->assertEquals(25, $countdown->seconds); 153 | } 154 | 155 | /** 156 | * Testing without dates 157 | */ 158 | public function testWitDefaultDates() 159 | { 160 | $countdown = Countdown::get(); 161 | 162 | $this->assertEquals(0, $countdown->seconds); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/Countdown.php: -------------------------------------------------------------------------------- 1 | timezone = $timezone; 42 | $this->carbon = $carbon->now($this->timezone); 43 | } 44 | 45 | /** 46 | * Sets the time to count from 47 | * 48 | * @param string|integer|DateTime|Carbon $time 49 | * 50 | * @return self 51 | */ 52 | public function from($time): self 53 | { 54 | $this->from = $this->asDateTime($time); 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * Sets the time to count to 61 | * 62 | * @param string|integer|DateTime|Carbon $time 63 | * 64 | * @return self 65 | */ 66 | public function to($time = null): self 67 | { 68 | $time ?: $this->carbon; 69 | 70 | $this->to = $this->asDateTime($time); 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Returns the object containing the values for the countdown 77 | * 78 | * @return object 79 | */ 80 | public function get() 81 | { 82 | if (is_null($this->from)) { 83 | $this->from = $this->carbon; 84 | } 85 | 86 | if (is_null($this->to)) { 87 | $this->to = $this->carbon; 88 | } 89 | 90 | $this->delta = $this->from->diffInSeconds($this->to); 91 | 92 | $this->computeYears() 93 | ->computeWeeks() 94 | ->computeDays() 95 | ->computeHours() 96 | ->computeMinutes() 97 | ->computeSeconds(); 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * Return a timestamp as DateTime object. 104 | * 105 | * @param mixed $value 106 | * 107 | * @return \Carbon\Carbon 108 | */ 109 | protected function asDateTime($value) 110 | { 111 | try { 112 | // If this value is already a Carbon instance, we shall just return it as is. 113 | // This prevents us having to re-instantiate a Carbon instance when we know 114 | // it already is one, which wouldn't be fulfilled by the DateTime check. 115 | if ($value instanceof Carbon) { 116 | return $value; 117 | } 118 | 119 | // If the value is already a DateTime instance, we will just skip the rest of 120 | // these checks since they will be a waste of time, and hinder performance 121 | // when checking the field. We will just return the DateTime right away. 122 | if ($value instanceof DateTimeInterface) { 123 | return $this->carbon->instance($value); 124 | } 125 | 126 | // If this value is an integer, we will assume it is a UNIX timestamp's value 127 | // and format a Carbon object from this timestamp. This allows flexibility 128 | // when defining your date fields as they might be UNIX timestamps here. 129 | if (is_numeric($value)) { 130 | return $this->carbon->createFromTimestamp($value); 131 | } 132 | 133 | // If the value is in simply year, month, day format 134 | if (is_string($value) && $this->isStandardDateFormat($value)) { 135 | return $this->carbon->createFromFormat('Y-m-d', $value)->startOfDay(); 136 | } 137 | 138 | // Finally 139 | return $this->carbon->parse((string)$value); 140 | } catch (Exception $e) { 141 | throw new InvalidDateFormatToCountdown; 142 | } 143 | } 144 | 145 | /** 146 | * Determine if the given value is a standard date format. 147 | * 148 | * @param string $value 149 | * 150 | * @return bool 151 | */ 152 | protected function isStandardDateFormat(string $value): int 153 | { 154 | return preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value); 155 | } 156 | 157 | /** 158 | * Compute the number of seconds for the countdown 159 | * 160 | * @return self 161 | */ 162 | private function computeSeconds(): self 163 | { 164 | $this->seconds = intval(bcmod(intval($this->delta), self::SECONDS_PER_MINUTE)); 165 | 166 | return $this; 167 | } 168 | 169 | /** 170 | * Compute the number of minutes for the countdown 171 | * 172 | * @return self 173 | */ 174 | private function computeMinutes(): self 175 | { 176 | $this->minutes = intval(bcmod((intval($this->delta) / self::SECONDS_PER_MINUTE), self::MINUTES_PER_HOUR)); 177 | 178 | return $this; 179 | } 180 | 181 | /** 182 | * Compute the number of hours for the countdown 183 | * 184 | * @return self 185 | */ 186 | private function computeHours(): self 187 | { 188 | $this->hours = intval(bcmod((intval($this->delta) / self::SECONDS_PER_HOUR), self::HOURS_PER_DAY)); 189 | 190 | return $this; 191 | } 192 | 193 | /** 194 | * Compute the number of days for the countdown 195 | * 196 | * @return self 197 | */ 198 | private function computeDays(): self 199 | { 200 | $this->days = intval(bcmod((intval($this->delta) / self::SECONDS_PER_DAY), self::DAYS_PER_WEEK)); 201 | 202 | return $this; 203 | } 204 | 205 | /** 206 | * Compute the number of weeks for the countdown 207 | * 208 | * @return self 209 | */ 210 | private function computeWeeks(): self 211 | { 212 | $this->weeks = intval(bcmod((intval($this->delta) / self::SECONDS_PER_WEEK), self::WEEKS_PER_YEAR)); 213 | 214 | return $this; 215 | } 216 | 217 | /** 218 | * Compute the number of years for the countdown 219 | * 220 | * @return self 221 | */ 222 | private function computeYears(): self 223 | { 224 | $this->years = intval(intval($this->delta) / self::SECONDS_PER_YEAR); 225 | 226 | return $this; 227 | } 228 | 229 | /** 230 | * Fill string with countdown numbers 231 | * 232 | * @param string $string string for fill 233 | * @throws \jpmurray\LaravelCountdown\Exceptions\InvalidPropertyStringForHumanException 234 | * @return string 235 | */ 236 | private function getStringForHumanRead(string $string): string 237 | { 238 | // search regex 239 | preg_match_all( 240 | '/{(.*?)}/', 241 | $string, 242 | $matches 243 | ); 244 | 245 | $peaces = $matches[1]; 246 | $filled = []; 247 | 248 | foreach ($peaces as $key => $peace) { 249 | // Check first class has property 250 | if (!property_exists($this, $peace)) { 251 | throw new InvalidPropertyStringForHumanException; 252 | } 253 | 254 | $filled[$matches[0][$key]] = $this->{$peace}; 255 | } 256 | 257 | $string = str_replace(array_keys($filled), array_values($filled), $string); 258 | 259 | return $string; 260 | } 261 | 262 | /** 263 | * Return string with countdown to human read 264 | * 265 | * @param string $custom Custom string to parse 266 | * @return string 267 | */ 268 | public function toHuman(string $custom = null): string 269 | { 270 | $sentence = ($custom ?: static::STRING_FOR_HUMAN); 271 | 272 | return $this->getStringForHumanRead($sentence); 273 | } 274 | } 275 | --------------------------------------------------------------------------------