Get up to 80 % extra points for free! More info:

Lesson 3 - Finishing DateUtils library for PHP

In the previous lesson, DateUtils library for formatting date and time in PHP, we started working on a simple utility class used for working with date and time in PHP. In today's tutorial, we'll finish up with it.

Pretty date and time

I'm not sure if there is any specific name for this date and time format. However, I have implemented it as prettyDateTime() and prettyDate() methods. If the date is today, yesterday or tomorrow, it'll be written out in words, e.g. "Today". If it's sometime this year, the month will be written out in words, e.g. February 15. If the year is different than the current one, the entire date will be written in digits, e.g. 02/15/2013. Eventually, the time will be added to it as well. Surely you agree that this way is more pleasing to users than a bunch of numbers. When we're done, this library will let you use it with great ease.

First of all, let's create a private method which converts a given DateTime instance to a pretty date:

private static function getPrettyDate($dateTime)
{
    $now = new DateTime();
    if ($dateTime->format('Y') != $now->format('Y'))
        return $dateTime->format(self::DATE_FORMAT);
    $dayMonth = $dateTime->format('d-m');
    if ($dayMonth == $now->format('d-m'))
        return "Today";
    $now->modify('-1 DAY');
    if ($dayMonth == $now->format('d-m'))
        return "Yesterday";
    $now->modify('+2 DAYS');
    if ($dayMonth == $now->format('d-m'))
        return "Tomorrow";
    return self::$months[$dateTime->format('n') - 1] . ' ' . $dateTime->format('d');
}

In the method, we save the current date. If the years of the current and the given dates differ, we'll return the numeric date including the year. If the day and the month are the same, we'll return "Today". Then, we change the current date and check whether it isn't yesterday or tomorrow. If we exhaust all of the possibilities, we return the day and month as words.

Now, let's add two public methods which will format a date to the desired format:

public static function prettyDate($date)
{
    return self::getPrettyDate(self::getDateTime($date));
}

public static function prettyDateTime($date)
{
    $dateTime = self::getDateTime($date);
    return self::getPrettyDate($dateTime) . $dateTime->format(' H:i:s');
}

Let's test it all out by converting several different dates into different formats:

echo(DateUtils::prettyDate(1446906152) . '<br />');
echo(DateUtils::prettyDateTime('2016-09-19 10:50') . '<br />');
echo(DateUtils::prettyDateTime('2016-09-18 10:50') . '<br />');
echo(DateUtils::prettyDate('20.9.2016') . '<br />');
echo(DateUtils::prettyDate('2016-09-01') . '<br />');
echo(DateUtils::prettyDate('2015/09/25') . '<br />');

The result:

Your page
localhost

As you can see, our method formats absolutely anything to a human-friendly format.

Parsing

Let's move on to the parsing method. We'll give it regional data as the input (e.g. 01/15/2014) as well as the format in which the date is supposed to be. The method will either throw an exception or return the date in the database format.

public static function parseDateTime($date, $format = self::DATETIME_FORMAT)
{
    if (mb_substr_count($date, ':') == 1)
        $date .= ':00';
    // Removes spaces around separators
    $a = array('/([\.\:\/])\s+/', '/\s+([\.\:\/])/', '/\s{2,}/');
    $b = array('\1', '\1', ' ');
    $date = trim(preg_replace($a, $b, $date));
    // Removes zeroes before numbers
    $a = array('/^0(\d+)/', '/([\.\/])0(\d+)/');
    $b = array('\1', '\1\2');
    $date = preg_replace($a, $b, $date);
    // Creates a DateTime instance which determines whether the given date exists
    $dateTime = DateTime::createFromFormat($format, $date);
    $errors = DateTime::getLastErrors();
    // Throwing validation exceptions
    if ($errors['warning_count'] + $errors['error_count'] > 0)
    {
        if (in_array($format, self::$errorMessages))
            throw new InvalidArgumentException(self::$errorMessages[$format]);
        else
            throw new InvalidArgumentException('Invalid value');
    }
    // Returning the value in MySQL format
    return $dateTime->format(self::$formatDictionary[$format]);
}

In the method, we concatenate "00" seconds to the date string in case there is exactly one colon. In that case, the user entered a time without specifying seconds. Then, we remove white characters before separators which are ":" and "/". Thanks to that, dates like "01/15/ 2014 12:00" pass and are converted to the following format, "01/15/2015 12:00:00". This means that the resulting date and time format is always left without white spaces and includes seconds.

We can pass this format to the DateTime class which performs the validation for us. You may find other desperate solutions, on the internet, where people call explode() on the date or other similar approaches. These validations are always incomplete, especially, the range control or control of component presence is quite complicated. Never program something which is already a part of the standard library for the given language! You'll never do it as well since PHP is written in the C language. Even if you do so, you could have used all of that time on something more efficient.

Then, we set the format for the DateTime instance, pass our date to it, and it'll try to parse it. Any errors that occur will be returned using the getLastErrors() method. If there are any, we throw an exception according to the format, otherwise, we return the date in the database format. We'll catch the exception later when we process the data and display it's message out to the user. In this case, it'd be ideal to create a custom exception.

Let's try our method out:

$date = DateUtils::parseDateTime('2/14/ 2016', DateUtils::DATE_FORMAT);
echo($date . '<br />');
$dateTime = DateUtils::parseDateTime('02/24/ 2016 10:30', DateUtils::DATETIME_FORMAT);
echo($dateTime . '<br />');
$time = DateUtils::parseDateTime('10:30', DateUtils::TIME_FORMAT);
echo($time . '<br />');

The result:

Your page
localhost

Date validation

For the sake of completeness let's also add a validDate() method which will determine whether a given date is valid:

public static function validDate($date, $format = self::DATETIME_FORMAT)
{
    try
    {
        self::parseDateTime($date, $format);
        return true;
    }
    catch (InvalidArgumentException $e)
    {
    }
    return false;
}

Let's test the method out by entering several dates:

var_dump(DateUtils::validDate('2/24/ 2016', DateUtils::DATETIME_FORMAT));
var_dump(DateUtils::validDate('2/24 16', DateUtils::DATE_FORMAT));
var_dump(DateUtils::validDate('2/29/ 2014', DateUtils::DATE_FORMAT));
var_dump(DateUtils::validDate('2/ 29/ 2012', DateUtils::DATE_FORMAT));
var_dump(DateUtils::validDate('14:56', DateUtils::TIME_FORMAT));
var_dump(DateUtils::validDate('14:62', DateUtils::TIME_FORMAT));

The result:

Your page
localhost

The method works as expected!

Now()

Since MySQL is not able to set the current date and time for the DateTime column when inserting a new row automatically, I often use the following method, which returns the current date and time in the database format:

public static function dbNow()
{
    $dateTime = new DateTime();
    return $dateTime->format(self::DB_DATETIME_FORMAT);
}

You may think that I'm a barbarian of sorts because I don't use the native NOW() function, however, this minor flaw is balanced out by the advantages of inserting a row straight from an array. By the way, the array will come straight from a form framework. The query will be generated automatically and will spare us a lot of work. If you've ever worked with ICT social's PDO wrapper, you know what I'm talking about. If you haven't, we'll get to it when we get to the form library.

Test this method out as well:

echo(DateUtils::dbNow());

The result:

Your page
localhost

The documented library is available for download below, I hope you'll put it to good use :) I'm looking forward to seeing you all in the next lesson, StringUtils library for working with texts in PHP, where we'll start writing a similar utility class for strings.


 

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.

Download

By downloading the following file, you agree to the license terms

Downloaded 24x (6.75 kB)
Application includes source codes in language PHP

 

Previous article
DateUtils library for formatting date and time in PHP
All articles in this section
Libraries for PHP
Skip article
(not recommended)
StringUtils library for working with texts in PHP
Article has been written for you by David Capka Hartinger
Avatar
User rating:
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities