0

我正在寻找一种用于验证日期的通用方法。

我想要的是某种实现验证规则的方法,例如:

First monday of month

monday or friday

Fourth day of month

有什么可以处理这样的要求吗?或者有人对我如何实现这一点有任何指示吗?

编辑:我很欣赏答案,但这是我现在最终使用的内容:

感谢所有的答案。我实际上最终使用的(至少在我找到完美的解决方案之前)大致是这个功能:

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()) );

这让我可以处理我的所有需求,以及 PHP 代码。使用它的人非常有限(<5 人),而且大多数人都有一定程度的 php 经验。现在我可以制定规则,例如:

first monday of month:w:==1 && :d: <= 7

monday or friday:w:==1 || :w:==5

Fourth day of month:d: == 4

我希望有一个更好更灵活的解决方案或有关安全性的反馈(这可以注入恶意 php 代码吗?)

4

2 回答 2

0

我会定义一组规则,例如:

$ordinals = array("First", "Second"...);
$days = array("Monday", "Tuesday"...);

$rules = array(
    array("%1 %2 of month", $ordinals, $days),
    array("%1 or %1", $days),
    etc
)

然后,执行 foreach(rule) 并生成所有可能的允许字符串

$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]);
        }
    }
}

然后做一个 foreach(allowed string) 比较我们所拥有的

foreach($alloweds as $allowed){
    if(preg_match($allowed, $input){
        //its valid
    }
}

你可以将你的规则设置得非常复杂——但值得注意的是,它们越复杂,这将花费的时间越长——在其当前形式中,它显然是一个指数时间算法,但它是一个起点。

于 2013-09-26T09:07:43.390 回答
0

不是最通用的解决方案,但这实际上完全符合您的要求:

班级:

<?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));
    }
}
?>

我不知道您检查的目的是什么,所以我还实现了构造方法来编译代码中未使用的“今天”值。如果您需要它们,请随时使用 self:: 调用它们

如何使用:

<?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);
?>

根据您需要获得的内容更改课程(我的意思是:编辑我设置的回报)。

在这种情况下,上面的代码返回:

2013/9/2 是每月的第一个星期一。

1

第二

而这:

echo $validate->firstInMonth("Tuesday",2,9,2013);
echo "<br />";
echo $validate->checkDayLiteral("Wednesday",15,9,2013);
echo "<br />";
echo $validate->dayPosition(24,9,2013);

返回这个:

没有。

24日

当然,这并不像您想要的那样通用,但这是可行的。

希望这可以帮助。

如果它不完全符合您的要求,对不起,请将此作为一个想法,我认为您很清楚如何从该代码中获取您想要的任何信息。

于 2013-09-26T09:37:57.820 回答