The Joomla! Community Magazine™

Summary for Date Package, Part 2

Written by | Saturday, 01 November 2014 00:00 | Published in 2014 November
Don't like how translations are handled in Joomla\DateTime? Maybe you want to change some functionality for date calculations? Ok, that's not a problem. Let's customize your DateTime objects.

Can I customize a behaviour for DateTime, really?

Yes, you can! But only if you use a Joomla\DateTime. You can change a few things without modification to a DateTime class itself. You can:

  • add new getters,
  • add new parsers,
  • change how since methods are handled,
  • change how translations are handled,
  • change how date calculations are handled.

In the first article I showed how you can add new getters and parsers. In this article I cover the rest.

Since methods

There are two since methods: since() and sinceAlmost(). Both of them produce a nice string for a time difference between dates:

$today = DateTime::today();
echo $today->since(DateTime::yesterday()); // output: '1 day ago'

The difference between them is that sinceAlmost() adds 'almost' prefix and makes some approximations:

$date = new DateTime('2014-08-24 16:00');
echo $today->sinceAlmost(new DateTime('2014-08-24 15:02')); // output: 'almost 1 hour ago'

That's the default functionality and you can easily change it. To do that you need to write your class which will be implementing a SinceInterface:

interface SinceInterface
{
    public function since(DateTime $base, DateTime $datetime = null, $detailLevel = 1);

    public function almost(DateTime $base, DateTime $datetime = null);
}

When you have it you also need to inject your SinceInterface object into DateTime:

use Joomla\DateTime\DateTime;
DateTime::setSince(new MySince());

And that's all.

Translations

It's quite possible that you can have your own translation system and prefer to use that instead of a default one. You can achieve that in a really simple way - just take a look at AbstractTranslation class:

abstract class AbstractTranslator
{
    protected $locale = 'en';

    public function setLocale($locale)
    {
        $this->locale = $locale;
    }

    /** Returns a translated item. */
    abstract public function get($item, array $replace = array());

    /** Returns a translated item with a proper form for pluralization. */
    abstract public function choice($item, $number, array $replace = array());
}

As you can see you need to implement two methods, and then you can inject your own translator:

use Joomla\DateTime\DateTime;
DateTime::setTranslator(new MyTranslator());

Strategy

Let's say that you don't like that startOfDay() method returns a midnight time - maybe you want it to be 9 o'clock. These kinds of modifications of behavior can be done by creating your own Strategy object which has to implement a StrategyInterface:

interface StrategyInterface
{
    public function startOfDay(\DateTime $datetime);

    public function endOfDay(\DateTime $datetime);

    public function startOfWeek(\DateTime $datetime);

    public function startOfMonth(\DateTime $datetime);

    public function endOfMonth(\DateTime $datetime);

    public function startOfYear(\DateTime $datetime);

    public function endOfYear(\DateTime $datetime);
}

In the package is a class called DateTimeStrategy which implements that interface and returns the following values:

startOfDay()    // The current day at 00:00:00
endOfDay()      // The current day at 23:59:59
startOfWeek() // Monday of the current week at 00:00:00 endOfWeek() // Sunday of the current week at 23:59:59
startOfMonth() // First day of the current month at 00:00:00 endOfMonth() // Last day of the current month at 23:59:59
startOfYear() // First day of the current year at 00:00:00 endOfYear() // Last day of the current year at 23:59:59

Let's say that we want to change the return values of startOfDay() and endOfDay() to 9:00 and 17:00. In fact we want to only override two default behaviours so it's better to create class by extending DateTimeStrategy instead of by implementing StrategyInterface.

use Joomla\DateTime\Strategy\DateTimeStrategy;
class MyStrategy extends DateTimeStrategy
{
    public function startOfDay(\DateTime $datetime)
    {
        $datetime->setTime(9, 0, 0);
    }
public function endOfDay(\DateTime $datetime) { $datetime->setTime(17, 0, 0); } }

Notice that in all methods of a StrategyInterface we get a PHP DateTime object and we make use of its mutability. We're making all of our changes to that PHP DateTime object, and we even don't need to return it.

In fact, changing a StrategyInterface is an important process. This is why there isn't a public static setStrategy() method, because it would make for more trouble than gain. For example, it would be impossible to have two different strategies in our project. Because when we make a StrategyInterface as a class property, then we can have only one of them.

Changing a StrategyInterface back and forth is also not a good idea. We also can't set a StrategyInterface for our objects because when we forget that we set one, we'll get a lot of unexpected results. So to remind us if we set some or not, we need an object to tell us about it.

How to do that? Create a custom DateTime and set a StrategyInterface in a constructor:

use Joomla\DateTime\DateTime;
class MyDateTime extends DateTime
{
    public function __construct($datetime, \DateTimeZone $timezone = null)
    {
        parent::__construct($datetime, $timezone);
        $this->setStrategy(new MyStrategy());
    }
}

And now everywhere when we would have a MyDateTimeobject we'll be certain that this object has changed the behavior of some methods.

Read 8893 times
Tagged under Google Summer of Code, English