Working with PHP's DateTime for Periods of Time

Tags:
h2>Source Code
<?php

/* Today */
$todayStart =  new DateTime ( "midnight today" );
$todayEnd= new DateTIme ("midnight tomorrow");
echo 'Today Start: ' . $todayStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Today End: ' . $todayEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

/* Tomorrow */
$tomorrowStart = new DateTime ( "midnight tomorrow" );
$tomorrowEnd = clone $tomorrowStart;
$tomorrowEnd->modify ( '+1 day' );

echo 'Tomorrow Start: ' . $tomorrowStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Tomorrow End: ' . $tomorrowEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

/* Yesterday */
$yesterdayStart = new DateTime ( "midnight yesterday" );
$yesterdayEnd = clone $yesterdayStart;
$yesterdayEnd->modify ( '+1 day' );

echo 'Yesterday Start: ' . $yesterdayStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Yesterday End: ' . $yesterdayEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

/* Last Week */
$lastWeekStart = new DateTime ( "midnight monday last week" );
$lastWeekEnd = clone $lastWeekStart;
$lastWeekEnd->modify ( '+1 week' );

echo 'Last Week Start: ' . $lastWeekStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Last Week End: ' . $lastWeekEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

/* Two Weeks Ago */
$twoWeeksAgoStart = new DateTime ( "midnight monday 2 weeks ago" );
$twoWeeksAgoEnd = clone $twoWeeksAgoStart;
$twoWeeksAgoEnd->modify ( '+1 week' );

echo 'Two Weeks Ago Start: ' . $twoWeeksAgoStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Two Weeks Ago End: ' . $twoWeeksAgoEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

/* Three Weeks Ago */
$threeWeeksAgoStart = new DateTime ( "midnight monday 3 weeks ago" );
$threeWeeksAgoEnd = clone $threeWeeksAgoStart;
$threeWeeksAgoEnd->modify ( '+1 week' );

echo 'Three Weeks Ago Start: ' . $threeWeeksAgoStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Three Weeks Ago End: ' . $threeWeeksAgoEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

/* Last month */
$lastMonthStart = new DateTime ( "midnight first day of last month" );
$lastMonthEnd = clone $lastMonthStart;
$lastMonthEnd->modify ( '+1 month' );

echo 'Last Month Start: ' . $lastMonthStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Last Month End: ' . $lastMonthEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

echo '<h2>Quarters</h2>';
$firstQuarterStart = new DateTime ( 'first day of January' );
$firstQuaterEnd = clone $firstQuarterStart;
$firstQuaterEnd->modify ( "+3 months" );

echo 'First Quarter Start: ' . $firstQuarterStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'First Quarter End: ' . $firstQuaterEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

$secondQuarterStart = new DateTime ( 'first day of April' );
$secondQuarterEnd = clone $secondQuarterStart;
$secondQuarterEnd->modify ( '+3 months' );

echo 'Second Quarter Start: ' . $secondQuarterStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Second Quarter End: ' . $secondQuarterEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

$thirdQuarterStart = new DateTime ( 'first day of July' );
$thirdQuarterEnd = clone $thirdQuarterStart;
$thirdQuarterEnd->modify ( '+3 months' );

echo 'Third Quarter Start: ' . $thirdQuarterStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Third Quarter End: ' . $thirdQuarterEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

$forthQuarterStart = new DateTime ( 'first day of October' );
$forthQuarterEnd = new DateTime ( 'first day of January next year' );

echo 'Forth Quarter Start: ' . $forthQuarterStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'Forth Quarter End: ' . $forthQuarterEnd->format ( 'Y-m-d H:i:s' ) . '<br><br>';

echo '<h2>Year To Date </h2>';
$ytdStart = new DateTime ( 'first day of January' );
$now = new DateTime ();

echo 'YTD Start: ' . $ytdStart->format ( 'Y-m-d H:i:s' ) . '<br>';
echo 'YTD End (now): ' . $now->format ( 'Y-m-d H:i:s' ) . '<br><br>';

This code is subject to change at any time.

Weeks and Days

Today Start: 2021-12-05 00:00:00
Today End: 2021-12-06 00:00:00

Tomorrow Start: 2021-12-06 00:00:00
Tomorrow End: 2021-12-07 00:00:00

Yesterday Start: 2021-12-04 00:00:00
Yesterday End: 2021-12-05 00:00:00

Last Week Start: 2021-11-22 00:00:00
Last Week End: 2021-11-29 00:00:00

Two Weeks Ago Start: 2021-11-15 00:00:00
Two Weeks Ago End: 2021-11-22 00:00:00

Three Weeks Ago Start: 2021-11-08 00:00:00
Three Weeks Ago End: 2021-11-15 00:00:00

Last Month Start: 2021-11-01 00:00:00
Last Month End: 2021-12-01 00:00:00

Quarters

First Quarter Start: 2021-01-01 00:00:00
First Quarter End: 2021-04-01 00:00:00

Second Quarter Start: 2021-04-01 00:00:00
Second Quarter End: 2021-07-01 00:00:00

Third Quarter Start: 2021-07-01 00:00:00
Third Quarter End: 2021-10-01 00:00:00

Forth Quarter Start: 2021-10-01 00:00:00
Forth Quarter End: 2022-01-01 00:00:00

Year To Date

YTD Start: 2021-01-01 00:00:00
YTD End (now): 2021-12-05 22:11:27

Disclaimer as to purpose

Some of you may believe that the ending dates all wrong. Well, that depends entirely upon perspective.

Each of the dates shown should be understood to begin at the start of the second, whereas our assumption might be that the ending date would be at the end. For database queries this is typically not desired. Essentially, this can leave an entire second for each query unaccounted for. An eight-core, Xeon-based, dedicated server can process thousands of transactions within the missing second and a poorly considered / constructed date range will produce inaccurate reports.

What must be created is a means to include all the transactions within a time frame which encompasses even those which are measured in the thousandths of the very last second in the query.

Database usage

The start date should be applied using a greater than or equal consideration.

The ending date should be applied using a straight-forward less than consideration.

Usages in xPDO

<?php
/**
 * Created by PhpStorm.
 * User: W. Shawn Wilkerson
 * Date: 5/10/2018
 * Time: 13:57
 */

namespace SanityLLC\Xxxxxxxxxxx;

use DateInterval;
use DateTime;
use Exception;

trait Utils
{

    /**
     * Increments the given date by number of days.
     * @param DateTime $datetime
     * @param int $days The number of days to add to the date.
     * @return DateTime|null
     * @throws Exception
     * @uses addIntervalToDate()
     */
    public function addDaysToDate(DateTime $datetime, int $days): ?DateTime
    {
        return $this->addIntervalToDate($datetime, 'P' . $days . 'D');
    }

    /**
     * Increments the given date by number of hours.
     * @param DateTime $datetime
     * @param int $hours The number of hours.
     * @return DateTime|null
     * @throws Exception
     * @uses addIntervalToDate()
     */
    public function addHoursToDate(DateTime $datetime, int $hours): ?DateTime
    {
        return $this->addIntervalToDate($datetime, 'PT' . $hours . 'H');
    }

    /**
     * Adjusts the date by the interval.
     * @param DateTime $dateTime An instance of a date representation.
     * @param string $interval A formatted interval (i.g.
     *
     * @return DateTime|null
     * @throws Exception
     * @see  \DateTime::add
     * @uses \DateInterval
     */
    public function addIntervalToDate(DateTime $dateTime, string $interval): ?DateTime
    {
        try {
            $x = $dateTime->add(new DateInterval($interval));
        } catch (Exception $e) {
            $this->logEvent(false, $e->getMessage());
        }
        return $x ?? null;
    }

    /**
     * Increments the given date by number of minutes.
     * @param DateTime $datetime
     * @param int $value The number of days to add to the date.
     * @return DateTime|null
     * @throws Exception
     * @uses addIntervalToDate()
     */
    public function addMinutesToDate(DateTime $datetime, int $value): ?DateTime
    {
        return $this->addIntervalToDate($datetime, 'PT' . $value . 'M');
    }

    /**
     * Increments the given date by number of months.
     * @param DateTime $datetime
     * @param int $value The number of days to add to the date.
     * @return DateTime|null
     * @throws Exception
     */
    public function addMonthsToDate(DateTime $datetime, int $value): ?DateTime
    {
        return $this->addIntervalToDate($datetime, 'P' . $value . 'M');
    }

    /**
     * Increments the given date by number of seconds.
     * @param DateTime $datetime
     * @param int $seconds The number of days to add to the date.
     * @return DateTime|null
     * @throws Exception
     * @uses addIntervalToDate()
     */
    public function addSecondsToDate(DateTime $datetime, int $seconds): ?DateTime
    {
        return $this->addIntervalToDate($datetime, 'PT' . $seconds . 'S');
    }

    /**
     * Increments the given date by number of weeks.
     * @param DateTime $datetime
     * @param int $weeks The number of days to add to the date.
     * @return DateTime|null
     * @throws Exception
     * @uses addIntervalToDate()
     */
    public function addWeeksToDate(DateTime $datetime, int $weeks): ?DateTime
    {
        return $this->addIntervalToDate($datetime, 'P' . $weeks . 'W');
    }

    /**
     * Retrieves the difference
     * @param DateTime $date1
     * @param DateTime $date2
     * @param string $format Defaults to 'y' for years.
     * @return string The formatted difference.
     * @throws Exception
     * @uses \DateTime::diff()
     */
    public function calculateDifferenceBetweenDates(DateTime $date1, DateTime $date2, string $format = 'y'): string
    {
        $out = '';
        try {
            $out = $date1->diff($date2);
        } catch (Exception $e) {
            echo $e->getMessage();
        }
        return ($out) ? $out->{$format} : $out;
    }

    /**
     * Translates the specified string.
     * @param string $string A string representation of a date, date time, etc.
     * @return null|DateTime
     * @throws Exception
     * @uses \DateTime::format()
     * @see https://secure.php.net/manual/en/datetime.createfromformat.php
     */
    public function createDateObject(string $string = ''): ?DateTime
    {
        try {
            $dateLength = strlen($string);
            if (is_numeric($string)) {
                $obj = DateTime::createFromFormat('U', $string);
            } elseif ($string == 'now' || empty ($string)) {
                $obj = new DateTime ();
            } elseif ($string == '-1-11-30 00:00:00' || $string == '-0001-11-30 00:00:00') {
                $obj = DateTime::createFromFormat('Y-m-d H:i:s', '00-00-00 00:00:00');
            } elseif ($dateLength == 10) {
                $obj = DateTime::createFromFormat('Y-m-d', $string);
            } elseif ($dateLength == 19) {
                $obj = DateTime::createFromFormat('Y-m-d H:i:s', $string);
            } elseif ($dateLength == 26) {
                $obj = DateTime::createFromFormat('Y-m-d H:i:s.u', $string);
            }
        } catch (Exception $e) {
            $this->logEvent(false, $e->getMessage());
        }
        return $obj ?? null;
    }

    /**
     * Formats the specified date into Y-m-d H:i:s.u
     * @param DateTime $dateTime
     * @return string
     */
    public function formatMicroDate(DateTime $dateTime):string
    {
        return $dateTime->format('Y-m-d H:i:s.u');
    }

    /**
     * Formats the specified date into Y-m-d H:i:s
     * @param DateTime $dateTime
     * @return string
     */
    public function formatStandardDate(DateTime $dateTime):string
    {
        return $dateTime->format('Y-m-d H:i:s');
    }
}