brick / date-time

Date and time library for PHP
MIT License
326 stars 30 forks source link

Php format parser #4

Closed solodkiy closed 6 years ago

solodkiy commented 6 years ago

Current PatternParser is flexible, but quite excess for tasks like read date from special format (like 'd.m.y') In php we have special expression language for date formats. (http://php.net/manual/ru/function.date.php) I am ready to implement parser from this format if you don't have a reason to reject it.

$date = LocalDate::parse($line[3], new PhpFormatParser('d.m.y'));

BenMorel commented 6 years ago

Hi, I see no reason why I would reject this, expect that I wanted to stay as far as possible from PHP native date-time functions in this library :)

So yes we could provide such a parser, but I wouldn't try to support every single format character, nor would I try to match exactly PHP's format. PhpFormatParser would therefore not be an appropriate name.

For example, PHP uses Y for 4 digits year, y for 2 digits year.

In a parser, I would only allow d, (2 digits) m (2 digits) and y (4+ digits), which is I think what your example suggests?

(not sure if we should allow 1 digit for day or month, or less than 4 digits for the year, by the way).

BenMorel commented 6 years ago

Also, I think it would be nice to have a locale-based parser, based on intl, just like the formatter I mentioned in #3.

So you could do something like:

LocalDate::parse('11/03/2018', 'fr_FR');

If such a parser was implemented, would you really need a custom format parser?

solodkiy commented 6 years ago

I wanted to stay as far as possible from PHP native date-time functions in this library

Why? We can easily implement this parser using standard date-time functional.

In a parser, I would only allow d, (2 digits) m (2 digits) and y (4+ digits), which is I think what your example suggests? (not sure if we should allow 1 digit for day or month, or less than 4 digits for the year, by the way).

Unfortunately I can't control format dates what come from real world, but I still need to parse it. So, when I say about 'd.m.y' I mean exactly 2 digits year dates like 09.03.18. I don't say about data from humans, but from external api, csv files etc... So, I really need a custom format parser.

Now I use this code to parse it


class DmyParser implements DateTimeParser
{

    /**
     * @param string $text The text to parse.
     *
     * @return DateTimeParseResult The parse result.
     *
     * @throws DateTimeParseException If the given text could not be parsed.
     */
    public function parse(string $text): DateTimeParseResult
    {
        $format = 'd.m.y';
        $dateTime = \DateTimeImmutable::createFromFormat($format, $text, new \DateTimeZone('UTC'));
        if (!$dateTime) {
            throw new \InvalidArgumentException('Cannot parse date '. $text . ' by format '.$format);
        }

        $result = new DateTimeParseResult();
        $result->addField(Year::NAME, $dateTime->format('Y'));
        $result->addField(MonthOfYear::NAME, $dateTime->format('n'));
        $result->addField(DayOfMonth::NAME, $dateTime->format('j'));

        return $result;
    }
}```
And for me it is too much code for this simple and frequent operation.
BenMorel commented 6 years ago

Why?

Because I find PHP date-time functions too messy.

So, I really need a custom format parser.

Understood. I would still prefer to provide a fixed list of format characters, though. It doesn't make sense to accept 'h' for a LocalDate, as your code above would allow. It only adds to confusion.

For d and m we could accept [1-2] digits for maximum compatibility, and for y 1+ digits.

However from your example above, I understand that you need to parse a 2-digits year (that translates to a 4-digits year). I'm not sure whether this should be allowed, as we cannot know whether you're talking about 19xx or 20xx?

Here PHP has a specific behaviour, which is to consider 00-69 as 2000-2069, and 70-99 as 1970-1999, which I'm not really fond of.

solodkiy commented 6 years ago

Here PHP has a specific behavior, which is to consider 00-69 as 2000-2069, and 70-99 as 1970-1999, which I'm not really fond of.

But it still the most convenient way to parse this format. Parser based on php format is very flexible it can parse also dates like 12 January 2016, time like 1:00 pm.

Also I can resolve my problem if I could create LocalDate from DateTime object (it also actual this when you interact with other libs) to write code like this: $local = LocalDate::createOfDate(DateTime::createFromFormat('y.m.d'))

BenMorel commented 6 years ago

A method to import from a native DateTime object would be acceptable, I guess. At least the responsibility for odd behaviours stays in your code, when you explicitly introduce a DateTime.

BenMorel commented 6 years ago

Just released version 0.1.4 with support for:

This should allow you to do exactly what you want:

LocalDate::fromDateTime(\DateTime::createFromFormat('j-M-Y', '15-Feb-2009'));

Closing this issue, feel free to comment if you still have issues.