Disable over-permissive behavior of DateTime::createFromFormat - php

Given an invalid date in the format MMDD, say 1332, the DateTime::createFromFormat method in PHP 5.3.0+ will accept it as a valid date, 0201 in that same MMDD format.
Code snippet:
$dateobj = DateTime::createFromFormat("md", "1332");
if ($dateobj) {
print $dateobj->format('Y/m/d H:i:s') . "\n";
}
Output:
2019/02/01 20:59:37
Obviously, 13 is not a valid month of the year and 32 is not a valid day of any month. It's also apparent that DateTime::createFromFormat is "rolling over" those numbers, as if it were adding 13 months and 32 days to a zero value (the current datetime's year). One month after December (month 12) is January (in this case month "13"), and 32 days after January 1 (inclusive) is February 1.
Is there a way to still use DateTime::createFromFormat but disable, override, or otherwise work around that specific over-permissive behavior?

One way to use DateTime::createFromFormat, and it alone, is to compare the DateTime object it created to the original input:
$dateobj = DateTime::createFromFormat($format, $date);
if ($dateobj && $dateobj->format($format) == $date) {
print($dateobj->format($format));
}
If they're the same, the input date is valid. If they're not the same, DateTime::createFromFormat did its "rollover" calculation and you have an invalid input date.
This was actually a solution given on the checkdate PHP Manual page as a User Contributed Note, though it doesn't involve checkdate itself.

Related

PHP doesn't give a proper output for month substraction

Code:
$time = strtotime('2020-03-31');
echo date('Y-m-d', strtotime('-1 month', $time));
Expected Result: Any date from Feb 2020
Actual Result: 2020-03-02
Is there any better way to add or subtract a month from a given date?
Months are an awkward interval to work with, because they don't have a fixed length. Should the algorithm assume that by "1 month" you mean "30 days", or "31 days", or should it just try subtracting 1 from the "month" field in the date structure?
The last option is what is happening here: given "2020-03-31", PHP's date library is subtracting 1 from the "03" to give "2020-02-31". Since that's an invalid date (February 2020 had 29 days), it then "normalises" it to a real date - 2 days after the 29th February was the 2nd March.
Probably you want to use a more specific period to subtract, like 30 days - although note that if the initial input is "2020-03-01" that will give you "2020-01-31", not "2020-02-01".
Ultimately, this is a problem with our irregular calendar, rather than with PHP. It's really up to you to define what you mean by "a month before", and use a more specific algorithm that captures that requirement.
You can make code like below
<?php
$time = strtotime('2020-03-1 -32 days');
echo date('M-Y', $time); // output Feb-2020
?>
The above code will return date as you expected

How to get full date from string without having the actual month in the string in PHP?

Is there any way to get the full date from a string that does not actually contain the month?
i am given the date in a format of Wednesday 16th and need to add this to my database with a month,
The application i am making is for lifestyle couriers and they get their manifests in that format and have the last 2 months available, so i need to find out the month?
Assuming that you want to regard the current and the last two months, I can imagine, that there will be no two day numbers of the same week day in that timespan (that should be proven first).
I would iterate back over the last n days using PHP's date function and try to detect your "Wednesday 16th" where n is the aggregate of month days that date with param "t" returns for the current and the last two months.
If you have your match then, you know the month and the year (the year could be the previous year as well if you start in January or February).
You can do it like this:
<?php
$date = 16;
$day = "Wednesday";
$oneMonthAgo = explode(" ",date("m Y F",strtotime("-1 month")));
$twoMonthsAgo = explode(" ",date("m Y F",strtotime("-2 months")));
if (date("l",strtotime($date."-".$oneMonthAgo[0]."-".$oneMonthAgo[1]))==$day) {
echo "Last month: ".$oneMonthAgo[2];
}
else if (date("l",strtotime($date."-".$twoMonthsAgo[0]."-".$twoMonthsAgo[1]))==$day) {
echo "Month before last: ".$twoMonthsAgo[2];
}
else {
echo "Not valid";
}
?>
Explanation
Because you are using PHP, there is an easy way to do this. In other languages, you would have to use a weekday algorithm.
In PHP, we can convert a properly formatted string into a time, from which we can then derive date information. Examples of a properly formatted string is -1 month or -2 months, which return time objects for the previous month or the month before that, respectively.
Using the date() function, we can get the month, year and textual month values for those months. We delimiter these by a space, so that we can explode these values into an array after they are found. Thus, we now have two arrays, $oneMonthAgo and $twoMonthsAgo, which both contain their month's respective number, year and textual month at the 0, 1 and 2 indexes respectively.
Now, we can create another time, by appending the date that we are looking for ($date) onto these values, with the proper delimiters in-between (when using - as the delimiters, PHP assumes a d-m-Y format, when using / PHP assumes a m-d-Y format). Then, by passing that through another date() function, we can find the data that we are looking for: the textual day that took place at that date. Now we can simply compare that to the day that we are looking for, $day. If the textual day is the same, then this is the month which we are looking for, so we can print out the full textual month.
We do this in an if / else statement for both months, and if we still haven't found it, assume that the date is invalid.
Notes
References to the PHP functions used can be found on the PHP Manual: date() function, strtotime() function, Valid dates for strtotime()
Yes and no. Since full date requires month to be known you need to know what to do with lack of that information. If you "guess" the month (be it current one or random, does not matter), then "yes" is close. If you cannot tell what the month we talk about then answer is no (or random).
PS: you may need a year too...
I use a while loop to loop backwards however many months needed to find what is searched for.
$date = 16;
$day = "Wednesday";
$x=0;
$start = date("Y-m-") . $date;
While(date("l", strtotime($start ."-" .$x . "months")) != $day){
$x++;
}
Echo $x . " months ago.";
// Or to output the date:
//Echo date("Y-m-d", strtotime($start ."-" .$x . "months"));
https://3v4l.org/vUYeX
This will output 0 months ago.
But if input $date= 15; it will output 5 months ago (March 2017).

PHP 6 digit date code display in human readable format

I have a set of dates that are formatted like this...
197402
192201
184707
The first four digits represents the year and the remaining two the month. I am trying to output these in this format
February 1974
January 1922
July 1847
I have tried passing it to the date function like this...
echo date ('F Y', 197402)
But this is giving me January 1970 everytime so I assume I have misunderstood how the date function works, can anyone help?
You're getting "January 1970" as an output, because you tried to create a date from the timestamp 197402, which is seconds from January 1st, 1970. If you output the full string from that (with seconds and whatnot), you'll see it's a valid timestamp, producing an actual date, but they all end up in the start of January 1970, see this online demo.
That format, YYYYMM, isn't a recognizable format for most functions. You need to split it up, if you know the format will be in that way - and use that data instead. You can use substr() to split the string, and then convert the numerical month to the string associated with that month, with the help of date() and mktime() (since you just specify the year and month).
The following snippet
$arr = [197402, 192201, 184707];
foreach ($arr as $v) {
$year = substr($v, 0, 4);
$month = substr($v, 4, 2);
echo date("F Y", mktime(0, 0, 0, $month, 0, $year))."<br />"; // mktime() produces a valid timestamp based on just month and year
// Alternatively, drop mktime() and use strtotime() and create from a standard format,
// while specifying a date in the month (which won't matter to the output)
// echo date("F Y", strtotime("$month/01/$year"))."<br />";
}
will output
February 1974
January 1922
July 1847
Alternatively, you can use the DateTime class (which is a lot simpler to work with), and create from a given format with date_create_from_format()
foreach ($arr as $v) {
echo date_create_from_format('Yh', $v)->format('F Y')."<br />";
}
This will generate the same output as above.
References
http://php.net/substr
http://php.net/mktime
http://php.net/date
http://php.net/datetime.createfromformat
I'd use the DateTime class, you can create from a specific format, and then output to another.
As pointed out in the comments below, you also need to set the day to the first of the month, otherwise you'll get undesired results if the current day is greater than the number of days in the given month.
echo DateTime::createFromFormat('Ymd', 19470201)->format('F Y');
As you are passing an int to date, it is considering it as a Unix Timestamp.
To create a date object from a predefined format, use DateTime::createFromFormat.
echo DateTime::createFromFormat('Ym',198403)->format('F Y');
results in
March 1984
You'd need to add a day to it, e.g. just add "01" and then use strtotime to convert that into a unix timestamp, as the date() function expects a timestamp as the parameter.
e.g.
echo date('F Y', strtotime("19220101"));
You have text representation of dates in a non-standard format. First you have to parse them and convert them to timestamps (the number of seconds since Jan 1, 1970 00:00:00 UTC). The PHP function date() can work only with timestamps.
The best approach (as of 2017) is to use the DateTime PHP class for date & time processing:
foreach (array('197402', '192201', '184707') as $text) {
$date = DateTime::createFromFormat('Ym', $text);
echo($date->format('F Y')."\n");
}
The method DateTime::createFromFormat() parses a string using the given format and creates a new DateTime object if the parsing succeeds. It is the OOP equivalent of strtotime() but smarter (because it can get hints about what date components to search in the input string.)
The method DateTime::format() produces the text representation of a date using the provided format. It is the OOP equivalent of date().
The OOP approach (the DateTime* classes) is recommended (and better than the procedural approach) because has built-in support for timezones (the procedural date-time functions lack it.)

PHP method strtotime is not resulting well

function DateFormat($dt)
{
return $newDate = date("d/m/Y", strtotime($dt));
}
$cr='2014-02-31';
echo DateFormat($cr);
Input: $cr='2014-02-31';
Output: 03/03/2014
I am passing 2014-02-31 and getting output 03/03/2014.
Please help me out.
PHP's date functions work with dates not strings. And that's an important distinction. Strings are just a bunch of characters in a specified order. Dates have months, days, years, hours, minutes, seconds, timezones, etc. When PHP works with dates it takes all of them into consideration.
So when you pass Feb 31 to a PHP date function it is going to try to make sense of it as a date and not a string. This means it isn't just going to take that date cut it up into bits and then rearrange them as you are expecting. It is going to turn that date into a date representation it can work with and then manipulate it.
As we all know, February does not have 31 days. As a result of the invalid date, PHP is trying to be helpful and taking three days after last day in February of that year (since Feb only has 28 days this year) and giving you that date.
The issue is February most years only has 28 days. 2/31 would be logically translated to 3/3. On a leap year you'd get 3/4...
The strtotime() method as it needs to be very flexible to be able to handle stuff without borking like:
strtotime('2014 February + 31 day - 1 year');
And no I don't think it should error out. When you have a well formed date string, PHP has a deceptively named method called checkdate() you could use:
$crappy_date='2014-02-31';
$date_parts = explode('-', $crappy_date);
$valid = checkdate($date_parts[1], $date_parts[2], $date_parts[0]);

Date('now') in PHP

After a long time I needed to use date function of PHP. I wrote something like:
echo date('now');
and I got the output below:
1220123
What does that mean ?
From the PHP manual :
n Numeric representation of a month, without leading zeros
o ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that
year is used instead. (added in PHP 5.1.0)
w Numeric representation of the day of the week
So, date("now") displays 12 (n), 2012 (o) and 3 (w).
You're probably looking for :
date("Y-m-d") for a date
date("Y-m-d H:i:s") for a datetime
"now" is not a valid parameter for for this expectation, infact it should be strtotime function here, not date.
Date considers your now as
n
Numeric representation of a month, without leading zeros
o
ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0)
w
Numeric representation of the day of the week
you need to give a valid format to date function (not recognize the 'now' string as meaning of now )
$date = date("Y-m-d H:i:s");
or you can use the DateTime class
$date = new DateTime();
Seems you consider "now" as a word to get the current date and time, however it would compile on each character. Here is the explanation how it'll compile.
n = Month in number
o = It considers as a year in ISO-8601.
w = Week in number
So that's why it's returning you the date, year and number of week in a month.
Hope I can explain you bit easily.
"now" is not a valid parameter for date()
Correct syntax to print current date in
yyyy-mm-dd hours minutes seconds
format is as given below
echo date('Y-m-d h:i:s');
also see PHP manual for details of date() function
http://php.net/manual/en/function.date.php

Categories