Lesson 2 - DateUtils library for formatting date and time in PHP
In the previous lesson, Introduction to libraries and frameworks for PHP, we explained why it's necessary to use libraries in PHP and clarified that these sort of libraries are gathered to make up frameworks.
In today's tutorial, we'll create our first PHP library - DateUtils. Before creating each library, we'll talk shortly about why we're actually creating it.
Motivation
In PHP, we usually receive dates from the database in the following format: yyyy-mm-dd. As I'm sure you understand, we usually need to display said date to the user in their regional format. In order to do that using regular PHP functions, we'd have to remember formats like "m/d/Y H:i:s". In other words, we would have to look them up in the PHP manual over and over again. Although we could theoretically format the date in the database, presentation logic doesn't belong there because it would cause problems if we ever wanted to use that value as an input for a function. Also, ideally, we'd like to print the date in these formats, "Today 15:15" or "January 15".
Without the use of the DatePicker from jQuery, a user sends us the date in his regional format as well, which we would need to parse into the database format. As you may have guessed, we also need to check whether the user entered a valid value. Meaning that simply having the right format won't do, we'll have to take other cases into account, i.e. if a user enters February 29th when it's not a leap year.
DateUtils
In order to provide these simple functions, we'll need to create a DateUtils class. It will be a utility class without an inner state. We'll use it often and from different places in the application. With all of these requirements in mind, the best thing we could do is make it static.
If you're using namespaces, put them into the Utilities namespace. Later, we'll create similar classes such as StringUtils, ArraysUtils, and so on.
class DateUtils
{
}
Preparing constants
Let's add several constants into our class, store their formats, and use them for conversions. The first two formats will be for local regional date and time, regional date and regional time (I'll use the American formats in this case). As you all should know at this point, H:i:s are hours:minutes:seconds. You may look up what any given symbol means in the PHP manual.
const DATETIME_FORMAT = 'm/d/Y G:i:s'; const DATE_FORMAT = 'm/d/Y'; const TIME_FORMAT = 'G:i:s'; const DB_DATETIME_FORMAT = 'Y-m-d H:i:s'; const DB_DATE_FORMAT = 'Y-m-d'; const DB_TIME_FORMAT = 'H:i:s';
Comment the code properly using PHPDoc.
Auxiliary arrays
Next, we'll add three auxiliary arrays. These will have to be static in order for them to be accessible from static methods.
We'll insert month names into the first array, and error messages for particular formats into the second one. The third array will be used to specify how we wish to convert between formats to make the keep the result compatible with the database format. This way of working with date and time is very efficient for the entire application. Keep in mind that a database is the alpha and omega of the entire application, whereas, PHP is just a support language.
private static $months = array('January', 'February', 'March', 'April', 'May', 'Jun', 'July', 'August', 'September', 'October', 'November', 'December'); private static $errorMessages = array( self::DATE_FORMAT => 'Invalid date, please, enter the date in the mm/dd/yyyy format', self::TIME_FORMAT => 'Invalid time, please, enter the time in the hh:mm format, you may also include seconds', self::DATETIME_FORMAT => 'Invalid date and time, please, enter the value in the mm/dd/yyyy hh:mm format, you may also include seconds', ); private static $formatDictionary = array( self::DATE_FORMAT => self::DB_DATE_FORMAT, self::DATETIME_FORMAT => self::DB_DATETIME_FORMAT, self::TIME_FORMAT => self::DB_TIME_FORMAT, );
Formatting date and time
We'll use the DateTime PHP class to work date and time, since it's truly genius work and you should definitely know a good bit about it.
Now, add this auxiliary method which creates a DateTime instance:
public static function getDateTime($date) { if (ctype_digit($date)) $date = '@' . $date; return new DateTime($date); }
The purpose of this method is to make our class universal and make it able to format a date and time when it comes as an integer as well (as a number of seconds since the UNIX epoch). We check whether the date is a number using the ctype_digit() function. If so, we prefix the string with "@". The DateTime class uses the "@" sign to specify the UNIX timestamp, which will let us know how to parse it. Keep in mind that this kind of format is kind of unpleasant to work with, so you'd better avoid it in your applications. However, for our intents and purposes, we'll keep things universal.
Regional format
We'll add two simple methods which format date and time and date only from any format to our local regional format (e.g. 1/15/2014):
public static function formatDate($date) { $dateTime = self::getDateTime($date); return $dateTime->format(self::DATE_FORMAT); } public static function formatDateTime($date) { $dateTime = self::getDateTime($date); return $dateTime->format(self::DATETIME_FORMAT); }
We'll be able to use these methods, from the views, to format dates from database results. We'll create a FormatHelper class for these purposes, in the future, which will use the DateUtils class.
Now, let's take the logic for a spin:
require_once('Utility/DateUtils.php'); echo(DateUtils::formatDate(1459996152) . '<br />'); echo(DateUtils::formatDateTime('2016-02-23 10:50') . '<br />'); echo(DateUtils::formatDate('02/24/2016') . '<br />'); echo(DateUtils::formatDate('2015-02-01') . '<br />'); echo(DateUtils::formatDate('2014/02/20') . '<br />');
The result isn't anything special, however, we're able to convert a date from any form to our regional format:
In the next lesson, Finishing DateUtils library for PHP, we'll implement formatting date and time to the "pretty format", e.g. "Today" or "January 15". We'll finish up with the class by adding a method which validates and parses a date entered in our regional format.