Date object treats 0 as a valid day, month or year. For example:

$date = new DateObject('00/00/2013', NULL, 'd/m/Y');
var_export($date->errors);
// Outputs an empty array.

I've set this issue to critical as this seems like quite a large hole in the date validation rules.

The problem comes from DateObject::arrayErrors(), specifically line 875 (http://drupalcode.org/project/date.git/blame/refs/heads/7.x-2.x:/date_ap...):

    foreach ($arr as $part => $value) {
      // Explicitly set the granularity to the values in the input array.
      if (is_numeric($value)) {
        $this->addGranularity($part);
      }
      // Avoid false errors when a numeric value is input as a string by casting
      // as an integer.
      $value = intval($value);
      if (!empty($value) && $this->forceValid($part, $value, 'now', $default_month, $default_year) != $value) {

The problem is empty(0) evaluates to true so we never check if that value is valid. That's fine for times but is obviously a problem for dates. It looks like this bug has been here from the beginning (http://drupalcode.org/project/date.git/commit/135af56e2723b56d972fdf888d...). Removing the empty() check fixes the issue with no side effects as far as I can see. I'll attach a patch in the comments. With the patch applied we get:

$date = new DateObject('00/00/2013', NULL, 'd/m/Y');
var_export($date->errors);
// Outputs:
// array (
//   'month' => 'The month is invalid.',
//   'day' => 'The day is invalid.',
// )

It's probably worth adding some unit tests around this as well.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Dean Reilly’s picture

Status: Active » Needs review
FileSize
703 bytes

Patch attached.

Dean Reilly’s picture

Status: Needs work » Needs review
FileSize
703 bytes

Ignore. Double post.

Status: Needs review » Needs work

The last submitted patch, zero-month-and-year-are-considered-valid-21013970-1.patch, failed testing.

Dean Reilly’s picture

Status: Needs review » Needs work

Hmm so it looks like the tests are failing because the date part is throwing an error so DateObject::parse never sets the correct time and we default to what the time is now in the default timezone (I guess the testbots are running in UTC).

I thought initially we could fix this up with something like:

     // Create time-only date.
    $input = '10:30:00';
    $timezone = NULL;
    $format = 'H:i:s';
    $date = new dateObject($input, $timezone, $format);
    $value = $date->format('H:i:s');
    $expected = '10:30:00';

i.e. removing the date parts but that doesn't work either. The problem seems to be dateobject doesn't distinguish between no date being provided and 0000-00-00 being provided. So this is going to need some more work.

Dean Reilly’s picture

So here's an attempt at differentiating between dates being left out when instantiating a DateObject and dates being set to zero.

Dean Reilly’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, zero-month-and-year-are-considered-valid-21013970-5.patch, failed testing.

Paul B’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
664 bytes

If I enter a date '0000-00-00' into a date textfield, it is recorded as today's date (even if 'No default value' is given, see
#1995864: Date field not obey 'No default value' (defaults to current date) when within Field Collection or Paragraph.

If I apply only the last hunk of the patch in #5, this problem seems to be fixed and it still passes the date API tests.

Paul B’s picture

Status: Needs review » Needs work

Forget my last comment, the tests pass but my problem was not fixed.

Paul B’s picture

I should add that I'm using a d-m-Y day format (so in my case it's "00-00-0000", not "0000-00-00").
I didn't think that would matter, but it does:

drush ev 'var_dump(new DateObject("0000-00-00"));'
object(DateObject)#9 (10) {
  ["granularity"]=>
  array(4) {
    [0]=>
    string(4) "year"
    [1]=>
    string(5) "month"
    [2]=>
    string(3) "day"
    [3]=>
    string(8) "timezone"
  }
  ["errors"]=>
  array(0) {
  }
  ["serializedTime":"DateObject":private]=>
  NULL
  ["serializedTimezone":"DateObject":private]=>
  NULL
  ["timeOnly"]=>
  bool(false)
  ["dateOnly"]=>
  bool(false)
  ["originalTime"]=>
  string(10) "0000-00-00"
  ["date"]=>
  string(20) "-0001-11-30 00:00:00"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Berlin"
}

This is wrong, it should be "0000-00-00 00:00:00".

drush ev 'var_dump(new DateObject("0000-00-00", "Europe/Berlin", "Y-m-d"));'
object(DateObject)#9 (10) {
  ["granularity"]=>
  array(7) {
    [0]=>
    string(4) "hour"
    [1]=>
    string(6) "minute"
    [2]=>
    string(6) "second"
    [3]=>
    string(5) "month"
    [4]=>
    string(3) "day"
    [5]=>
    string(4) "year"
    [6]=>
    string(8) "timezone"
  }
  ["errors"]=>
  array(0) {
  }
  ["serializedTime":"DateObject":private]=>
  NULL
  ["serializedTimezone":"DateObject":private]=>
  NULL
  ["timeOnly"]=>
  bool(true)
  ["dateOnly"]=>
  bool(false)
  ["originalTime"]=>
  string(10) "0000-00-00"
  ["date"]=>
  string(19) "2014-01-23 00:00:00"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Berlin"
}

This is also wrong.

Paul B’s picture

Status: Needs work » Needs review
FileSize
754 bytes

OK, maybe the -0001-11-30 value makes some sense since month 0 would be the month prior to month 1, etcetera. The date_api module says

    // If the input value is '0000-00-00', PHP's date class will later
    // incorrectly convert it to something like '-0001-11-30' if we do setDate()
    // here. If we don't do setDate() here, it will default to the current date
    // and we will lose any way to tell that there was no date in the orignal
    // input values. So set a flag we can use later to tell that this date
    // object was created using only time values, and that the date values are
    // artifical.

But on https://bugs.php.net/bug.php?id=42971 it says that this is not a bug.

Anyway, I think

new DateObject("0000-00-00")

and

new DateObject("0000-00-00", "Europe/Berlin", "Y-m-d")

should return the same value. The patch should fix it I believe.

Dean Reilly’s picture

Yep, I've been thinking about this too and maybe it is valid to have 0 as a day, month and year value.

However, let me explain where I ran into this use case. We had a date field on a node using a text field widget. Users would sometimes enter something like 00/00/2014 into the field and the node would submit without any validation issues because the date api module treats this as a valid date but in every case it's not the date the user intended!

So if we are going to support this in the date format module we should probably have a think if we want this functionality to be user facing too.

vijaycs85’s picture

Issue tags: +sprint

Lets focus on this issue and get it in ASAP

vijaycs85’s picture

The last submitted patch, 14: 2101397-0-date-14-testonly.patch, failed testing.

vijaycs85’s picture

Assigned: Unassigned » podarok
Status: Needs review » Reviewed & tested by the community

Ok, lets get this in. Keeping it RTBC and assigning to @podarok for final review and commit.

Dean Reilly’s picture

The latest patch is not going to work as we now don't correctly recognise time-only inputs (see the attached patch with more tests).

It seems to me we have at least three problems in this thread:

1) Should an input like 0000-00-00T16:17:38 be treated as a time-only input or as a date.

2) Should 0000-00-00 be considered valid input for a date.

3) If 0000-00-00 is a valid input, should we allow end users to enter these kind of dates through date widgets?

And we need to make a decision about how we want to fix these before we can code a good solution.

Personally, I'm of the opinion that we should drop support for zero-dates. They don't gain us very much as we already have good support for date arithmetic elsewhere which is much more flexible and explicit in its intent. Also including and trying to support it could lead to some very complex code. Finally I'd argue that because we already have validation in place that tries to prevent zero-dates (it just doesn't work at the minute because of the empty() bug) it looks like this was already the decision made previously.

This then settles the other two questions: the first would be a time input and the widgets would not allow users to do things like 2014-00-25.

Dean Reilly’s picture

Assigned: podarok » Unassigned
Status: Reviewed & tested by the community » Needs work

The last submitted patch, 17: 2101397-0-date-16.patch, failed testing.

steinmb’s picture

Issue tags: -sprint +Needs reroll
manishsaharan’s picture

Assigned: Unassigned » manishsaharan
scottm316’s picture

Assigned: manishsaharan » Unassigned
Status: Needs work » Needs review
FileSize
2.29 KB

Re-rolled!

Status: Needs review » Needs work

The last submitted patch, 22: 2101397-0-date-22.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

manishsaharan’s picture

Assigned: Unassigned » manishsaharan
Status: Needs work » Needs review
FileSize
744 bytes

This Patch will solve the issue

Status: Needs review » Needs work

The last submitted patch, 24: date_2101397_24.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

scottm316’s picture

Assigned: manishsaharan » Unassigned
Status: Needs work » Needs review
FileSize
4.36 KB

As has been explored in many other issues, date-only and time-only values are not a reality when saving things in the database. There does exist dateOnly and timeOnly flags in the code, but they don't do much.

This patch needed a little more than a re-roll to work properly but I think I've got it as far as preventing zero month and zero day entries. Any changes or suggestions or questions would be welcomed. This is basically one of the most used modules in D7 so I understand the implications of what's been changed here.

DamienMcKenna’s picture

Issue tags: -Needs reroll

Thank you scottm316 for the patch.

Would someone mind testing the patch in #26 to see if it resolves your problems? Thanks.

DamienMcKenna’s picture

Status: Needs review » Needs work

The test files have been renamed, so this needs a reroll.

guilhermevp’s picture

I tried to re-roll the patch acommodating the changes in the module, but the problem remains.

DamienMcKenna’s picture

If you could please upload the patch it'd be a step forwards. Thank you.

guilhermevp’s picture

Submitting patch. I hope it may help!

DamienMcKenna’s picture

Thank you, I appreciate it!

I think there might be a typo here:

+          if (empty($value)) {
+            $this->erros['month'] = t('The month is invalid.');
+          }
guilhermevp’s picture

Sorry! indeed I notice this mistake and corrected it, but forgot to update the patch!

solideogloria’s picture

Status: Needs work » Needs review
FileSize
4.41 KB
433 bytes

I fixed the typo.

DamienMcKenna’s picture

Status: Needs review » Needs work

The last submitted patch, 35: date-n2101397-34-test-only.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

DamienMcKenna’s picture

Status: Needs work » Needs review
Parent issue: » #3201850: Plan for Date 7.x-2.12-rc1

Tests-only patch fails, full patch passes, this is great!

steinmb’s picture

Status: Needs review » Reviewed & tested by the community

I see no problems with the patch, makes the test pass. RTBC from me.

Checking patch date_api/date_api.module...
Checking patch date_api/tests/DateApiTestCase.test...
Hunk #1 succeeded at 361 (offset 15 lines).
Hunk #2 succeeded at 374 (offset 15 lines).
Hunk #3 succeeded at 419 (offset 15 lines).
Applied patch date_api/date_api.module cleanly.
Applied patch date_api/tests/DateApiTestCase.test cleanly.

DamienMcKenna’s picture

Status: Reviewed & tested by the community » Fixed

Committed. Thank you all!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.