So in my PHP program I'm creating a calendar feature and one of the classes is "CalendarDay". What I want to do is be able to instantiate a new day for each day count, so for example new CalendarDay (22) means a new 22nd of the month date. There is also a show() function used for displaying each day. The class itself functions normally but when I try instantiating new days using recursion it no longer seems to work as everything related to the instantiated object disappears from the webpage.
class CalendarDay{
private $current_month;
private $current_year;
private $current_date;
public $reminderSet;
public $reminders;
public function __construct($current_day_of_month){
$current_year = date("Y");
$current_month = date("m");
$this->days_in_month = cal_days_in_month(CAL_GREGORIAN, $current_month, $current_year);
$this->current_date = date("d");
$this->current_day_of_month = $current_day_of_month;
}
public function show(){
$output = '<div class = "generator>"';
//$output .= $this->current_date;
//$output .= '<h1>' . $this->current_date . '</h1>';
$output .= '</div>';
$output .= $this->current_day_of_month;
echo $output;
}
}
My failed attempt at recursion:
for ($countTo31 == 0; $countTo31 == 31; $countTo31++){
$holder = $countTo31;
$date = new CalendarDay ($holder);
$date->show();
}
For the reference, this original block of code without the recursion works normally:
$holder = $countTo31;
$date = new CalendarDay ($holder);
$date->show();
I'm very confused with what you're trying to accomplish...
You have a "day" class which takes input to initialise a specific day but instead actually works out the current day based on date("Y-m-d");?.. And then outputs the input day anyway?
Honestly, it looks more like you want a "month" object
Initial problems
You use == to define your starting point
== is not an assignment operator, it's a comparison.
It effectively adds an additional iteration of the loop at the start of the loop
for($i == 1; $i < 5; $i++){
echo $i;
}
// Loop 1:
// --> Notice on $i == 1
// --> Notice on $i < 5
// --> Notice on echo $i
// --> Notice on $i++
// Loop 2:
--> $i = 1 BECAUSE of the previous $i++
so the intended loop starts...
Additionaly the loop, in this case, should start with 1 not 0(!)
You use == to define your condition
for loops like yours work effectively as:
for ( A ; B ; C ){
// Do something...
}
// Loop works as:
// Check A against B
// If TRUE then "Do something..."
// and then do C
// IF FALSE then break
However, even if your assignment (A) was correct (i.e. $countTo31 = 1), it still wouldn't work because 1 !== 31 and therfore the loop breaks from the start
It should be $countTo31 <= 31
Looping over object, rewriting the variable
Your code currently rewrites the variable which holds the date object with every loop
Effectively you create an object to output the day, output that data, and instantly remove the object so that it can't be used for anyting else...
Your HTML output has a " in the wrong place:
$output = '<div class = "generator>"';
//Should be...
$output = '<div class = "generator">';
Some of the variables in your class are not assigned or declared properly
$current_month is declared but never assigned
$current_year is declared but never assigned
$current_day_of_month is assigned but not declared
$days_in_month is assigned but not declared
Interim solution
Without further information on what you are intending to do it isn't possible to give good/accurate guidance, so I will leave a working example which should show you what to do:
$days_in_month = cal_days_in_month(
CAL_GREGORIAN,
date("m"),
date("Y")
);
for ($day = 1; $day <= $days_in_month; $day++){
echo "Day {$day}<br>";
}
Proposed changes
It doesn't look as though you really even want a "day" class for the functions you're trying to implement. So, in my mind, it would be better to first create a "month" object with all of the days of the month and then have that generate a "day" object for each day of the month which then can gather the information for each day e.g. reminders.
Doing it this way you can then update each day as you go with, for example, user input or database data.
class Month
{
private $month;
private $year;
private $days = [];
public function __construct($month, $year)
{
$this->month = $month;
$this->year = $year;
$number_of_days = cal_days_in_month(
CAL_GREGORIAN,
$month,
$year
);
for ($i = 1; $i <= $number_of_days; $i++){
$date = "{$this->year}-{$this->month}-{$i}";
// $days[] = new Day($date);
$this->days[$i] = new Day($date);
}
}
public function getDay($day)
{
return $this->days[$day];
}
public function getNumberOfDays()
{
return count($this->days);
}
}
class Day
{
private $date;
private $reminders = [];
public function __construct($date)
{
$this->date = $date;
// Initialise day...
# Get reminders
# Get meetings
# Get bills to pay
}
public function getReminders()
{
return $this->reminders;
}
public function setReminder($content, $time)
{
// Set reminders
$this->reminders[] = [
"content" => $content,
"time" => $time
];
}
public function show()
{
return date("d / m / Y", strtotime($this->date));
}
}
$month = new Month(12, 2020);
for ($i = 1; $i <= $month->getNumberOfDays(); $i++){
echo $month->getDay($i)->show()."<br>";
}
Related
How can i sum times into array using Carbon?
<?php
namespace App\Models;
use Carbon\Carbon;
class Appointment extends BaseModel
{
public static function total_time()
{
$appointments = Appointment::get();
$sumtimes = [];
foreach($appointments as $a){
$dti = Carbon::parse($a->dateinitial);
$dtf = Carbon::parse($a->datefinal);
$time = $dti->diff($dtf)->format('%H:%I:%S');
$sumtimes[] = $time;
}
$sumtimes= sum($sumtimes);
return $sumtimes;
}
inside sum_times, there is a list of times that need to be summed like:
$sum_times[0] = "00:01:18"
$sum_times[1] = "00:03:11"
$sum_times[2] = "01:01:18"
$sum_times[3] = "00:01:28"
I need it to return "01:07:15"
<?php
public static function total_time(): string
{
$seconds = 0;
foreach(Appointment::get() as $appointment){
$dateinitial = Carbon::parse($appointment->dateinitial);
$datefinal = Carbon::parse($appointment->datefinal);
$seconds += $datefinal->diffInSeconds($dateinitial);
}
return gmdate('H:i:s', $seconds);
}
Also you must set for your fields (dateinitial, datefinal) cast datetime for automated parsing to Carbon type. Docs for date casts.
Each result of diff can be continuously added to a datum. At the end of the loop we get the sum as the difference from the base date to the date. Carbon is an extension of DateTime. I show the sample code with the base class so that it is reproducible for everyone.
$data = [
['from' => '2022-03-01 16:00', 'to' => '2022-03-02 12:00'], //20:00:00
['from' => '2022-03-02 12:30', 'to' => '2022-03-02 22:02'], //09:32:00
]; //total 29:32:00
$basis = '2000-01-01';
$dateBase = date_create('2000-01-01');
$date = clone $dateBase;
foreach($data as $dates){
$dateFrom = date_create($dates['from']);
$dateTo = date_create($dates['to']);
$diff = $dateFrom->diff($dateTo);
$date->add($diff);
}
$totalDiff = $dateBase->diff($date);
$hours = $totalDiff->d *24 + $totalDiff->h; //days * 24 + hours
echo 'Sum: '.$hours.$totalDiff->format(':%I:%S');
//Sum: 29:32:00
Try self on 3v4l.org
I am working on an application that has date logic within i.e something will be returned from the database based on the date, and only the relevant data for that date should be returned. How would I go about unit testing this? There are 24 dates that return 24 different pieces of data, I thought maybe doing something like this,
public function testGetActiveData()
{
$this->withoutExceptionHandling();
//Create the data
$dates = CarbonPeriod::create('2019-12-01', '2019-12-12');
$start_date = new Carbon("2019-12-01 00:00:00");
$dates->forEach(function($date){
$data = factory(Data::class)->create([
'date_playable' => $date->toDateTimeString()
]);
factory(LinkedData::class, 15)->create([
'data_id' => $data->id
]);
});
for($day = 1; $day <= 24; $day++) {
if($day = 1) {
Cache::put('data_date', $start_date->toDateTimeString(), now()->addMinutes(30));
} else {
Cache::put('data_date', $start_date->toDateTimeString()->addDays($day), now()->addMinutes(30));
}
$response = $this->json('GET', '/api/data');
$response->assertJson(['date_playable' => Cache::get('data_date')]);
}
}
However this errors due to too many attempts, is there a better way to test this? An obvioulsy I am testing dates in the future so I am pushing a "future date" into cache and then using that is there a better of doing that also?
As described in this comment of a similar problem, you may disable the default Laravel rate limit in your test by applying withoutMiddleware before the TestCase get or json calls.
public function testGetActiveData()
{
$this->withoutExceptionHandling();
//Create the data
$dates = CarbonPeriod::create('2019-12-01', '2019-12-12');
$start_date = new Carbon("2019-12-01 00:00:00");
$dates->forEach(function($date){
$data = factory(Data::class)->create([
'date_playable' => $date->toDateTimeString()
]);
factory(LinkedData::class, 15)->create([
'data_id' => $data->id
]);
});
for($day = 1; $day <= 24; $day++) {
if($day = 1) {
Cache::put('data_date', $start_date->toDateTimeString(), now()->addMinutes(30));
} else {
Cache::put('data_date', $start_date->toDateTimeString()->addDays($day), now()->addMinutes(30));
}
// circumvent the throttle rate limit middleware
// with the "withoutMiddleware" method
$response = $this->withoutMiddleware()->json('GET', '/api/data');
$response->assertJson(['date_playable' => Cache::get('data_date')]);
}
}
I am trying to create an invoice number like - HCL/LF/02/2018 in where the number will be incremented by one for the new invoice. Would someone help me please to get expected invoice_no? I tried something like this in my controller -
public function create()
{
$check = OrderProformaInvoice::orderBy('created_at', 'DESC')->get();
$number = 01;
$year = date('Y');
if (count($check) > 0) {
$invoiceNo = OrderProformaInvoice::latest()->first(['invoice_no']);
$arr = array('HCL','LF', $invoiceNo + 1, $year);
$newInvoiceNo = implode("/",$arr);
}else{
$arr = array('HCL','LF', $number, $year);
$newInvoiceNo = implode("/",$arr);
}
return view('admin.marchendaising.order-proforma-invoices.create', compact('newInvoiceNo'));
}
And in my view form input field is-
<input type="text" name="invoice_no" class="form-control" value="{{ $newInvoiceNo }}">
You have mistake in this line:
$invoiceNo = OrderProformaInvoice::latest()->first(['invoice_no']);
Correct:
$invoiceNo = OrderProformaInvoice::latest()->get()->invoice_no;
You made a mistake here:
$invoiceNo = OrderProformaInvoice::latest()->first(['invoice_no']);
There was no need for this line since you already have a count of all of the OrderProformaInvoice so we may aswell set this to a variable and just increase it for a new one rather than making another call to the database.
If you ever were to delete a database record, you could use
$invoice = OrderProfromaInvoice::orderBy('created_at', 'desc')->first();
Then instead of $numOfInvoices++ you could do $invoice->id++ which will take the ID of the latest invoice and increment it by one.
public function create()
{
$check = OrderProformaInvoice::orderBy('created_at', 'DESC')->get();
$year = date('Y');
$numOfInvoices = count($check)
$arr = array('HCL','LF', $numOfInvoices++, $year);
$newInvoiceNo = implode("/",$arr);
// You could also just do this
$newInvoiceNo = 'HCL/LF/' . $numOfInvoices++ . '/' . $year;
// $newInvoiceNo will equal example: 'HCL/LF/12/2018';
return view('admin.marchendaising.order-proforma-invoices.create', compact('newInvoiceNo'));
}
How to check if one UNIX timestamp range is overlapping another UNIX timestamp range in PHP?
I am developing an application which takes future reservations. But, only one (1) reservation is allowed per period.
Example:
Mr. X has a reservation for a resource from 10:00 A.M. to 12:00 P.M. (noon). Later, Ms. Y wants to reserve that same resource from 8:00 A.M. to 11:00 P.M.. My application should reject Ms. Y's attempted reservation because it overlaps Mr. X's prior reservation.
I am storing the start and end times of existing reservations in UNIX timestamps (integers), but I could convert them into the following format "yyyy-mm-dd hh:mm:ss" if required, or vice versa.
I do not understand how to solve this problem. If I check the new start time with all the existing reservation start times, and the new end time in a similar fashion, the logic will have many if statements and make the application slow.
Would you please help me to solve this issue in an efficient way without using lots of server resources.
Your help is greatly appreciated.
Thanks
Introduction
In other words, you need to do a comparison of all reservation intervals (UNIX timestamps) for a particular resource to determine if a new reservation is valid (within the domain for new reservations).
Step 1
First, a SQL query similar to this might help. While key words like ANY, ALL, NOT, EXISTS and others may seem tempting, it is up to you to decide how much information you need in the event of a scheduling conflict (based on your UI). This query provides the opportunity to extract the maximum amount of information (in PHP, etc ...) about a potential reservation with look ahead forecasting.
// A query like this might help. It's not perfect, but you get the idea.
// This query looks for ALL potential conflicts, starting and ending.
$sql = "SELECT DISTINCT `t1`.`startTime`, `t1`.`endTime`
FROM `reservations` AS `t1`
INNER JOIN `resources` AS `t2`
ON `t1`.`resourceId` = `t2`.`resourceId`
WHERE `t2`.`resourceId` = :resourceId
AND (`t1`.`startTime` BETWEEN :minTime1 AND :maxTime1)
OR (`t1`.`endTime` BETWEEN :minTime2 AND :maxTime2)
ORDER BY `t1`.`startTime` ASC";
Potentially. this will leave you with a multi-dimentional array. The following logic allows you to get a report detailing why the reservation cannot be made. It is up to you to interpret the report in another module.
Step 2
Generalize the solution as a methods of a Reservation class. Depending on your RDBMS, you may be able to do something like this in SQL. Although, it will probably be far less specific and you may want that granularity later. You could send the report in JSON format to a JavaScript front end (just something to think about).
private function inOpenDomain(array $exclusion, $testStart, $testEnd)
{
$result = null;
$code = null;
$start = $exclusion[0];
$end = $exclusion[1];
if (($testStart > $end) || ($testEnd < $start)) {
$result = true;
$code = 0; //Good! No conflict.
} elseif ($testStart === $start) {
$result = false;
$code = 1;
} elseif ($testStart === $end) {
$result = false;
$code = 2;
} elseif ($testEnd === $start) {
$result = false;
$code = 3;
} elseif ($testEnd === $end) {
$result = false;
$code = 4;
} elseif (($testStart > $start) && ($testEnd < $end)) { //Middle
$result = false;
$code = 5;
} elseif (($testStart < $start) && ($testEnd > $start)) { //Left limit
$result = false;
$code = 6;
} elseif (($testStart < $end) && ($testEnd > $end)) { //Right limit
$result = false;
$code = 7;
} elseif (($testStart < $start) && ($testEnd > $end)) { //Both limits
$result = false;
$code = 8;
} else {
$result = false;
$code = 9;
}
return ['start' => $start, 'end' => $end, 'result' => $result => 'code' => $code];
}
Step 3
Make a method that manages the checking of prior reservation times (assuming PDO::FETCH_ASSOC).
private function checkPeriods(array $periods, $newStartTime, $newEndTime)
{
$report = [];
if (!isset($periods[0])) { //If NOT multi-dimensional
$report = inOpenDomain($periods, $newStartTime, $newEndTime)
} else {
for ($i = 0, $length = $count($periods); $i < $length; ++$i) {
$report[$i] = inOpenDomain($periods[$i], $newStartTime, $newEndTime);
}
}
return $report;
}
Step 4
Fashion a method for doing a SELECT on the reservations table using a PDO prepared statement. Generally, ...
private function getReservationTimes($resourceId, $minTime, $maxTime)
{
$sql = "SELECT DISTINCT `t1`.`startTime`, `t1`.`endTime`
FROM `reservations` AS `t1`
INNER JOIN `resources` AS `t2`
ON `t1`.`resourceId` = `t2`.`resourceId`
WHERE `t2`.`resourceId` = :resourceId
AND (`t1`.`startTime` BETWEEN :minTime1 AND :maxTime1)
OR (`t1`.`endTime` BETWEEN :minTime2 AND :maxTime2)
ORDER BY `t1`.`startTime` ASC";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(:resourceId , $resourceId);
$stmt->bindParam(:minTime1 , $minTime);
$stmt->bindParam(:maxTime1 , $maxTime);
$stmt->bindParam(:minTime2 , $minTime);
$stmt->bindParam(:maxTime2 , $maxTime);
$stmt->execute();
return $stmt->fetchAll();
}
Step 5
Make a public method (interface) for the entire process.
public function isOpen($minTime, $maxTime)
{
$periods = $this->getReservationTimes($this->resource->getResourceId(), $minTime, $maxTime);
if (empty($periods)) {
return true; //You may reserve the resource during the time period.
}
return $this->checkPeriods($periods, $this->start, $this->end));
}
Step 6
Separate the concerns.
Create a class hierarchy for the actual items being reserved.
abstact class Product
{
}
class Resource extends Product implements Reservable //Or, something ...
{
private $resourceId;
//etc ....
}
Create a class hierarchy for reservations.
abstract class Interval
{
private $start;
private $end;
public function __construct($start, $end)
{
$this->start = $start;
$this->end = $end;
}
}
class Reservation extends Interval
{
private $db;
private $resource;
public function __construct(PDO $pdo, Reservable $resource, $reqStartTime, $reqEndTime)
{
parent::__construct($reqStartTime, $reqEndTime);
$this->db = $pdo;
$this->resource = $resource;
}
}
Step 7
Run within try/catch
When you instantiate the Reservation object, supply at least a Reservable object, the requested start time, and requested end time (as UNIX timestamps, in this case).
try
{
$day = 84600; // Seconds per day.
$future = $day * 90; // Application specific.
//User requested times.
$reqStartTime = 1488394687 + $day; // Tomorrow.
$reqEndTime = 1488394687 + ($day * 2); // Two day duration.
//Search constraints.
$minTime = time(); // Today. Right now.
$maxTime = 1488394687 + $future; // 90 day look ahead.
$reservation = new Reservation($pdo, $resourceObj, $reqStartTime, $reqEndTime);
$availability = $reservation->isOpen($minTime, $maxTime);
if($availability === true){
$reservation->confirm();
} else {
//Have some other object deal with the report
$reporter = new Reporter($availability);
$reporter->analyzeReport();
//Have some other object update the view, etc ...
}
}
catch(Exception $e)
{
//Handle it.
}
i want to equal youtube playlist all videos time from this link http://gdata.youtube.com/feeds/api/playlists/PLCK7NnIZXn7gGU5wDy9iKOK6T2fwtGL6l. here have time code like this time='00:05:11.500' .. i want to get all videos time from php then its show like this from php
show it like this : 2:10:50 (2=hours,10=minutes,50=seconds)
i want to variable from php for like this one. plzz help for this post thanks. i tried to do that.. but i can do this.. if someone can plz help me.. if have 4 videos, want to equal all videos time and then want to show all duration from php only
Ok, here's an answer that solves the problem assuming you have no code whatsoever, and no intention of
trying do experiment yourself.
You will probably not be able to use this for anything else than the exact problem described:
adding all the durations of this feed together and displaying it as hours:minutes:seconds
<?php
$total_seconds = 0;
$dom = new DOMDocument();
$dom->loadXML(file_get_contents('http://gdata.youtube.com/feeds/api/playlists/PLCK7NnIZXn7gGU5wDy9iKOK6T2fwtGL6l'));
$xpath = new DOMXPath($dom);
foreach ($xpath->query('//yt:duration/#seconds') as $duration) {
$total_seconds += (int) $duration->value;
}
Then you display $total_seconds in your format. Here's two options:
assuming that hours will never be larger than 24
echo gmdate("H:i:s", $total_seconds);
allowing total time to be larger than 24 hours
echo (int) ($total_seconds / 3600) . ':' . (int) ($total_seconds / 60) % 60 . ':' . $total_seconds % 60;
Keep in mind: This code does exactly ZERO error checking. Things that can go wrong:
The PHP configuration may not allow http stream wrapper
The PHP build might not have Dom enabled
The XML feed may be unavailable
The XML feed might not contain any entries
EDIT:
I took a closer look at the feed, and it seems the "time" entries are just pointers for the thumbnails. The actual duration for a video is set in seconds <yt:duration seconds='667'/> so you could just add them together as integers and then use the DateTime class to convert to whatever your format is. Example here.
END EDIT
First of all, to get all the times, you could need an atom feed reader in PHP. There are plenty out there. Do not try to parse the XML, ATOM is a well known standard that should be easily used (if you really only want the times, you could go with an xpath query).
Now that you have all the times at your disposal, you need a way to add them up easily, preferably without messing with nested loops and if-statements.
Here's a class that represents a single time entry for a single video:
final class Duration
{
private $hours;
private $minutes;
private $seconds;
private $centis;
/* we don't want any Durations not created with a create function */
private function __construct() {}
public static function fromString($input = '00:00:00.000') {
$values = self::valuesFromString($input);
return self::fromValues($values['hours'], $values['minutes'], $values['seconds'], $values['centis']);
}
public function addString($string) {
$duration = self::fromString($string);
return $this->addDuration($duration);
}
public function addDuration(Duration $duration) {
// add the durations, and return a new duration;
$values = self::valuesFromString((string) $duration);
// adding logic here
$centis = $values['centis'] + $this->centis;
$this->fixValue($centis, 1000, $values['seconds']);
$seconds = $values['seconds'] + $this->seconds;
$this->fixValue($seconds, 60, $values['minutes']);
$minutes = $values['minutes'] + $this->minutes;
$this->fixValue($minutes, 60, $values['hours']);
$hours = $values['hours'] + $this->hours;
return self::fromValues($hours, $minutes, $seconds, $centis);
}
public function __toString() {
return str_pad($this->hours,2,'0',STR_PAD_LEFT) . ':'
. str_pad($this->minutes,2,'0',STR_PAD_LEFT) . ':'
. str_pad($this->seconds,2,'0',STR_PAD_LEFT) . '.'
. str_pad($this->centis,3,'0',STR_PAD_LEFT);
}
public function toValues() {
return self::valuesFromString($this);
}
private static function valuesFromString($input) {
if (1 !== preg_match('/(?<hours>[0-9]{2}):(?<minutes>([0-5]{1}[0-9]{1})):(?<seconds>[0-5]{1}[0-9]{1}).(?<centis>[0-9]{3})/', $input, $matches)) {
throw new InvalidArgumentException('Invalid input string (should be 01:00:00.000): ' . $input);
}
return array(
'hours' => (int) $matches['hours'],
'minutes' => (int) $matches['minutes'],
'seconds' => (int) $matches['seconds'],
'centis' => (int) $matches['centis']
);
}
private static function fromValues($hours = 0, $minutes = 0, $seconds = 0, $centis = 0) {
$duration = new Duration();
$duration->hours = $hours;
$duration->minutes = $minutes;
$duration->seconds = $seconds;
$duration->centis = $centis;
return $duration;
}
private function fixValue(&$input, $max, &$nextUp) {
if ($input >= $max) {
$input -= $max;
$nextUp += 1;
}
}
}
You can create a new Duration only by calling the static factory fromString(), and that accepts only strings in the form "00:00:00.000" (hours:minutes:seconds.milliseconds):
$duration = Duration::fromString('00:04:16.250');
Next, you can add another string or an actual duration object, to create a new Duration:
$newDuration = $duration->addString('00:04:16.250');
$newDuration = $duration->addDuration($duration);
The Duration object will output it's own duration string in the format '00:00:00.000':
echo $duration;
// Gives
00:04:16.250
Or, if you're interested in the separate values, you can get them like so:
print_r($duration->toValues());
// Gives
Array
(
[hours] => 0
[minutes] => 4
[seconds] => 16
[milliseconds] => 250
)
Final example for using this in a loop to get the total video time:
$allTimes = array(
'00:30:05:250',
'01:24:38:250',
'00:07:01:750'
);
$d = Duration::fromString();
foreach ($allTimes as $time) {
$d = $d->addString($time);
}
echo $d . "\n";
print_r($d->toValues());
// Gives
02:01:45.250
Array
(
[hours] => 2
[minutes] => 1
[seconds] => 45
[milliseconds] => 250
)
For questions on why I used a final class with private constructor:
I wrote this as an exercise for myself, following Mathias Veraes's blog post on "named constructors".
Also, I couldn't resist adding his "TestFrameworkInATweet" as well:
function it($m,$p){echo ($p?'✔︎':'✘')." It $m\n"; if(!$p){$GLOBALS['f']=1;}}function done(){if(#$GLOBALS['f'])die(1);}
function throws($exp,Closure $cb){try{$cb();}catch(Exception $e){return $e instanceof $exp;}return false;}
it('should be an empty duration from string', Duration::fromString() == '00:00:00.000');
it('should throw an exception with invalid input string', throws("InvalidArgumentException", function () { Duration::fromString('invalid'); }));
it('should throw an exception with invalid seconds input string', throws("InvalidArgumentException", function () { Duration::fromString('00:00:61:000'); }));
it('should throw an exception with invalid minutes input string', throws("InvalidArgumentException", function () { Duration::fromString('00:61:00:000'); }));
it('should add milliseconds to seconds', Duration::fromString('00:00:00.999')->addString('00:00:00.002') == Duration::fromString('00:00:01.001'));
it('should add seconds to minutes', Duration::fromString('00:00:59.000')->addString('00:00:02.000') == Duration::fromString('00:01:01.000'));
it('should add minutes to hours', Duration::fromString('00:59:00.000')->addString('00:02:00.000') == Duration::fromString('01:01:00.000'));
it('should add all levels up', Duration::fromString('00:59:59.999')->addString('00:01:01.002') == Duration::fromString('01:01:01.001'));
$duration = Duration::fromString('00:00:01.500');
it('should add a Duration', $duration->addDuration($duration) == '00:00:03.000');