(PHP) strtotime function false positive with char - php

I don't understand why this is a valid date
strtotime("1920-09k-12") // -1556877600 // I expect false
I see that adding a char after the month or day is a valid date.
strtotime("1920-09-12d") // -1555905600 // I expect false
strtotime("1920-09n-12") // -1556838000 // I expect false
Instead
strtotime("1920-09k-12k") // false
strtotime("1920r-09-12") // false
strtotime("1920-09-12") // -1555862400
Is this the expected behaviour?
I use Laravel and the strtotime function is used for validate date in the framework but when go to save a record in DB with a "false positive" date a QueryException is raised.
I resolved the problem with a custom validation but i'm curious to know why strtotime has this behaviour.

This may not fully answer your question, but examining the results of date_parse() for your sample dates, it appears that the first letter in the string is interpreted as the timezone, and the remainder of the string as well, which either causes a warning or an error of "Double timezone specification", and in the case of an error, the DateTime cannot be created.
For example:
date_parse("1920-09-12d")
Array
(
[year] => 1920
[month] => 9
[day] => 12
[hour] =>
[minute] =>
[second] =>
[fraction] =>
[warning_count] => 0
[warnings] => Array
(
)
[error_count] => 0
[errors] => Array
(
)
[is_localtime] => 1
[zone_type] => 2
[zone] => -240
[is_dst] =>
[tz_abbr] => D
)
Notice the timezone "D"
date_parse("1920-09n-12")
Array
(
[year] => 1920
[month] => 9
[day] => 1
[hour] =>
[minute] =>
[second] =>
[fraction] =>
[warning_count] => 1
[warnings] => Array
(
[8] => Double timezone specification
)
[error_count] => 0
[errors] => Array
(
)
[is_localtime] => 1
[zone_type] => 2
[zone] => 60
[is_dst] =>
[tz_abbr] => N
)
Notice the timezone "N", furthermore the 12 is not interpreted as the day of month, but rather I suspect "-12" is interpreted as an additional timezone specification, hence the warning.
date_parse("1920r-09-12")
Array
(
[year] =>
[month] =>
[day] =>
[hour] => 19
[minute] => 20
[second] => 0
[fraction] =>
[warning_count] => 1
[warnings] => Array
(
[5] => Double timezone specification
)
[error_count] => 1
[errors] => Array
(
[8] => Double timezone specification
)
[is_localtime] => 1
[zone_type] => 2
[zone] => 300
[is_dst] =>
[tz_abbr] => R
)
Notice the timezone "R", furthermore no date is parsed, rather 1920 is interpreted as the time 19:20:00, and I suspect the remainder of the string is interpreted as 2 timezones, "-09" & "-12", causing the error "Double timezone specification".

Related

Unknown date format from API response

API Server responds with token expiration date in the following format:
2022-05-09T02:11:27.747
What format is it?
That's ISO-8601 standard time format. Year month day T hour minute second millisecond. The date_parse function will handle this.
<?php
$x = '2021-04-01T19:18:17.654';
print_r(date_parse($x));
?>
Output:
Array
(
[year] => 2021
[month] => 4
[day] => 1
[hour] => 19
[minute] => 18
[second] => 17
[fraction] => 0.654
[warning_count] => 0
[warnings] => Array
(
)
[error_count] => 0
[errors] => Array
(
)
[is_localtime] =>
)

Check if multiple datetime are same in php foreach

I want to split items which have same date and different date.
foreach ($cart->getAllItems() as $item)
{
$pickupDateTime = $item->getCartPickupDate().' '.$itemgetCartPickupTime();
$pickupDateTime = date('Y-m-d G:i:s', strtotime($pickupDateTime)) //2018-03-09 6:03:00 or 2018-03-09 21:10:00
// Split items here with same Date //
if(Dates are Same Condition){
$items_normal[]= $item->getProductId();
}else
{
//If Dates Are diffrent
$ites_special[]= $item->getProductId();
}
}
Need to compare if dates are same in first if condition and in else part the items which have different dates.
Look at the following arrays, in this I have same date and time, I need to club those which have same date and time. In below example, the 3rd one must be come under first Array as it has different time i.e. 6:05 instead of 6:03
Array
(
[1] => Array
(
[product_id] => 742
[qty] => 1
[date] => 03/09/2018
[time] => 06:03 AM
[tax] => 0
[splinst] => null
)
[2] => Array
(
[product_id] => 743
[qty] => 1
[date] => 03/09/2018
[time] => 06:03 AM
[tax] => 0
[splinst] => null
)
)
Array
(
[3] => Array
(
[product_id] => 744
[qty] => 1
[date] => 03/09/2018
[time] => 06:03 AM
[tax] => 0.12
[splinst] => null
)
[4] => Array
(
[product_id] => 757
[qty] => 1
[date] => 03/09/2018
[time] => 06:05 AM
[tax] => 0.25
[splinst] => null
)
)
You should use the DateTime object:
$pickupDateTime = $item->getCartPickupDate().' '.$itemgetCartPickupTime();
$pickupDateTime = new DateTime($pickupDateTime);
You can compare two DateTime objects which each other:
if ($dateTimeObject1 == $dateTimeObject2) {
// both have same date and time
}
Be aware that you should not use strict comparison (===) here as it would check if it is the same object and not if the DateTime is the same.
If you need to compare only the date and ignore the time you can call setTime on both objects to reset the time to 0:
$dateTimeObject1->setTime(0, 0, 0);
$dateTimeObject2->setTime(0, 0, 0);
if ($dateTimeObject1 == $dateTimeObject2) {
// both have the same date
}
You could store the date in keys into an array to group items :
foreach ($cart->getAllItems() as $item)
{
$pickupDateTime = $item->getCartPickupDate().' '.$itemgetCartPickupTime();
$pickupDateTime = date('Y-m-d G:i:s', strtotime($pickupDateTime)) //2018-03-09 6:03:00 or 2018-03-09 21:10:00
$items_special[$pickupDateTime][] = $item->getProductId();
}
foreach ($items_special as $date => $items) {
if (count($items) > 1) {
$items_normal = $items ;
unset($items_special[$date]);
break;
}
}
$items_special = array_values($items_special);
So, $items_special will look like :
Array
(
[0] => Array
(
[product_id] => 757
[qty] => 1
[date] => 03/09/2018
[time] => 06:05 AM
[tax] => 0.25
[splinst] => null
)
)
And $items_normal will look like :
Array
(
[0] => Array
(
[product_id] => 742
[qty] => 1
[date] => 03/09/2018
[time] => 06:03 AM
[tax] => 0
[splinst] => null
)
[1] => Array
(
[product_id] => 743
[qty] => 1
[date] => 03/09/2018
[time] => 06:03 AM
[tax] => 0
[splinst] => null
)
[2] => Array
(
[product_id] => 744
[qty] => 1
[date] => 03/09/2018
[time] => 06:03 AM
[tax] => 0.12
[splinst] => null
)
)

PHP preg_match constant output

I'm trying to parse out a string that could have multiple values and if one doesn't exist the array is outputting the part I'm looking for up one spot, causing me logic headaches.
What I'm trying to parse: P1DT12H15M, or PT1H5M, or PT15M
Basically it's P(Number of Days) T(Number of Hours)(Number of Minutes). The P and T are constant. Here's the match string I have so far:
'/P([0-9]*?)D?T([1-2]?[0-9]?)H?([1-5]?[0-9]?)M?/'
It pulls everything apart, but the array output is not what I'm looking for.
PT2H gives Array ( [0] => PT2H [1] => [2] => 2 [3] => )
PT2H15M gives Array ( [0] => PT2H15M [1] => [2] => 2 [3] => 15 )
But
PT15M gives Array ( [0] => PT15M [1] => [2] => 15 [3] => )
I need that number to be in position 3, if possible.
$exp = '/P(?:([0-9]+)D)*T(?:([1-2]?[0-9])H)*(?:([1-5]?[0-9])M)*/';
preg_match($exp, 'PT2H15M', $m); print_r($m);
preg_match($exp, 'PT15M', $m); print_r($m);
preg_match($exp, 'P1DT12H15M',$m); print_r($m);
result
Array
(
[0] => PT2H15M
[1] =>
[2] => 2
[3] => 15
)
Array
(
[0] => PT15M
[1] =>
[2] =>
[3] => 15
)
Array
(
[0] => P1DT12H15M
[1] => 1
[2] => 12
[3] => 15
)
So, you can take $P = m[1], $H= m[2], $M = m[3]
Your input strings looks like time interval specifiers. Let the PHP internal class DateInterval parse them then access the properties of [DateInterval] to get the values you need:
$int = new DateInterval('P1DT12H15M');
print_r($int);
produces:
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 1
[h] => 12
[i] => 15
[s] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] =>
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
You can access the properties directly (they are public):
printf("%d days, %d hours, %d minutes\n", $int->d, $int->h, $int->i);
If you need them as array you can even convert $int to an array and use them this way:
$date = (array)$int;
printf("%d days, %d hours, %d minutes\n", $date['d'], $date['h'], $date['i']);
If you insist, you can even emulate the behaviour of the preg_match() you struggle to create:
$string = 'P1DT12H15M';
$interval = new DateInterval($string);
$array = (array)$interval;
$matches = array_merge(array($string), array_slice(array_values($array), 0, 6));
print_r($matches);
displays:
Array
(
[0] => P1DT12H15M
[1] => 0
[2] => 0
[3] => 1
[4] => 12
[5] => 15
[6] => 0
)
(use array_slice(..., 3, 6) to keep only the time components)

Formatting dates in an array

I have an array which has changing key depending on the table and it can also have custom fields added. I want to be able to identify if the array contains any values that are dates and then format the date.
I have used two examples here, but they keys could be anything and the dates could be including date and/or time. The language I am using in my app is PHP5.5.
Any ideas?
Array
([0] => Array (
[id] => 1
[filename] => 1051404995566.png
[createdby] => 1
[createddate] => 2014-07-10 13:32:46
[enddate] => 2014-07-10
)
[1] => Array (
[id] => 2
[filename] => 1561404995587.png
[createdby] => 1
[createddate] => 2014-07-10 13:33:07
[enddate] => 2014-08-01
)
)

While iterating through an array of associative arrays, does PHP's foreach order iterations by key instead of index?

I am having troubling using foreach with an array of associative arrays, where the keys in the associative arrays are numbers.
$rows = $_POST["row"];
// print_r($rows);
foreach ($rows as $r) {
fwrite($f, $r["date"]);
fwrite($f, "#");
fwrite($f, $r["desc-short"]);
fwrite($f, "#");
// etc.
}
The POST variable contains arrays identified by row[index]. If I stick in a print_r() it displays the POST values in the order they appeared in the original form (which is not necessarily numerical order, as rows can be inserted in the middle and the counter represents when they were added, not where), but when I iterate with foreach it ends up printing row[8] (assuming eight rows) last, even though it was inserted after row 2 (for example).
It seems that because my keys are numbers, foreach is treating the keys as if they were the order. How can I avoid this behavior?
Output of example data from print_r($rows):
Array ( [1] => Array ( [date] => 12/12/2013 [desc-short] => Show title [desc-long] => A sample long description [start-time] => 12:30 [duration] => 13 [rating] => TVY ) [2] => Array ( [date] => 12/12/2013 [desc-short] => TEST [desc-long] => TEST [start-time] => 12:45 [duration] => 14 [rating] => TVY ) [8] => Array ( [date] => 12/12/2013 [desc-short] => Calendar of Events [desc-long] => A list of local events displayed every hour on the hour [start-time] => 13:00 [duration] => 15 [rating] => TVY ) [3] => Array ( [date] => 12/12/2013 [desc-short] => Show title [desc-long] => A sample long description [start-time] => 12:45 [duration] => 12 [rating] => TVY ) [4] => Array ( [date] => 12/12/2013 [desc-short] => Calendar of Events [desc-long] => A list of local events displayed every hour on the hour [start-time] => 13:00 [duration] => 15 [rating] => TVY ) [5] => Array ( [date] => 12/12/2013 [desc-short] => Show title [desc-long] => test [start-time] => 13:15 [duration] => 100 [rating] => TVY ) [6] => Array ( [date] => 12/12/2013 [desc-short] => Calendar of Events [desc-long] => A list of local events displayed every hour on the hour [start-time] => 15:00 [duration] => 15 [rating] => TVY ) )
And yes, 7 is missing. I'll have to look into that as well. Rows number 1-6 were loaded from a file, while [8] was added in their midst later with JavaScript.
You can't avoid this if your keys are stuck like this. That's just how PHP works. If you need to maintain the order then you'll need to prefix numbers with letter

Categories