Comparing dates off-by-one

Hi

My goal is to display something within a date-range. For example, this is my debug-file:

titel: Testblock ohne echten Inhalt
text: ‘Lorem ipsum dolor sit amet’
datevon: 2020-05-31
datebis: 2020-05-31

I want this to be displayed between those days. In my example, it is supposed to be displayed only today.

This is my code:

<?php foreach (page('wichtige_informationen/aktuelles')->banner()->toStructure() as $subpage) : ?>
      Date:

      <?= date('Y-m-d') ?>

      Date von:

      <?= $subpage->dateVon()->toDate('Y-m-d') ?>

      Date bis:

      <?= $subpage->dateBis()->toDate('Y-m-d') ?>

      <?php 

        if ($subpage->dateBis()->toDate('Y-m-d') >= date('Y-m-d') and ($subpage->dateVon()->toDate('Y-m-d') <= date('Y-m-d') ) ):

        snippet('rotier-knopf', ['subpage' => $subpage]);

      endif ?>

    <?php endforeach ?>

Obviously, the first few lines are for debug-purposes. The only relevant line is the if-condition.

The funny thing is that my code is off-by-one. For example, when I set the dates from 2020-05-05 to 2020-05-10, the text will be shown from 2020-05-06 until 2020-05-09, so off-by-one.
The code itself works.

I could of couse just add a day (that is a workaround I don’t like), but I think I am missing something here… Does somebody in this forum see a logic-mistake?

Thanks in advance :slight_smile:

Try swapping and for && in the if statement, and you should probably wrap both sides in parenthesis

<?php 

  if ( ($subpage->dateBis()->toDate('Y-m-d') >= date('Y-m-d') ) && ( ($subpage->dateVon()->toDate('Y-m-d') <= date('Y-m-d') ) ):

  snippet('rotier-knopf', ['subpage' => $subpage]);

endif ?>

Are the dates stored correctly in the content file? And do the debugging lines show the correct information?

That should not be necessary and certainly not as many…

Oops… i squinted at that for a while, did i do one too many?

There’s actually one missing now because you added one too many… :wink:

:eyeglasses: I hate long lines.

Yep, especially without an editor.

@carsten_kgs What exactly do you want to achieve here?

And which exact. Kirby version are you using?

I’m not sure how string comparisons work in PHP, but it might be more reliable to compare timestamps (numbers).

One thing that may be happening is that ->toDate('Y-m-d') does two conversions:

  • first it needs to parse the field’s value (a string) into a timestamp (a number);
  • then it needs to format that number back into a string.

The string '2020-06-01' converts to a timestamp that represents the June 1st at midnight (the very start of the day). I wouldn’t be surprised if when converting back to a date, it would take the server’s timezone into account and convert the date to '2020-05-31'.

If you do the work yourself and with numbers, it’s a bit easier to control what’s happening (though dealing with dates is always difficult anyway because humans are weird).

Also it might be useful to move that date comparison code in its own function, maybe in a page model? If the template name for your sub-pages is ‘subpage’, it could look like:

<?php // site/modes/subpage.php

class SubpagePage extends Page {
  public function isVisibleToday(): bool {
    $from = $this->content()->dateVon()->toDate();
    $to = $this->content()->dateBis()->toDate();
    if (is_int($from) && is_int($to)) {
      $now = time();
      // Provided the 'to' date is written as 'YYY-MM-DD' with no time,
      // the time will be set to midnight (start of the day), but we want
      // the content to be visible up to midnight at the end of the day.
      return $now >= $from && $now < ($to + 24 * 60 * 60);
    }
    return false;
  }
}

Note that we have to add a full day (as a number of seconds) to the ‘to’ date, to make sure that time actually means “that day, but at the very end of the day”.

Another option is to declare your dates with a time as well, so that you can control more precisely when a subpage is visible or not.

So your content could look like

----
datevon: 2020-05-31 00:00
----
datebis: 2020-06-01 00:00 
----

or even:

----
datevon: 2020-05-31 06:00
----
datebis: 2020-05-31 22:00 
----

(If you end up using explicit times in your dates, you should change the ($to + 24 * 60 * 60) to just $to in the example above.)

See also: Cookbook - Working with dates.

Quick warning about timezones

I think Kirby parses dates with the strtotime PHP function. I’m not sure if the times (including the default “midnight”) will match a specific timezone.

If you want “2020-05-31 06:00” (or “2020-05-31” which is implicitly “2020-05-31 00:00”) to mean “6 o’clock in the timezone that I care about, e.g. the timezone for Germany”, you should check that the web server and your local PHP version use the same timezone:

var_dump(date_default_timezone_get());
// e.g. "Europe/Berlin"

otherwise, e.g. if it returns “UTC”, you can end up in situations where your content end up being visible the day before at 22:00 instead of midnight.

You can probably add a line like this in your config.php to set the timezone you want:

date_default_timezone_set('Europe/Berlin');

My idea you find at

https://forum.getkirby.com/t/date-field-saves-wrong-date/13124/21

Took a bit longer than planned…

The issue is really the missing time.

We are now using for example

datevon: 2020-05-31 00:00
datebis: 2020-05-31 00:00

Thanks a lot for the discussion here!