I am looking for a versatile method for validating dates.
What I want, is some way of achieving validation rules such as:
First monday of month
monday or friday
Fourth day of month
Is there anything that can handle such requirements? Or does anyone have any pointers as to how i could achieve this?
Edit: I appreciate the answers, but heres what i ended up using for now:
Thanks for all the answers. What i actually ended up using (At least till i find the perfect solution) is roughly this function:
function isDateValid($rule,$date){
$DATE = (int) $date;
/* Make it safe for eval */
preg_match_all('/:\w:|&&|\|\||<|=|>|!|\(|\)|\d+/i',$rule,$filteredRule);
$rule = implode('',$filteredRule[0]);
//replace rule notations
$f = function($str){
return "date('$str[1]',\$DATE)";
};
//Or if php < 5.3: $f = create_function('$str', 'return "date(\'$str[1]\',\$DATE)";');
$rule = preg_replace_callback('/:(\w):/i', $f, $rule);
//Evaluate expression
return #eval("return $rule;");
}
//Example of usage testing first of month:
var_dump( isDateValid(':d:==1',time()) );
This lets me handle all the requirements i have in, well PHP code. The people who use this is very limited (<5 persons) and most of them have some degree of php experience. Now i can make rules such as:
first monday of month : :w:==1 && :d: <= 7
monday or friday : :w:==1 || :w:==5
Fourth day of month : :d: == 4
I would appreciate an even better more flexible solution or feedback regarding security (Can this be injected with mallicious php code?)
I would define a set of rules something like:
$ordinals = array("First", "Second"...);
$days = array("Monday", "Tuesday"...);
$rules = array(
array("%1 %2 of month", $ordinals, $days),
array("%1 or %1", $days),
etc
)
Then, do a foreach(rule) and generate all possible allowed strings
$alloweds = array();
foreach($rules as $rule){
$str = $rule[0];
for($i = 1; $i < sizeof($rule); $i++){
for($a = 0; $a < sizeof($rule[$i]); $a++){
$alloweds[] = str_replace($str, "%$i", $rule[$i][$a]);
}
}
}
Then do a foreach(allowed string) compare to what we have
foreach($alloweds as $allowed){
if(preg_match($allowed, $input){
//its valid
}
}
you could set up your rules to be quite complicated - however its worth noting that the more complex they are the longer this will take - in its current form its clearly an exponential time algorithm, but its a starting point.
Not the most versatile solution, but this actually does EXACTLY what you want to do:
Class:
<?php
class validate {
private static $month;
private static $year;
private static $day;
private static $day_literal;
function __construct() {
self::$year = date("Y");
self::$month = date("m");
self::$day = date("d");
self::$day_literal = date("l");
}
public function firstInMonth($required,$day,$month,$year) {
$firstday;
for ($i = 1; $i < 31; $i++) {
if (checkdate($month,$i,$year)) {
if (trim(strtolower(date("l",mktime(0,0,0,$month,$i,$year)))) == trim(strtolower(trim($required)))) {
$firstday = $i;
break;
}
else {
continue;
}
}
}
if (strtotime(date("Y-m-d",mktime(0,0,0,$month,$i,$year))) == strtotime(date("Y-m-d",mktime(0,0,0,$month,$day,$year)))) {
return "{$year}/{$month}/{$day} is the first {$required} of the month.";
}
else {
return "Nope.";
}
}
public function checkDayLiteral($literal,$day,$month,$year) {
if (trim(strtolower(date("l",mktime(0,0,0,$month,$day,$year)))) == trim(strtolower($literal))) {
return true;
}
else {
return false;
}
}
public function dayPosition($day,$month,$year) {
return date("jS",mktime(0,0,0,$month,$day,$year));
}
}
?>
I don't know what's the purpose of your checking, so I've also implemented the construct method to compile the "today" values, which are NOT used in the code. If you need them, feel free to call them using self::
How to use:
<?php
$validate = new validate;
echo $validate->firstInMonth("Monday",2,9,2013);
echo "<br />";
echo $validate->checkDayLiteral("Monday",2,9,2013);
echo "<br />";
echo $validate->dayPosition(2,9,2013);
?>
Change the class according to WHAT you need to get (I mean: edit the returns I've set).
In this case, the above code returns:
2013/9/2 is the first Monday of the month.
1
2nd
while this:
echo $validate->firstInMonth("Tuesday",2,9,2013);
echo "<br />";
echo $validate->checkDayLiteral("Wednesday",15,9,2013);
echo "<br />";
echo $validate->dayPosition(24,9,2013);
returns this:
Nope.
24th
Of course, this is not as versatile as whatever you want, but this works.
Hope this helps.
If it does not do exactly what you want I'm sorry, just take this as an idea, I think it's pretty clear how you can get whatever information you want from this code.
Related
I created a small function that helps me build an array (i use it to populate a select2 element). It works great but it doesn't accept 0 as the starting number.
Although it is not really crucial i would really want to understand why this happens and how to fix it.
Here is the function:
function create_numstring_array($startNum, $endNum, $jumps, $sideString = NULL) {
if($startNum && $endNum) {
$data = array();
$counter = intval($startNum);
while($endNum > $counter ) {
$data["$counter"] = $counter.' '.$sideString;
$counter = $counter + $jumps;
// echo $counter."<br />";
}
return $data;
}
}
/* DOESNT WORK
echo '<pre>Code:'."<br />";
print_r(create_numstring_array(0, 9, 0.5, ''));
echo '</pre>'."<br />";
*/
/* WORKS! */
echo '<pre>Code:'."<br />";
print_r(create_numstring_array(1, 9, 0.5, ''));
echo '</pre>'."<br />";
I guess it gets stuck in this part
while($endNum > $counter) {
Since $counter = 0 but how can i overcome this?
Because (bool)0 == False. So, your code fails, because you are testing $startNum and it is treated as boolean false.
Change it to something more reasonable, for example: if (is_int($startNum) ... or functions like that (is_numeric could be candidate)
function create_numstring_array($startNum, $endNum, $jumps, $sideString = NULL){
#check for valid input
#(can be float or integer so lets end always greater than start)
if($startNum>$endNum || !is_numeric($jumps)) {
return null;
}
#create the range
$keys = range($startNum, $endNum, $jumps);
#create values with or without sideString
$values = ($sideString)
? array_map(function($a) use ($sideString){ return $a.' '.$sideString;},$keys)
: $keys;
#return the new array
return array_combine($keys,$values);
}
echo '<pre>Code:'."<br />";
print_r(create_numstring_array(0, 9, 0.5, ''));
echo '</pre>'."<br />";
Why your version is not working, is explained in the comments, so here a working version, that check for valid input and valid jumbs. (Works with float and integer). Remove/Skipp last and first entry is they are not needed.
Like the title says, PHP is really confusing me on a simple if comparison statement that's returning the opposite of what it should be returning. I'm trying to compare 2 datetime's that are first converted to strings:
//Fetched db query, this returns 2012-06-23 16:00:00
$databaseDateTime = strtotime($row['time']);
//This now returns 1340481600
//today's date and time I'm comparing to, this returns 2012-06-22 17:14:46
$todaysDateTime = strtotime(date("Y-m-d H:i:s"));
//this now returns 1340399686
Great, everything works perfect so far. Now here's where things get hairy:
if ($databaseDateTime < $todaysDateTime) { $eventType = 'past'; }
And this returns 'past', which of course it shouldn't. Please tell me I'm missing something. My project kind of depends on this functionality being airtight.
**EDIT***
Thanks guys for taking the time to help me out. Let me post the entire code because a few of you need more context. The request is coming from an IOS5 to my backend code and json is being sent back to the phone.
<?php
//all included files including $link to mysqli_db and function sendResponse()
function getEvents($eventType, $eventArray) {
global $link;
global $result;
global $i;
global $todaysDateTime;
foreach ($eventArray as $key => $value) {
$sqlGetDeal = mysqli_query($link, "SELECT time FROM deals WHERE id='$value' AND active='y' LIMIT 1") or die ("Sorry there has been an error!");
while ($row = mysqli_fetch_array($sqlGetDeal)) {
//compare times to check if event already happened
$databaseDateTime = strtotime($row['time']);
if ($databaseDateTime < $todaysDateTime) { $eventType = 'past'; }
$result[$i] = array(
'whenDeal' => $eventType,
'time' => $databaseDateTime,
);
$i++;
}//end while
}//end foreach
}
if (isset($_GET['my'])) {
//$_GET['my'] comes in as a string of numbers separated by commas e.g. 3,2,6,3
$myDeals = preg_replace('#[^0-9,]#', '', $_GET['my']);
$todaysDateTime = strtotime(date("Y-m-d H:i:s"));
$result = array();
$kaboomMy = explode(",", $myDeals);
$i = 1;
if ($myEvents != "") {
getEvents('future', $kaboomMy);
}//end if
sendResponse(200, json_encode($result));
} else {
sendResponse(400, 'Invalid request');
} //end $_POST isset
?>
Found a quick hack around the issue. I just added a local variable to my function and rearranged my compare statement
//added local variable $eventTyppe to function
$eventTyppe;
changed compare from:
if ($databaseDateTime < $todaysDateTime) { $eventType = 'past'; }
to:
if ($todaysDateTime < $databaseDateTime ) {
$eventTyppe = $eventType;
} else {
$eventTyppe = 'past';
}
Notice if I rearrange compare:
if ($databaseDateTime < $todaysDateTime ) {
$eventTyppe = 'past';
} else {
$eventTyppe = $eventType;
}
I still get the same error. This is the weirdest thing I've ever seen and the first PHP bug I've run into (I'm assuming it's a PHP bug).
Could you print the values of the times right before this line?
if ($databaseDateTime < $todaysDateTime) { $eventType = 'past'; }
Since that one is declared as global I'm wondering if is it coming back incorrectly.
I was wondering if there is any way to detect if a number is negative in PHP?
I have the following code:
$profitloss = $result->date_sold_price - $result->date_bought_price;
I need to find out if $profitloss is negative and if it is, I need to echo out that it is.
if ($profitloss < 0)
{
echo "The profitloss is negative";
}
Edit: I feel like this was too simple an answer for the rep so here's something that you may also find helpful.
In PHP we can find the absolute value of an integer by using the abs() function. For example if I were trying to work out the difference between two figures I could do this:
$turnover = 10000;
$overheads = 12500;
$difference = abs($turnover-$overheads);
echo "The Difference is ".$difference;
This would produce The Difference is 2500.
I believe this is what you were looking for:
class Expression {
protected $expression;
protected $result;
public function __construct($expression) {
$this->expression = $expression;
}
public function evaluate() {
$this->result = eval("return ".$this->expression.";");
return $this;
}
public function getResult() {
return $this->result;
}
}
class NegativeFinder {
protected $expressionObj;
public function __construct(Expression $expressionObj) {
$this->expressionObj = $expressionObj;
}
public function isItNegative() {
$result = $this->expressionObj->evaluate()->getResult();
if($this->hasMinusSign($result)) {
return true;
} else {
return false;
}
}
protected function hasMinusSign($value) {
return (substr(strval($value), 0, 1) == "-");
}
}
Usage:
$soldPrice = 1;
$boughtPrice = 2;
$negativeFinderObj = new NegativeFinder(new Expression("$soldPrice - $boughtPrice"));
echo ($negativeFinderObj->isItNegative()) ? "It is negative!" : "It is not negative :(";
Do however note that eval is a dangerous function, therefore use it only if you really, really need to find out if a number is negative.
:-)
if(x < 0)
if(abs(x) != x)
if(substr(strval(x), 0, 1) == "-")
You could check if $profitloss < 0
if ($profitloss < 0):
echo "Less than 0\n";
endif;
if ( $profitloss < 0 ) {
echo "negative";
};
Don't get me wrong, but you can do this way ;)
function nagitive_check($value){
if (isset($value)){
if (substr(strval($value), 0, 1) == "-"){
return 'It is negative<br>';
} else {
return 'It is not negative!<br>';
}
}
}
Output:
echo nagitive_check(-100); // It is negative
echo nagitive_check(200); // It is not negative!
echo nagitive_check(200-300); // It is negative
echo nagitive_check(200-300+1000); // It is not negative!
Just multiply the number by -1 and check if the result is positive.
You could use a ternary operator like this one, to make it a one liner.
echo ($profitloss < 0) ? 'false' : 'true';
I assume that the main idea is to find if number is negative and display it in correct format.
For those who use PHP5.3 might be interested in using Number Formatter Class - http://php.net/manual/en/class.numberformatter.php. This function, as well as range of other useful things, can format your number.
$profitLoss = 25000 - 55000;
$a= new \NumberFormatter("en-UK", \NumberFormatter::CURRENCY);
$a->formatCurrency($profitLoss, 'EUR');
// would display (€30,000.00)
Here also a reference to why brackets are used for negative numbers:
http://www.open.edu/openlearn/money-management/introduction-bookkeeping-and-accounting/content-section-1.7
Can be easily achieved with a ternary operator.
$is_negative = $profitloss < 0 ? true : false;
I wrote a Helper function for my Laravel project but can be used anywhere.
function isNegative($value){
if(isset($value)) {
if ((int)$value > 0) {
return false;
}
return (int)$value < 0 && substr(strval($value), 0, 1) === "-";
}
}
Does anyone know of a good class / library to convert English representations of time into timestamps?
The goal is to convert natural language phrases such as "ten years from now" and "three weeks" and "in 10 minutes" and working out a best match unix timestamp for them.
I have hacked up some pretty poor and untested code to get going on it, but I am sure there are great parsers out there for calendars and such.
private function timeparse($timestring)
{
$candidate = #strtotime($timestring);
if ($candidate > time()) return $candidate; // Let php have a bash at it
//$thisyear = date("Y");
if (strpos($timestring, "min") !== false) // Context is minutes
{
$nummins = preg_replace("/\D/", "", $timestring);
$candidate = #strtotime("now +$nummins minutes");
return $candidate;
}
if (strpos($timestring, "hou") !== false) // Context is hours
{
$numhours = preg_replace("/\D/", "", $timestring);
$candidate = #strtotime("now +$numhours hours");
return $candidate;
}
if (strpos($timestring, "day") !== false) // Context is days
{
$numdays = preg_replace("/\D/", "", $timestring);
$candidate = #strtotime("now +$numdays days");
return $candidate;
}
if (strpos($timestring, "year") !== false) // Context is years (2 years)
{
$numyears = preg_replace("/\D/", "", $timestring);
$candidate = #strtotime("now +$numyears years");
return $candidate;
}
if (strlen($timestring) < 5) // 10th || 2nd (or probably a number)
{
$day = preg_replace("/\D/", "", $timestring);
if ($day > 0)
{
$month = date("m");
$year = date("y");
return strtotime("$month/$day/$year");
}
else
{
return false;
}
}
return false; // No can do.
}
Use the DateTime class.
e.g.:
$string='four days ago';
$d=date_create($string);
$d->getTimestamp();
ETA:
which you could extend:
class myDateTime extends DateTime {
static $defined_expressions=array(...);
function __construct($expression=NULL) {
if ($exp=$this->translate($expression)) {
parent::__construct($exp);
}
}
function translate($exp) {
//check to see if strtotime errors or not
//if it errors, check if $exp matches a pattern in self::$defined_expressions
return $exp, modified $exp or false
}
}
Sometime ago I had come across http://www.timeapi.org which converts natural language queries into time. It is an API though.
The ruby source code is on github. If need be, I guess you could try to port it to PHP.
Just got a notification from PHPClasses, with one of the runner-ups of the monthly innovation award: Text to Timestamp
You could try that...
Cocoa's and GNUStep's NSDateFormatter are able to handle such time representations. The GNUStep version is open-source.
I'm in PHP working on an Euler problem. I have this function so far:
<?php
$biggest = 0;
$counter = 1;
function test($i){
global $biggest;
global $counter;
if ($i == 1) {
echo "I'm done! Took me $biggest steps";
}
else {
if ($i%2 == 0) {
$counter = $counter + 1;
if ($counter>$biggest) {
$biggest = $counter;
}
test($i/2);
}
else {
$counter = $counter + 1;
if ($counter>$biggest) {
$biggest = $counter;
}
test(3*$i+1);
}
}
}
test(13);
?>
I have the problem mostly licked, but I can't seem to get back at the original input. The question is "When you have a number, if odd get 3n+1, when even, get n/2, do until returns 1. What starting value yields the most "steps" before you get to one?" I currently am returning the number of steps, but I keep resetting $i as I recurse, so I can't record what starting # yielded my $biggest number of steps.
How can I keep track of that number, but also not have it destroyed at the next instance of the loop? (I'll eventually wrap this in a for ($i=1, $i<1000000, $i++) loop)
Thanks!
A common approach is to pass the original argument through each time, so that when eventually you get to your base case, you still have it available. A primitive (and almost entirely unrelated example):
<?php
function fact($n) {
if($n == 1) return 1;
else return $n * fact($n - 1);
}
?>
This is an extremely basic implementation of the factorial function in PHP. Now say you wanted for whatever reason to have the initial value available in the final step: you'd build a wrapper function fact($n) that would call something like memory_fact($n, $initial):
<?php
function fact($n) {
return memory_fact($n, $n);
}
function memory_fact($n, $initial) {
if($n == 1) return 1;
else return $n * memory_fact($n - 1, $initial);
}
?>
This way, memory_fact always knows where it started.
It's easy, just pass it around as a parameter! Here's some python-ish pseudocode:
def func(start, arg):
if foo(arg):
return func(start, arg+1)
else:
return [start, arg]
You don't need the globals; globals are evil. Try returning something useful from test(). Also, you'll find the test() above wastes many cycles. Try using memoization.
Here's a memoization example for calculating Fibonacci numbers:
function fib($n) {
static $data = array(1, 1);
if (!isset($data[$n])) {
$data[$n] = fib($n-1) + fib($n-2);
}
return $data[$n];
}
Note that there are other time-efficent constant-space approaches to handle Fibonacci numbers (including one in O(log n) time), but the Collatz conjecture is a little trickier.