Pleasanterで申請フォームを作っていると、「開始日は過去日でも入力できるようにしたいが、終了日は開始日から1年以内に制限したい」という要件が出てきます。
利用期間や契約期間を申請するフォームでは、申請が遅れて開始日が申請日より前になることがあります。ここで開始日を申請日以降に限定すると、業務上は正しい申請なのに登録できません。
かといって終了日を無制限にすると、本来認めていない長期間の申請が登録されてしまいます。開始日は過去日を許可しつつ、終了日は開始日以降、かつ開始日から1年後まで。この形にします。
この日付制御では、画面上のJavaScriptだけに頼らず、申請処理の直前にサーバー側でも同じ条件を検証します。画面側では利用者が入力中に間違いへ気付けるようにし、サーバー側では不正な日付の登録を確実に止めます。
ここでは、開始日をDateA、終了日をDateBとした例で、要件整理、判定ロジック、エラーメッセージ、カレンダーの入力範囲、うるう年への対応、テストパターンまで書いていきます。実際に使うときは、項目名や申請処理の設定を利用中のPleasanter環境に合わせて置き換えてください。
今回の基本条件:開始日は過去日でも許可します。終了日は開始日以降とし、開始日から数えて1年後の日付を上限にします。
今回実装したい日付チェック
まず、実装したい日付チェックの条件をはっきりさせます。日付制御は、「1年以内」という言葉だけで実装を始めると、基準日や境界日の認識がずれます。
今回の要件では、開始日は申請日を基準に制限しません。申請日より前の日付でも、業務上必要なら入力できます。そのうえで、終了日は開始日と同じ日、または開始日より後の日付だけにします。
終了日の上限は開始日の1年後です。開始日が2026年7月24日なら、終了日は2027年7月24日まで。2027年7月25日は、開始日から1年後を超えるのでエラーです。
日付チェックの条件
- 開始日は過去日でも入力できます。
- 終了日は開始日と同じ日、または開始日より後にします。
- 終了日は開始日から1年後まで入力できます。
- 申請日は期間の上限計算に使用しません。
- 条件に違反した場合は申請処理を中止します。
申請日が2026年7月31日、開始日が2026年7月23日、終了日が2027年7月23日という申請は許可します。開始日は申請日より前ですが、終了日が開始日から1年以内に収まっているからです。
開始日が2026年7月24日、終了日が2027年7月25日ならエラーです。終了日が上限を1日超えています。申請日ではなく、入力された開始日を基準に終了日の妥当性を判断する、というのがここでの考え方です。
当初の要件で発生した問題
当初は、開始日と終了日の両方を「申請日から1年以内」に制限する案でした。ただ、この条件だと申請日より前の開始日を入力できません。
「申請日から1年以内」という条件
申請日を基準にすると、こういう条件になります。
当初の条件:開始日と終了日は、申請日以降かつ申請日から1年以内の日付とする。
未来の利用期間だけを申請する業務なら、これで分かりやすい。しかし、利用開始後に申請するケースや、申請処理が遅れたケースには対応できません。
過去の開始日を入力できなくなる
実際の利用開始日は2026年7月23日で、申請したのが2026年7月24日だったとします。
申請日:2026年7月24日
開始日:2026年7月23日
終了日:2027年7月23日
この申請は、開始日から終了日までが1年以内に収まっています。それでも「開始日は申請日以降」という条件があると、開始日が1日前というだけでエラーになります。
業務上、開始日が過去になることを認めるなら、申請日を基準とした制限は要件と合いません。開始日に対する申請日基準のチェックを外して、終了日の上限だけを開始日基準で判定する形に変えました。
要件を開始日基準へ変更する
最終的な条件はこうなります。
最終的な条件:終了日は、開始日と同じ日から開始日の1年後まで入力できます。開始日には、申請日を基準とした過去日制限を設けません。
申請日がいつであるかに関係なく、開始日と終了日の関係だけを見ます。これで、過去日から始まる申請を許可しながら、必要以上に長い期間が登録されるのを防げます。
日付チェックの条件を整理する
実装前に、正常になるケースとエラーになるケースを具体的に並べておきます。開始日と終了日が同じ場合、上限日と同じ場合、上限を1日超える場合。この境界値をはっきりさせておくと後で楽です。
開始日または終了日が未入力の場合
開始日と終了日を必須項目にするなら、どちらか一方でも未入力ならエラーです。日付の前後関係を確認する前に未入力チェックを入れると、利用者に分かりやすいエラーを返せます。
開始日:未入力
終了日:2027年7月24日
結果 :エラー
開始日がなければ終了日の上限を計算できません。終了日がなければ申請期間が確定しないので、期間を必須とする申請では処理を中止します。
終了日が開始日より前の場合
終了日は、開始日と同じ日か、それより後でなければなりません。
開始日:2026年7月24日
終了日:2026年7月23日
結果 :エラー
終了日が開始日の前日になっていて、期間として成立していません。比較処理では、終了日が開始日より小さい場合にエラーを返します。
終了日が開始日の1年後を超える場合
開始日が2026年7月24日なら、終了日の上限は2027年7月24日です。
開始日:2026年7月24日
終了日:2027年7月25日
結果 :エラー
上限日を1日でも超えたらエラーです。終了日が2027年7月24日なら、開始日の1年後と同じ日なので許可します。
開始日が過去日でも許可する
今回の要件では、開始日が申請日より前であること自体はエラーにしません。
申請日:2026年7月31日
開始日:2026年7月23日
終了日:2027年7月23日
結果 :許可
終了日が開始日以降で、開始日から1年以内なら、申請日は判定に影響しません。この条件を明確にしておくと、画面の説明文とサーバー側の判定が一致します。
申請ボタン押下時にチェックする理由
入力画面のJavaScriptを使えば、開始日や終了日を変更した時点で警告を出せます。ただ、画面側のチェックだけでは、不正な日付の登録を防ぎきれません。
入力中の警告だけでは不十分
JavaScriptは、利用者が通常の画面操作をする分には便利です。終了日が開始日より前になったときや、1年後を超えたときに、その場でメッセージを出せます。
一方で、JavaScriptが読み込まれない状態、ブラウザーの開発者ツールによる書き換え、APIからの登録、インポート処理では、画面上の制御を通りません。
終了日のカレンダーで選択できない日付をグレーアウトしても、日付を直接入力したり、コピー&ペーストしたりする操作までは防げません。
最終的にはサーバースクリプトで検証する
不正な申請を確実に止めるには、申請処理を実行する直前にサーバー側で開始日と終了日を確認します。条件に違反していれば、エラーメッセージを返して申請処理を中止します。
サーバー側では、少なくともこの順番で確認します。
- 開始日と終了日が入力されているか確認します。
- 日付として正しく解釈できるか確認します。
- 終了日が開始日より前でないか確認します。
- 開始日の1年後を計算します。
- 終了日が上限日を超えていないか確認します。
画面側とサーバー側を併用する
| 実装場所 | 主な役割 | 特徴 |
|---|---|---|
| 画面側JavaScript | 入力中に警告し、利用者の修正を促す | 分かりやすい一方、回避される可能性があります。 |
| サーバースクリプト | 申請前に最終検証し、不正登録を防ぐ | 画面以外の登録経路にも備えられます。 |
画面側は利用者の操作性、サーバー側はデータの正しさ。どちらか一方ではなく、同じ条件を両方へ実装します。
サーバースクリプトで使用する日付判定ロジック
ここからは、開始日をDateA、終了日をDateBとした日付判定の考え方です。Pleasanterで実際に項目値を取得する記述やエラーを返す記述は、利用しているバージョン、スクリプトの実行条件、項目構成に合わせて置き換えてください。
開始日と終了日を取得する
開始日:DateA
終了日:DateB
取得した値は、文字列のまま比較せず、日付として解釈してから比較します。文字列の形式が2026-07-24でも2026/07/24でも扱えるようにするなら、区切り文字を統一して年・月・日に分解します。
日付を安全に変換する関数
function parseDateValue(value) {
if (!value) return null;
const parts = String(value).trim().replace(///g, '-').split('-');
if (parts.length !== 3) return null;
const year = Number(parts[0]);
const month = Number(parts[1]);
const day = Number(parts[2]);
if (!Number.isInteger(year) ||
!Number.isInteger(month) ||
!Number.isInteger(day)) {
return null;
}
const date = new Date(year, month - 1, day);
if (date.getFullYear() !== year ||
date.getMonth() !== month - 1 ||
date.getDate() !== day) {
return null;
}
return date;
}
JavaScriptのDateは、存在しない日付を自動的に翌月へ繰り越すことがあります。存在しない月日を渡してもエラーにならないので、生成後の年・月・日が入力値と一致するかを確認しています。
1年後の日付を計算する関数
開始日の1年後を計算するとき、単純に365日分のミリ秒を加算してはいけません。うるう年をまたぐと、業務上の「1年後」と365日後が一致しなくなります。
function isLeapYear(year) {
return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);
}
function addOneYear(date, leapDayPolicy = 'feb28') {
const nextYear = date.getFullYear() + 1;
const month = date.getMonth();
const day = date.getDate();
if (month === 1 && day === 29 && !isLeapYear(nextYear)) {
return leapDayPolicy === 'mar1'
? new Date(nextYear, 2, 1)
: new Date(nextYear, 1, 28);
}
return new Date(nextYear, month, day);
}
この例では、開始日が2月29日で翌年に2月29日が存在しない場合、feb28を指定すると2月28日、mar1を指定すると3月1日を返します。どちらを採用するかは、実装前に業務ルールとして決めておきます。
期間を検証する関数
function validatePeriod(startValue, endValue) {
if (!startValue || !endValue) {
return '期間(開始日)と期間(終了日)を入力してください。';
}
const startDate = parseDateValue(startValue);
const endDate = parseDateValue(endValue);
if (!startDate || !endDate) {
return '期間には正しい日付を入力してください。';
}
if (endDate < startDate) {
return '期間(終了日)は、期間(開始日)以降の日付を入力してください。';
}
const maximumDate = addOneYear(startDate, 'feb28');
if (endDate > maximumDate) {
return '期間(終了日)は、期間(開始日)の1年後までの日付を入力してください。';
}
return '';
}
戻り値が空文字なら正常、メッセージが返ればエラーです。Pleasanterのサーバースクリプトでは、この関数へDateAとDateBの値を渡し、エラーが返った場合に申請を中止する処理へつなぎます。
const startValue = /* DateAの値を取得 */;
const endValue = /* DateBの値を取得 */;
const errorMessage = validatePeriod(startValue, endValue);
if (errorMessage) {
/* Pleasanterのエラー表示処理を実行 */
/* 申請処理を中止 */
}
注意:上記は日付判定部分を分かりやすく示した例です。項目値の取得方法、エラーの返却方法、申請処理を止める方法は、実際のPleasanter環境のスクリプト仕様に合わせて実装してください。
エラーメッセージを分かりやすくする
入力チェックでは、「日付が不正です」ではなく、どの条件に違反しているかを具体的に伝えます。利用者が修正方法を理解できるメッセージにしておくと、問い合わせや再申請が減ります。
終了日が開始日より前の場合
「開始日より後」と書くと、開始日と終了日が同じ1日だけの期間を許可できないように読めます。開始日と同じ日を許可するなら、「開始日以降」と書きます。
開始日の1年後を超える場合
「申請日の1年後」ではなく「開始日の1年後」と書きます。基準日を具体的に書いておくと、利用者がどの日付を修正すべきか迷いません。
画面上へ入力ガイドを表示する
※期間(終了日)は、期間(開始日)の1年後までの日付を入力してください。開始日は過去日でも入力できます。
エラーが出てから説明するだけでなく、終了日の項目付近に入力ガイドを出しておくと、利用者が申請前に条件を理解できます。開始日が過去日でもよいことを明記しておくと、問い合わせがかなり減ります。
JavaScriptで入力時に警告する
サーバー側の検証に加えて、画面側でも開始日と終了日の関係を確認します。利用者が終了日を入力した直後にエラーへ気付ければ、申請ボタンを押してから修正する手間がなくなります。
終了日の最小値と最大値を設定する
HTMLの日付入力欄では、minとmaxを設定すればカレンダー上の選択範囲を制御できます。開始日を終了日の最小値にし、開始日の1年後を最大値にします。
function formatDateValue(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
function updateEndDateLimit(startInput, endInput) {
const startDate = parseDateValue(startInput.value);
if (!startDate) {
endInput.removeAttribute('min');
endInput.removeAttribute('max');
return;
}
const maximumDate = addOneYear(startDate, 'feb28');
endInput.min = formatDateValue(startDate);
endInput.max = formatDateValue(maximumDate);
}
実際には、Pleasanterの画面上でDateAとDateBに対応する入力要素を取得して、この関数へ渡します。画面の要素名やセレクターは環境で違うので、実際のHTML構造を確認して設定してください。
開始日を変更した場合
開始日が変更されたら、終了日の最小値と最大値を再計算します。すでに入力されている終了日が新しい範囲から外れたら、警告を出すか、利用者へ再入力を促します。
function checkCurrentPeriod(startInput, endInput) {
const message = validatePeriod(startInput.value, endInput.value);
if (message) {
return message;
}
return '';
}
開始日を後の日付へ変更すると、これまで正常だった終了日が開始日より前になります。開始日を変更した結果、終了日が新しい上限を超えることもあります。だから開始日の変更時にも、両方の日付を再検証します。
終了日を変更した場合
終了日を変更したときは、開始日より前でないか、開始日の1年後を超えていないかを確認します。エラーがあれば、終了日の近くへメッセージを出すと分かりやすくなります。
ただし、画面上で警告したからといって、サーバー側の検証を省いてはいけません。画面側は入力支援、サーバー側は登録防止。役割を分けて実装します。
選択できない日付をグレーアウトする
終了日のカレンダーで、開始日より前の日付と開始日の1年後を超える日付を選択できないようにすると、利用者は入力可能な範囲を目で理解できます。
開始日を基準に選択範囲を更新する
開始日が2026年7月24日なら、終了日の選択範囲を2026年7月24日から2027年7月24日までに設定します。
| 設定項目 | 設定値 |
|---|---|
| 開始日 | 2026年7月24日 |
| 終了日の最小値 | 2026年7月24日 |
| 終了日の最大値 | 2027年7月24日 |
開始日を変更したら、終了日の範囲も即座に更新します。終了日の入力欄へ古い値が残っている場合は、新しい範囲に収まっているか再確認します。
グレーアウトだけに頼らない
カレンダーの選択範囲を制御しても、直接入力、コピー&ペースト、ブラウザー側の書き換え、API、インポートなど、別の経路から範囲外の日付が渡されます。
利用しているブラウザーや入力部品によっては、minやmaxの見え方も違います。グレーアウトは利用者向けの補助機能と考えて、申請時にはサーバー側で必ず同じ条件を検証します。
1年後の日付を判定するときの注意点
「開始日から1年以内」という条件では、1年をどう計算するかを決めておきます。365日を加算するだけでは、うるう年をまたぐときに業務上の想定とずれます。
365日後と1年後は同じとは限らない
開始日が2027年3月1日の場合、2028年はうるう年です。単純に365日を加える方法と、年だけを1つ進める方法では、結果が変わります。
業務要件が「同じ月日の翌年まで」なら、年を1つ加算して上限日を計算するほうが自然です。2026年7月24日の1年後は、2027年7月24日として扱います。
2月29日の扱いを決める
開始日が2月29日で、翌年がうるう年でない場合、翌年には同じ月日がありません。上限を2月28日にするか、3月1日にするかを、業務要件として決めておきます。
| 開始日 | 考え方 | 上限日 |
|---|---|---|
| 2024年2月29日 | 翌年2月の最終日まで | 2025年2月28日 |
| 2024年2月29日 | 翌年の同等期間を3月1日までとする | 2025年3月1日 |
どちらが正しいかは、技術だけでは決められません。利用期間、契約期間、資格の有効期限など、対象業務の規程に合わせて決めます。そして、画面側とサーバー側で同じルールを使います。
時刻を含めず日付単位で比較する
今回の条件は日付単位の比較です。時刻やタイムゾーンが混ざると、同じ日付でも内部的には前日や翌日として扱われることがあります。
年・月・日を取り出してローカル日付として生成し、時刻を含まない形で比較します。ISO形式の文字列をそのままnew Date()へ渡す方法は、実行環境によってタイムゾーンの影響を受けます。日付項目では年・月・日を明示的に分解するほうが安全です。
テストしておきたいパターン
日付制御を実装したら、通常ケースだけでなく境界値と異常値を確認します。開始日と終了日が同じ場合、上限日と同じ場合、上限を1日超える場合は必ず通します。
| 開始日 | 終了日 | 想定結果 | 確認内容 |
|---|---|---|---|
| 2026/7/24 | 2026/7/24 | OK | 開始日と終了日が同じ |
| 2026/7/24 | 2027/7/24 | OK | 上限日と同じ |
| 2026/7/24 | 2027/7/25 | エラー | 上限を1日超える |
| 2026/7/24 | 2026/7/23 | エラー | 終了日が開始日より前 |
| 2026/7/23 | 2027/7/23 | OK | 開始日が申請日より前でも許可 |
| 未入力 | 2027/7/24 | エラー | 開始日が未入力 |
| 2026/7/24 | 未入力 | エラー | 終了日が未入力 |
| 2024/2/29 | 2025/2/28 | 要件による | 2月29日の上限ルールを確認 |
画面上の手入力だけでなく、日付ピッカーからの選択、コピー&ペースト、レコードの編集、再申請など、実際の運用で使う操作も確認します。
できれば、申請ボタン以外の更新経路やインポート処理についても、同じ制限が要るかを整理しておきます。どの登録経路でも同じデータ品質を保ちたいなら、サーバー側の検証を共通化します。
実装後の確認ポイント
- 開始日が過去日でも申請できること
- 開始日と終了日が同じでも申請できること
- 開始日の1年後当日まで申請できること
- 上限を1日超えた場合に申請が止まること
- 終了日が開始日より前の場合に申請が止まること
- 画面側とサーバー側のメッセージが一致すること
- 2月29日の判定が業務ルールどおりであること
まとめ
Pleasanterで終了日を開始日から1年以内に制限するときは、申請日ではなく、入力された開始日を基準に上限を計算します。これで、開始日が申請日より前でも、業務上必要な申請を通せます。
基本条件は、開始日は過去日でも許可、終了日は開始日以降かつ開始日の1年後まで。開始日と終了日が同じ場合は許可、開始日の1年後と同じ日も許可。終了日が開始日より前の場合や、1年後を1日でも超える場合はエラーです。
実装は、画面側のJavaScriptと申請時のサーバースクリプトを併用します。画面側では終了日の最小値と最大値を設定して入力中に警告し、サーバー側では未入力、日付形式、前後関係、1年後の上限を再確認して不正な申請を止めます。
「1年後」を365日後として計算すると、うるう年をまたぐときに結果がずれます。年を1つ加算する考え方を基本にして、開始日が2月29日の場合は、翌年の上限を2月28日とするか3月1日とするかを業務要件として決めてください。
エラーメッセージでは、「申請日の1年後」ではなく「開始日の1年後まで」と書きます。画面上の入力ガイドにも同じ表現を使って、利用者が基準日を誤解しないようにします。
最後に、開始日と終了日が同じケース、上限日と同じケース、上限を1日超えるケース、開始日が過去になるケース、未入力、2月29日をテストします。日付制御は境界値で不具合が出やすいので、画面の操作性とサーバー側の登録防止を分けて確認しておくと安心です。


コメント