2008年01月04日

日付入力のチェックその2

前回に引き続き日付入力のチェックを考えてみます。今回は前回の課題?であった感覚的な入力(期待値)との差異という部分をなんとかしてみたいとおもいます。

まず前回定めたルールのうち1〜4まではそのままで問題ないとおもいますが、5.の年月日のどれかが省略された場合は1を補うというルールおよび6.の不正な値はmktime()に任せるという2つのルールをもう一度練り直してみます。

5.のルールは単一の年月日だけならばよいのですが、範囲指定した場合の後のほうの年月日にこれを適用してしまうと、感覚的にちょっと違和感があります。例えば…
2005.04.06-10.25

のような入力の場合、現状だと…
2005.04.06-2012.01.01

になります。

これは後のほうの年月日が10.25なので年が省略されたとは解釈されずに、10を西暦と解釈しようとします。ここで6.のルールのmktime()の仕様で、2桁の西暦は0-69の間の値は2000-2069に70-100は1970-2000にマップされ、従ってまず年は2010となります。

さらに次の25は月と解釈され25=12*2+1で2年繰り上がり年が2012年になり月は1月になります。そして5.のルールで日が空なので1日に設定され上記のような結果になります。

しかし感覚的には(個人的にはですが)
2005.04.06-2005.10.25

になって欲しい。おそらくほとんどの人がそう考えるだろうとおもいますが???(まあいろんな人がいるのでわかりませんが…)とにかく自分は↑のようになってくれたらとおもいました。

んで、ここには2つの問題があります。1つは10.25を自動的に頭から年月日と解釈しようとしていること、もう1つは仮に年月ではなく月日と解釈されたとしても、今度は年が空なので1が補われ結果的に2001.10.25のようになってしまうということです。

そこでまず年月日を頭から解釈していくという部分をなんとかしなければなりません。但しこれは範囲指定の後ろの方の年月日に関してということになります。前の方の年月日は頭から順に解釈していっても特に問題はないようにおもいます(今のところ)。

次に月日と解釈した後に年が空になりますが、ここで自動的に1を補ってしまうというのも考えものです。↑の例では2005、つまり前の方の年月日と同じ年に設定したいわけです。ただしこのルール?も適用したいのは後ろの方の年月日だけで、前の方の年月日は1を補う形で問題ないとおもわれます(今のところ×2)。

このあたりまで考えてみると、どうも前の方の年月日と後ろの方の年月日の処理は分けた方がよさそうな気がしてきます。さらに現状では範囲指定なしの、つまり単体の年月日指定というのも許可していますが、これは煩雑になりそうなので、たとえ年月日が一つしか入力されなくても結果として設定されるモノは範囲指定にした方がすっきりいくような気がします。そんなわけで、ここはやはりclassを作ることにしました。

基本設計?としては年月日オブジェクトというものを想定して、範囲指定なのでそれぞれdateFromクラス,dateToクラスのように範囲指定の[〜]の前をfrom、後をtoというように2つのインスタンスを作ります。さらに範囲指定オブジェクト?を作り年月日オブジェクトはそこから生成するようにします。またdate*クラスはfromとtoで微妙に挙動が違うことが予想されるので大元の抽象クラスを作りそこから継承するようにします。
class Period
{
private $from;
private $to;

function __construct($gdate)
{
$temp = explode('-', $gdate);

list($from, $to) = $temp;

$this->from = new dateFrom($from);
$this->to = new dateTo($to);
}

public function getDateRange()
{
$jd[] = $this->from->get();
$jd[] = $this->to->get();

sort($jd);

$from = date("Y.m.d", jd[0]);
$to = date("Y.m.d", jd[1]);

return $from."〜".$to;
}
}

abstract class Ymd
{
protected $year;
protected $month;
protected $day;

function __construct($date)
{
$temp = explode('.', $date);
}

function get()
{
return mktime(0, 0, 0, $this->getMonth(), $this->getDay(), $this->getYear());
}

abstract public function getYear();
abstract public function getMonth();
abstract public function getDay();
}

class dateFrom extends Ymd
{
function __construct($date)
{
parent::__construct($date);
}

function getYear()
{
if (!isset($this->year)) {return 1;}
return $this->year;
}

function getMonth()
{
if (!isset($this->month)) {return 1;}
return $this->month;
}

function getDay()
{
if (!isset($this->day)) {return 1;}
return $this->day;
}
}

class dateTo extends Ymd
{
function __construct($date)
{
parent::__construct($date);
}

function getYear()
{
if (!isset($this->year)) {return 1;}
return $this->year;
}

function getMonth()
{
if (!isset($this->month)) {return 1;}
return $this->month;
}

function getDay()
{
if (!isset($this->day)) {return 1;}
return $this->day;
}
}

だいたいの概観?は上記の通りです。呼び出し側からはPOSTで受取った値を適宜入力チェック&整形してperiodオブジェクトを生成し、getDateRange()を呼び出す、という流れです。

$gdate = trim(preg_replace("/[ \s]+/u", ' ', $_POST['gdate']));
$gdate = preg_replace("/[^0-9.-]/", "", $gdate);
$vDate = new Period($gdate);
$valid_date = $vDate->getDateRange();

[.]ドットと[-]ハイフン区切りの日付データを受取ったPeriodクラス(オブジェクト)は、ハイフンで範囲指定の前半の日付(from)と後半の日付(to)を分けて、Ymd(日付抽象クラス)を継承したそれぞれのクラスのインスタンスを生成します。

日付クラスではドットで年月日を分けてメンバに持つようにして、あとはgetメソッドで適宜返すようにします。上記では今のところ、設定されていない場合は1を返すように書いてあります。

PeriodクラスのgetDateRange()はそれぞれのdateクラスのget()を呼び出して、ソート&ドット区切りの日付に整形して返します。dateクラスのget()メソッドは親クラス(Ymd抽象クラス)で定義してあり、mktime()でUNIXタイムスタンプを返すようにしてあります。

とりあえずこんな感じで前回の動作を再現することができたので、次回からはこのクラスに手を入れて感覚的な入力との差異を解消していく方向で考えてみます。
タグ:PHP codeigniter
posted by ciallost at 20:14| Comment(0) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/76441831

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。