Determine \DateTime has inferred a day in constructor - php

I am trying to make user-input accept a date to limit searching. I use the following:
try {
$begin = new \DateTime($range['begin']);
} catch (\Exception $e) {
$begin = null;
}
If the date fails to construct, then the query (not shown) is not modified.
Occasionally the users enter just a month, and it would be obvious then that the user wants to search between the first and last day of the month, and I might modify the query in a different way.
Unfortunately php infers, without other specification that the user means the first of the month. Is there any way to determine if this has happened? Another way to ask this question is how does one determine that a user has supplied a valid month without a day and distinguish that from supplying a date with first day of the month that leverages the myriad formats DateTime automatically interprets?
My first pass used date_parse, but that also infers the date.

better always to know format of input value, so then you can simple convert to datetime object, for example:
$inputFieldValue = '22/02/2017';
$date = \DateTime::createFromFormat('d/m/Y', $inputFieldValue);
if ($date instanceof \DateTime) {
echo 'input is valid date, do what you need';
} else {
echo 'input is not valid date';
}
more documentation http://php.net/manual/en/datetime.createfromformat.php

Related

Guessing date format with PHP

I have a user input in web-form in HTML5 type="date" field. Then this entered value (together with others) is saved as JSON document in MySQL database. Now I need to extract that date and print it into generated MS Word document with one given format. Great! But as you know HTML5 date field accepts date format depending on user locales, settings, etc. I noticed that all dates entered in different browsers are saved in DB in "Y-m-d" format. Is this really so in all the cases? Can I rely on this format for future usage? Could not find any specification information on the topic.
If one can not rely on "Y-m-d" format are there any PHP libraries for "guessing" date format?
First of all, why are you saving JSON document in MySQL database instead of some more suitable document database (e.g. MongoDB)?
Anyway, based on this and this the displayed date format differs from the actual value. The date value in the HTTP Request will be always formatted as yyyy-mm-dd, which is the ISO 8601 format and I guess it should be reliable.
In PHP you can process date strings with the DateTime class. This class supports many different date formats. For example:
// timezone is set based on your system preferences if not set
$date1 = new DateTime('2017-04-27', new DateTimeZone('+00:00'));
$date2 = new DateTime('27 April 2017', new DateTimeZone('+00:00'));
$date3 = new DateTime('2017-04-27T00:00:00+00:00');
var_dump($date1 == $date2); // bool(true)
var_dump($date1 == $date3); // bool(true)
If the date string is wrong, an exception is thrown:
try {
$date = new DateTime('2017 4 27');
} catch (Exception $e) {
echo $e->getMessage(); // DateTime::__construct(): Failed to parse time string (2017 4 27) at position 5 (4): Unexpected character
}
So, in your case you can you this class for date validation and than proper output formatting, e.g.:
$dateString = '2017-04-27T14:22:11+00:00';
try {
$date = new DateTime($dateString);
echo $date->format('Y-m-d'); // 2017-04-27
} catch (Exception $e) {
// do something else
}

PHP: DateTime::__construct() error

I keep getting the following error :
DateTime::__construct(): Failed to parse time string (46-61-9481) at position 0 (4): Unexpected character
which relates to this piece of script within an API
$dob = new DateTime(str_replace('/','-',Input::post('date_of_birth')));
$customer->date_of_birth = $dob->getTimestamp();
I have quite limited knowledge when it comes to code but, I'm wondering if anyone would be kind enough to let me know what to change to avoid these errors.
The problem here is the use of DateTime(). PHP DateTime() expects a valid date and otherwise it will throw an exception. If you still want to accept invalid date input, better to put this in a try catch block. If the date is a valid one, it will work well and otherwise it will come to catch block. In the catch block, you can set a default date such as 00/00/0000 and save to DB.
try {
$date = new DateTime('01-01-2016');
$date = $date->format('m/d/Y');
} catch (\Exception $e) {
$date = '00/00/0000';
}
echo $date;
46 is not a valid month, nor a valid day of month. A valid month or day of month never starts with a 4.
The date 46-61-9481 is invalid, hence the error.
The solution is to validate the input date prior to passing it to DateTime constructor.

PHP Carbon Check If Chosen Date is Greater than Other Date

I've started using PHP Carbon for my application since it seems so much easier than using and manipulating date/time with the DateTime class. What I want to do is check if the chosen date ($chosen_date) is greater than another date ($whitelist_date). I have tried this in the code below:
$chosen_date = new Carbon($chosen_date);
$whitelist_date = Carbon::now('Europe/London');
$whitelist_date->addMinutes(10);
echo "Chosen date must be after this date: ".$whitelist_date ."</br>";
echo "Chosen Date: ".$chosen_date ."</br>";
if ($chosen_date->gt($whitelist_date)) {
echo "proceed";
} else {
echo "dont proceed";
}
The original $chosen_date value comes from POST data. Here is the output I get:
Chosen date must be after this date: 2015-09-22 21:21:57
Chosen Date: 2015-09-22 21:01:00
proceed
Clearly the chosen date is not greater than the whitelist date but still the if statement returns true and echo's "proceed". I have been over the code over and over but I can't see where I have gone wrong.
It Might be, the time zones are not the same, so try this
$chosen_date = new Carbon($chosen_date, 'Europe/London');
$whitelist_date = Carbon::now('Europe/London');
$whitelist_date->addMinutes(10);
Remember you can always construct the instance and set the timezone for it:
$date = new Carbon();
$date->setTimezone('Europe/London');
$whitelist_date = $date->now();
Any tips on how I can manage data for users with different timezones?
You can create different objects with different Time zones. Try this and play with the results.
$london_date = new Carbon($chosen_date_from_london, 'Europe/London');
$colombia_date = new Carbon($chosen_date_from_colombia, 'Bogota/America');
Let's say you compare them:
$are_different = $london_date->gt($colombia_date);
var_dump($are_different); //FALSE
Nope, they're not different, although they're different times when you stare at the clock and in different parts of the world, they're still in the same Present Moment, the NOW.
There you go, just crate different objects or instances of Carbon(), and set different time zones using $instance->setTimeZone(TimeZone);
Or try using the following one:
if ($chosen_date->gte($whitelist_date))

How to detect Ambiguous and Invalid DateTime in PHP?

When dealing with local DateTime values provided by a user, it's quite possible to have a time that is either invalid or ambiguous, due to Daylight Saving Time transitions.
In other languages and frameworks, there are often methods such as isAmbiguous and isValid, on some representation of the time zone. For example in .NET, there is TimeZoneInfo.IsAmbiguousTime and TimeZoneInfo.IsInvalidTime.
Plenty of other time zone implementations have similar methods, or functionality to address this concern. For example, in Python, the pytz library will throw an AmbiguousTimeError or InvalidTimeError exception that you can trap.
PHP has excellent time zone support, but I can't seem to find anything to address this. The closest thing I can find is DateTimeZone::getTransitions. That provides the raw data, so I can see that some methods could be written on top of this. But do they exist already somewhere? If not, can anyone provide a good implementation? I would expect them to work something like this:
$tz = new DateTimeZone('America/New_York');
echo $tz->isValidTime(new DateTime('2013-03-10 02:00:00')); # false
echo $tz->isAmbiguousTime(new DateTime('2013-11-03 01:00:00')); # true
I 'm not aware of any existing implementations and I haven't had cause to use advanced date/time features such as these as yet, so here is a clean room implementation.
To enable the syntax illustrated in the question we are going to extend DateTimeZone as follows:
class DateTimeZoneEx extends DateTimeZone
{
const MAX_DST_SHIFT = 7200; // let's be generous
// DateTime instead of DateTimeInterface for PHP < 5.5
public function isValidTime(DateTimeInterface $date);
public function isAmbiguousTime(DateTimeInterface $date);
}
To keep distracting details from cluttering the implementation, I am going to assume that the $date arguments have been created with the proper time zone; this is in contrast to the example code given in the question.
That is to say, the correct result will not be produced by this:
$tz = new DateTimeZoneEx('America/New_York');
echo $tz->isValidTime(new DateTime('2013-03-10 02:00:00'));
but instead by this:
$tz = new DateTimeZoneEx('America/New_York');
echo $tz->isValidTime(new DateTime('2013-03-10 02:00:00', $tz));
Of course since $tz is already known to the object as $this, it should be easy to extend the methods so that this requirement is removed. In any case, making the interface super user friendly is out of the scope of this answer; going forward I will focus on the technical details.
isValidTime
The idea here is to use getTransitions to see if there are any transitions around the date/time we are interested in. getTransitions will return an array with either one or two elements; the timezone situation for the "begin" timestamp will always be there, and another element will exist if a transition occurs shortly after it. The value of MAX_DST_SHIFT is small enough that there is no chance of getting a second transition/third element.
Let's see the code:
public function isValidTime(DateTime $date)
{
$ts = $date->getTimestamp();
$transitions = $this->getTransitions(
$ts - self::MAX_DST_SHIFT,
$ts + self::MAX_DST_SHIFT
);
if (count($transitions) == 1) {
// No DST changes around here, so obviously $date is valid
return true;
}
$shift = $transitions[1]['offset'] - $transitions[0]['offset'];
if ($shift < 0) {
// The clock moved backward, so obviously $date is valid
// (although it might be ambiguous)
return true;
}
$compare = new DateTime($date->format('Y-m-d H:i:s'), $this);
return $compare->modify("$shift seconds")->getTimestamp() != $ts;
}
The final point of the code depends on the fact that PHP's date functions calculate timestamps for invalid date/times as if wall clock time had not shifted. That is, the timestamps calculated for 2013-03-10 02:30:00 and 2013-03-10 03:30:00 will be identical on the New York timezone.
It's not difficult to see how to take advantage of this fact: create a new DateTime instance equal to the input $date, then shift it forward in wall clock time terms an amount equal to the DST shift in seconds (it is imperative that DST not be taken into account to make this adjustment). If the timestamp of the result (here the DST rules come into play) is equal to the timestamp of the input, then the input is an invalid date/time.
isAmbiguousTime
The implementation is quite similar to isValidTime, only a few details change:
public function isAmbiguousTime(DateTime $date)
{
$ts = $date->getTimestamp();
$transitions = $this->getTransitions(
$ts - self::MAX_DST_SHIFT,
$ts + self::MAX_DST_SHIFT);
if (count($transitions) == 1) {
return false;
}
$shift = $transitions[1]['offset'] - $transitions[0]['offset'];
if ($shift > 0) {
// The clock moved forward, so obviously $date is not ambiguous
// (although it might be invalid)
return false;
}
$shift = -$shift;
$compare = new DateTime($date->format('Y-m-d H:i:s'), $this);
return $compare->modify("$shift seconds")->getTimestamp() - $ts > $shift;
}
The final point depends on another implementation detail of PHP's date functions: when asked to produce the timestamp for an ambiguous date/time, PHP produces the timestamp of the first (in absolute time terms) occurrence. This means that the timestamps of the latest ambiguous time and the earliest non-ambiguous time for a given DST change will differ by an amount larger than the DST offset (specifically, the difference will be in the range [offset + 1, 2 * offset], where offset is an absolute value).
The implementation takes advantage of this by again doing a "wall clock shift" forward and checking the timestamp difference between the result and the input $date.
See the code in action.

How do you validate a date format?

I want my users to be able to change the date format to whatever they like:
$dateformat = $_POST['format'];
$datestamp = date($dateformat,$timestamp);
How do I check if $dateformat is a correct valid date format string?
if( strtotime($_POST['format']) !== FALSE)
{
echo 'valid date';
}
else
{
echo 'invalid date';
}
But will not work on dates having year > 2048 as not defined in current PHP
checkdate() will tell you whether the date can be parsed should you decide to stick with a certain format:
strtotime() will let you know if a date string can be parsed - you can pass it any string. If the date cannot be parsed, the function returns false.
As a side note: I wouldn't let the user choose how to enter dates. This will make it very complicated for you, obviously - but also as user I would ask myself how I am supposed to enter the date, since most of the users will be used to instruction with regard to dates. Why not provide them with a simple drop down for year, month and date, and then use these three parameters with checkdate()?
I'd recommend you provide your users with a select drop down of various date formats then storing it in a database. You can then pull their selected format and insert it into the first parameter of the date() function.
Have a look into Zend_Date, but without knowing to interpret 06-03-2012 as 6th march or 3rd june it is not possible.
You know? 12/12/12 is the date, were the europeans and the americans can talk about the day without misunderstanding ;-)
Here's a regex implementation:
$input = 'M d Y';
if(preg_match('#(dd|DD|m|M|mm|MM|y|yy|YY|-|\s)+#', $input)) {
$valid = true;
print date($input);
} else {
print "invalid date format";
}
It assumes that the space char and dash chars are allowed.
It specifies flags listed here: http://php.net/manual/en/datetime.formats.date.php
More here: http://php.net/manual/en/function.date.php

Categories