3

There is what I would call a bug in date_parse when there is no day. $d = date_parse("Feb 2010") will give $d["day"] == 1.

See the comment on this on the date_parse manual page.

Any nice workaround for this problem? :-)

UPDATE The date comes from published research reports. Unfortunately this means that they could look in different ways. I want to convert them to more standard ISO format when displaying the references. To help the readers I want always to include just the given fields (years, month, date). So this should be valid (and just give me the year):

2010

This should be valid, but just give me 2010-02 so to say:

Feb 2010

UPDATE 2 So far I have seen two bugs here in date_parse. It can't parse 2010. And it gives a day though there is no day in Feb 2010.

I can of course write a fix for this, but surely someone has already done this, or???

Community
  • 1
  • 1
Leo
  • 4,136
  • 6
  • 48
  • 72
  • 1
    PHP always tries to return a valid date, I imagine that is the reason it returns 1 for that index rather than false. IMO false really wouldn't make any sense and the only sensible value to return is 1. – vascowhite Nov 21 '14 at 00:52
  • 1
    Just the year or year plus month are still valid dates. 2010 or 2010-02 are valid ISO dates, but I see now date_parse says 2010 is invalid. Just another bug. :-( – Leo Nov 21 '14 at 00:55
  • 1
    The workaround is to write your own date parser that does what you want. – Barmar Nov 21 '14 at 00:58
  • Shouldn't be too difficult if the input is in a standard format. – vascowhite Nov 21 '14 at 00:59
  • 1
    Oh, give me time. I have no idea about how the input time looks. So I rather use the work that all other people have put into this. (I just wished they avoided the bugs in date_parse. Or fixed them!) – Leo Nov 21 '14 at 00:59
  • @Leo I don't believe it is a bug. It just doesn't work the way you want it to. – vascowhite Nov 21 '14 at 01:00
  • 1
    @vascowhite, the manual does not say exactly what the return value is. So strictly it is not a bug in the return value, but a bug in the manual. ;-) – Leo Nov 21 '14 at 01:02
  • @Leo If you expand your question with the actual problem you want to solve and what you've already tried, you'll likely get a working answer very quickly. – vascowhite Nov 21 '14 at 01:02
  • does $d["day"] === 1? – les Nov 21 '14 at 01:02
  • 2
    @Leo Yes you're right, the manual could be clearer. – vascowhite Nov 21 '14 at 01:03
  • Yes, that is one of the problems, @les. – Leo Nov 21 '14 at 01:07
  • I updated the question as you suggested, @vascowhite. – Leo Nov 21 '14 at 01:07

3 Answers3

2

The above bugfix routine is great, Leo, thanks. Unfortunately it still trips over January, thinking that 2014-01 is the same as 2014-01-01 --- we're eleven-twelfths of the way there.

The date formats that PHP can parse, that don't contain a day-of-month, appear to be (in php_src:date/lib/parse_date.re):

gnudateshorter   = year4 "-" month;
datenoday        = monthtext ([ .\t-])* year4;
datenodayrev     = year4 ([ .\t-])* monthtext;

Very few, conveniently. We can run the same regexes on $dateRaw, essentially reverse-engineering what the parser had decided.

(Side observations: the above excludes formats like 5/2016, which is parsed as "20 May with some extra characters at the end"; they are also similar to day-of-year and week-of-year formats, so we'll try not to trip over those.)

function date_parse_bugfix($dateRaw) {
  $dateRaw = trim($dateRaw);
  // Check for just-the-year:
  if (strlen($dateRaw) === 4 && preg_match("/\d{4}/", $dateRaw) === 1) {
    $da = date_parse($dateRaw . "-01-01");
    $da["month"] = false;
    $da["day"] = false;
  }
  else {
    $da = date_parse($dateRaw);
    if ($da) {
      // If we have a suspicious "day 1", check for the three formats above:
      if ($da["day"] === 1) {
        // Hat tip to http://regex101.com
        // We're not actually matching to monthtext (which is looooong), 
        // just looking for alphabetic characters
        if ((preg_match("/^\d{4}\-(0?[0-9]|1[0-2])$/", $dateRaw) === 1) ||
            (preg_match("/^[a-zA-Z]+[ .\t-]*\d{4}$/", $dateRaw) === 1) ||
            (preg_match("/^\d{4}[ .\t-]*[a-zA-Z]+$/", $dateRaw) === 1)) {
              $da["day"] = false;
        }
      }
    }
  }
  return $da;
}
1

No answers so I answer my own question. Here is a workaround the problems I saw.

// Work around for some bugs in date_parse (tested in PHP 5.5.19)
//   http://php.net/manual/en/function.date-parse.php
//
// Date formats that are cannot be parsed correctly withoug this fix:
//   1) "2014" - Valid ISO 8061 date format but not recognized by date_parse.
//   2) "Feb 2010" - Parsed but gives ["day"] => 1.
function date_parse_5_5_bugfix($dateRaw) {
  // Check "2014" bug:
  $dateRaw = rtrim($dateRaw);
  $dateRaw = ltrim($dateRaw);
  if (strlen($dateRaw) === 4 && preg_match("/\d{4}/", $dateRaw) === 1) {
    $da = date_parse($dateRaw . "-01-01");
    $da["month"] = false;
    $da["day"] = false;
  } else {
    $da = date_parse($dateRaw);
    if ($da) {
      if (array_key_exists("year", $da)
          && array_key_exists("month", $da)
          && array_key_exists("day", $da))
        {
          if ($da["day"] === 1) {
            // Check "Feb 2010" bug:
            // http://www.phpliveregex.com/
            if (preg_match("/\b0?1(?:\b|T)/", $dateRaw) !== 1) {
              $da["day"] = false;
            }
          }
        }
    }
  }
  return $da;
}

Some tests (visual ;-) )

$a = date_parse_5_5_bugfix("2014"); print_r($a);
$b = date_parse_5_5_bugfix("feb 2010"); print_r($b);
$c = date_parse_5_5_bugfix("2014-01-01"); print_r($c);
$d = date_parse_5_5_bugfix("2014-11-01T06:43:08Z"); print_r($d);
$e = date_parse_5_5_bugfix("2014-11-01x06:43:08Z"); print_r($e);
Leo
  • 4,136
  • 6
  • 48
  • 72
0

Can you try:

$dateTime = strtotime('February, 2010');
echo date('Y-m', $dateTime);
happyvirus
  • 279
  • 3
  • 21
  • 1
    I think he wants to know which fields the user supplied. So he wants the day to be `null` if the user didn't give a day. – Barmar Nov 21 '14 at 00:56
  • 2
    I honestly do not understand the downvote here. If you do not like my answer, IGNORE and find something of your own. I'm not off-topic here – happyvirus Nov 21 '14 at 01:05
  • "If you do not like my answer"; But that is exactly what down votes are for. – vascowhite Nov 21 '14 at 01:09
  • @vascowhite Pleae read the tooltip on the downvote icon. My answer IS useful and relates to the topic. – happyvirus Nov 21 '14 at 01:12
  • @Leo, that isn't true. People can and do down or up vote for any reason they see fit. If you hover over the down vote arrow you will see a tool tip that says 'this answer is not useful', it does not say 'this answer is wrong'. 'Useful' of course is from the view point of the voter and is subjective. – vascowhite Nov 21 '14 at 01:13
  • @vascowhite "People can and do down or up vote for any reason they see fit" Please read this http://meta.stackoverflow.com/questions/251758/why-is-stack-overflow-so-negative-of-late – happyvirus Nov 21 '14 at 01:15
  • 1
    You are both jumping to the conclusion that it was my down vote. I was merely trying to explain how the site works. Agree or disagree, that is the way things work. It is one vote, just ignore it, that's what I do. Believe me, I've had a few :) However, you are right about the nap @Sajidkhan, it is 3 am here and I can't sleep :( – vascowhite Nov 21 '14 at 01:18