PHP : Better date parser than strtotime - php

I'm trying to parse a string in a specific format and I'm really surprised to discover that I can't find a good function to do that.
The only one I found is strtotime and it doesn't fit as it guesses the date format. I really don't trust the "guess" part.
Moreover my string input is in a french format (dd/mm/aaaa) which it seems that it's not well understood (it parses american formats like mm/dd/aaaa).
What I'm looking for is a function that take in input a date string and a format to parse.
I could do it myself with a regexp but I can't believe that it doesn't already exist.
I found :
DateTime::createFromFormat(). But it only work with PHP 5.3 and I don't have the power to upgrade the PHP version (5.2)
strptime(). This method does what I want but is not implemented on windows platform (by the way: WTF ??)
Any suggestion ?

Unfortunately, it seems that such parsing is better done manually, by exploding the string at slashes and then switching day and month.

Check out Zend_Date, which lets you specify the format when you set a date. As well as including constants for many common formats, you can specify your own too.
$date = new Zend_Date();
$date->set('27/08/2009','DD/MM/YYYY');

The following comment from php.net on strtotime may help:
Fails for non-US dates where the
ordering is uncertain, such as
01/02/2003 - parses this as Feb 1st,
rather than Jan 2nd.
If you are parsing dates for a non-US
locale, you can flip these elements of
your date:
<?php
$y = $_POST['date'];
if (preg_match('/^\s*(\d\d?)[^\w](\d\d?)[^\w](\d{1,4}\s*$)/', $y, $match)) {
$y = $match[2] . '/' . $match[1] . '/' . $match[3];
}
echo date('d # m # Y', strtotime($y));
?>
WARNING: Above only works for dates,
and breaks for times: 12:30:01 will be
converted to 30/12/01.

I've written a class myself, I think you'll find an ok version in gadmdatecommand.php in http://sourceforge.net/projects/phpdbedittk
Regarding the comments here to just explode by '/' and swap the number, its not quite that simple. If you offer to enter dates into an input box, you may get - depending on the locality of the user and the application
1/7/2010
1.7.2010
1-7-2010
15 Jul
1 Jul 2010
1/6/8
and many more variations. I've solved this problem (at least for me successfully) by creating dateformats, each of which have
a) a regex that matches the format
b) an array mapper that matches regex brackets into date pieces (day, month, minute, am/pm)
c) an output format for date()
HTH

If you know your date format input will be English-formatted, then you can process it into a more standard date format. A simple parsing of 24/7/2007 to 2007-07-24 is trivial. Explode with the forward slash and put the parts in the right spot. I know for a fact that strtotime will parse 2007-07-24 correctly.

strptime():
Internally, this function calls the strptime() function provided by the system's C library. This function can exhibit (!) noticeably different behaviour across different operating systems. The use of date_parse_from_format(), which does not suffer from these issues, is recommended on PHP 5.3.0 and later.
http://www.php.net/manual/en/function.strptime.php

Related

PHP DateTime::createFromFormat DATE_ATOM ISO 8601

I am using PHP 7.2, and trying to create a date from a string as follows:
$dateString = '2018-12-31T01:01:01+00:00';
$converted = DateTime::createFromFormat(DATE_ATOM), $dateString);
The snippet above works fine and returns the expected result.
The problem happens when I swap the day and month in the date string provided above, as follows:
$dateString = '2018-31-12T01:01:01+00:00';
$converted = DateTime::createFromFormat(DATE_ATOM), $dateString);
I was expecting this second example to return false, but instead I get an actual date time, 2020-07-12 01:01:01.000000.
So, I have no way of telling whether the second date was a proper date or not, because the system accepted it, and I will be saving in my database something which I shouldn't.
Could this be considered a bug in PHP 7.2?
Could this be considered a bug in PHP 7.2?
No, this is how the method is intentionally implemented. You can argue if it's the right way to implement it, but that's how it is. Notably even if we all agree that it's not right here, it will still keep working as it's implemented.
The practical answer here is to write a validator (for entirety of format and all parts) and run any untrusted input through it.

How to separate the timezone and datetime from a string?

I have a string get from a text file name.
$string = "20181011000000GMT+0800"
// trying to convert it
Carbon::createFromFormat('YmdHisTO', $string)->format...
It shows me this error.
Unexpected data found. The timezone could not be found in the database
Data missing
How can i separate the datetime and timezone, if there's GMT+0800 end of the datetime string?
You can parse that format with a slight change to the format string you're using:
$string = "20181011000000GMT+0800";
Carbon::createFromFormat('YmdHisT+', $string);
The + sign tells the parser to ignore everything after the first timezone identifier (GMT). Strictly, it'll work without the + too, but I prefer to use it for clarity. Up to you though.
See https://3v4l.org/kDorn
Actually, testing this a bit more, I'm not sure the plus sign is that relevant. I think either of the timezone format characters (T and O) are simply swallowing all of the timezone information, rather than just their specific parts. DateTime's parser can be a bit of a minefield sometimes.
I would recommend using strtotime() it accepts every valid datetime format. See usage: http://nl1.php.net/manual/en/function.strtotime.php
$DateTimeString = "20181011000000GMT+0800";
$timestamp = strtotime($DateTimeString);
// trying to convert it
Carbon::createFromTimestamp($timestamp)->toDateTimeString();

strtotime with year in 2 digit format

Hallo can someone explain the behaviour of strtotime function with year in non-standard format.
echo date("d-m-Y",strtotime('02-12-10')) .'<br>'; //10-12-2002
echo date("d-m-Y",strtotime('09.09.10')) .'<br>'; //02-09-2010 --How this is interpreted?
echo date("d-m-Y",strtotime('02-12-2010')) .'<br>'; //02-02-2010
echo date("d-m-Y",strtotime('09.09.2010')) .'<br>'; //09-09-2010
I wanted to convert strings of format dd.mm.yy(09.09.10) to datetime format.
strtotime() can be a bit flaky in such cases. It is built to recognize standard american date formats.
If you can use PHP > 5.3, consider using DateCreateFromFormat which has the big advantage of accepting a pre-defined format string to parse the incoming data.
On pre-5.3 platforms, strptime() seems to offer a second-best alternative. It's not available on Windows and has some minor issues - be sure to read the manual page before using.
From the manual:
The "Day, month and two digit year, with dots or tabs" format (dd [.\t] mm "." yy) only works for the year values 61 (inclusive) to 99 (inclusive) - outside those years the time format "HH [.:] MM [.:] SS" has precedence.
You are using '09.09.10' and 10 does not fall in the valid range hence change the separator to -
This is not 2-digit year question.
this is non-standard format question.
And asking impossible things from mere a program.
Even me, not being a computer, have no idea what these 09's dooes mean in your date. is it day.month.year? or year.month.day or whatever?
Assuming it's day.month.year:
$list($d,$m,$y) = explode("09.09.10");
echo "$d-$m-20$y";

Correct Regular expressions to match a date

What is the correct regular expression to use to validate a date like. 2009-10-22 or 2009-01-01 etc. Platform PHP
This (from regexplib.com) will match what you want, and perform checks for leap years, days-per-month etc. It's a little more tolerant of separators than you want, but that can be easily fixed. As you can see, it's rather hideous.
Alternatively (and preferably in my opinion) you may want to simply check for figures in the correct places, and then perform leap year and days-per-month checks in code. Sometimes one regexp isn't so understandable and there's greater clarity in performing the checks in code explicitly (since you can report precisely what's wrong - "only 30 days in November", rather than a "doesn't match pattern" message, which is next to useless)
If you want something simple that does a little more than just validates format, but doesn't go as far as validating how many days is in the month that is entered, or leap years, you can use this:
^(19|20)[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$
This example allows years 19xx and 20xx
As you have to deal with accepting 2009-02-28 but not 2009-02-29 but accept 2008-02-28 you need more logic that 1 think a regex can give. (But if someone can show it I would be impressed)
I would try to convert it to a date and report if the conversion failed or if you you language has a check date function use that.
\d{4}-\d{2}-\d{2} would match string in that form, but to check if date is valid, you'd had to break that string to year, month and date (you can use this regexp for that) parts and check each of them.
You can additionally, make sure that year must start with 1 or 2: [12]\d{3}-\d{2}-\d{2}, and you can also do the same for month and day: [12]\d{3}-[01]\d-[0123]\d (but I would go with the first regexp and compare parts "manually")
found this on the web tested it with a few dates and looks stable, for dates between 1900 and 2000:
(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])
OK, a regex that will validate month and day ranges could be
[0-9]{4}-(?:1[0-2]|[1-9])-(?:3[01]|[12][0-9]|[1-9])
If you want to restrict the years, say, from 1900 to 2050, you could end up with
(?:2050|20[0-4][0-9]|19[0-9]{2})-(?:1[0-2]|[1-9])-(?:3[01]|[12][0-9]|[1-9])
They will not catch "subtly wrong" dates like February 31st, so it's really quite clear that a sanity check needs to be performed outside of the regex.
In .NET Regex:
\d{4}\-\d{2}\-\d{2}
[0-9]{4}-[0-9]{2}-[0-9]{2}
or
\d\d\d\d-\d\d-\d\d
or
...
simply read first regex tutorial
^\d{4}-\d{2}-\d{2}$
but no regular expression can prevent someone to enter "9867-39-56"
For a complete validation (which would include verifying that the day, month and year parts are valid) a Regex is not the tool of choice. Apart from month issues you'd get into trouble with leap years...
So, if you just want to check if the rough format is correct, or isolate the different parts (year-month-day), a regex is fine.
([0-9]{1,4})-(1[012]|0?[1-9])-([12][0-9]|3[01]|0?[1-9])
This is already pretty exact and captures the year (0..9999), month and day into capture groups, ready for parsing...
If you can rely on more than a regular expression, an hybrid solution by using Posix functions date() and time() delivered with PHP could look like this:
<?php
date_default_timezone_set("GMT");
function validateDate($date)
{
if (preg_match("^[0-9]{4}-[0-9]{2}-[0-9]{2}^", $date))
{
return date('Y-m-d', strtotime($date)) === $date;
}
return false;
}
// Some tests
$dates = array(
'2009-09-09', '2009-09-32', '2009-31-00', '2035-01-02',
);
foreach($dates AS $date)
{
echo $date .': '. (validateDate($date) ? 'OK' : 'FAILS') ."\n";
}
?>
It's not elegant plus you'll be limited by Unix Epoch time (from January 1 1970 00:00:00 GMT to January 19 2038 03:14:07 GMT), but it's reliable and it's well supported in PHP.

what is the best method to translate strings to mysql dates?

What is the best way in php to take the following string mm[some char]dd[some char]yyyy and translate it to yyyymmdd?
I will probably want in the future, according to local do the same with dd[some char]mm[some char]yyyy.
If there is a way that already uses the Zend Framework API, the better
<?php
$str = '08-24-1989'; // can be in any recognizable date format.
$new_str = date('Ymd', strtotime($str)); // produces "20090824".
?>
You can replace Ymd in the second statement above with any date format characters found here.
If you're looking to use Zend's Zend_Date framework, check out some examples and documentation here. Quite frankly though, the PHP functions are a lot simpler and easier to use in your case.
date('Ymd', strtotime($time));
Strtotime is absolutely the best tool to translate almost any time format into a standard one that you can then use Date to put into the format you want.
Because you question title says MySQL Dates, this is the string format that mysql uses.
date('Y-m-d h:i:s', strtotime($time));
Unless [some char] varies , use the mysql str_to_date function, e.g. STR_TO_DATE('12|23|2009','%m|%d|%Y');
I would absolutely use TIMESTAMP for any date storage. It's incredibly easy to handle time differences (like SELECT ... WHERE date BETWEEN 2138728753 AND 376251237) and can be translated to any locale pretty easily :)

Categories