Trongate PHP Framework Docs
Introduction
Quick Start
Basic Concepts
Understanding Routing
Intercepting Requests
Module Fundamentals
Database Operations
Templates
Helpers
Form Handling
Form Validation
Working With Files
Image Manipulation
Working With Dates & Times
Language Control
Security
Tips And Best Practices

Validating Date and Time Data

Trongate provides built-in validation rules for all HTML5 date and time input formats. These rules ensure data is in the correct format and represents valid dates and times.

Built-In Validation Rules

Rule Format Validated Example Use With
valid_date YYYY-MM-DD 2025-12-27 form_date()
valid_time HH:MM or HH:MM:SS 14:30 or 14:30:00 form_time()
valid_datetime_local YYYY-MM-DDTHH:MM 2025-12-27T14:30 form_datetime_local()
valid_month YYYY-MM 2025-12 form_month()
valid_week YYYY-W## 2025-W52 form_week()

Basic Usage

Validating a Date

PHP
$this->validation->set_rules('due_date', 'due date', 'required|valid_date');

Validating a Time

PHP
$this->validation->set_rules('start_time', 'start time', 'required|valid_time');

Validating a DateTime

PHP
$this->validation->set_rules('appointment', 'appointment', 'required|valid_datetime_local');

Validating a Month

PHP
$this->validation->set_rules('billing_month', 'billing month', 'required|valid_month');

Validating a Week

PHP
$this->validation->set_rules('work_week', 'work week', 'required|valid_week');

What Each Rule Validates

valid_date

Validates that the value:

  • Matches the YYYY-MM-DD format exactly
  • Represents a valid calendar date
  • Properly handles leap years (Feb 29 only in leap years)
  • Rejects invalid dates like 2025-02-30 or 2025-13-01
PHP
// Valid examples
'2025-12-27'  // ✓ Valid
'2024-02-29'  // ✓ Valid (2024 is a leap year)

// Invalid examples
'2025-13-01'  // ✗ Invalid (month 13 doesn't exist)
'2025-02-29'  // ✗ Invalid (2025 is not a leap year)
'12/27/2025'  // ✗ Invalid format
'2025-12-32'  // ✗ Invalid (December only has 31 days)

valid_time

Validates that the value:

  • Matches HH:MM or HH:MM:SS format
  • Hours are between 00-23
  • Minutes are between 00-59
  • Seconds (if provided) are between 00-59
PHP
// Valid examples
'14:30'       // ✓ Valid
'09:00'       // ✓ Valid
'23:59'       // ✓ Valid
'00:00:00'    // ✓ Valid
'14:30:45'    // ✓ Valid

// Invalid examples
'24:00'       // ✗ Invalid (hour 24 doesn't exist)
'14:60'       // ✗ Invalid (minute 60 doesn't exist)
'2:30 PM'     // ✗ Invalid format
'14.30'       // ✗ Invalid format

valid_datetime_local

Validates that the value:

  • Matches YYYY-MM-DDTHH:MM format exactly
  • Contains the literal "T" separator
  • Date portion is valid (same rules as valid_date)
  • Time portion is valid (same rules as valid_time)
PHP
// Valid examples
'2025-12-27T14:30'     // ✓ Valid
'2024-02-29T00:00'     // ✓ Valid (leap year)

// Invalid examples
'2025-12-27 14:30'     // ✗ Invalid (space instead of T)
'2025-12-27T24:00'     // ✗ Invalid (hour 24)
'2025-13-01T14:30'     // ✗ Invalid (month 13)
'2025-12-27T14:30:00'  // ✗ Invalid (includes seconds)

valid_month

Validates that the value:

  • Matches YYYY-MM format exactly
  • Year is a 4-digit number
  • Month is between 01-12
PHP
// Valid examples
'2025-12'     // ✓ Valid
'2025-01'     // ✓ Valid

// Invalid examples
'2025-13'     // ✗ Invalid (month 13)
'2025-00'     // ✗ Invalid (month 00)
'25-12'       // ✗ Invalid (2-digit year)
'2025/12'     // ✗ Invalid format

valid_week

Validates that the value:

  • Matches YYYY-W## format exactly
  • Contains the literal "W" prefix for week number
  • Year is a 4-digit number
  • Week number is between 01-53
PHP
// Valid examples
'2025-W52'    // ✓ Valid
'2025-W01'    // ✓ Valid

// Invalid examples
'2025-W00'    // ✗ Invalid (week 00)
'2025-W54'    // ✗ Invalid (week 54)
'2025-52'     // ✗ Invalid (missing W)
'2025W52'     // ✗ Invalid (missing hyphen)

Combining with Other Rules

Date and time validation rules work seamlessly with other validation rules:

PHP
// Required date
$this->validation->set_rules('due_date', 'due date', 'required|valid_date');

// Optional date (validates format only if provided)
$this->validation->set_rules('completion_date', 'completion date', 'valid_date');

// Required time with custom callback
$this->validation->set_rules('meeting_time', 'meeting time', 'required|valid_time|callback_during_business_hours');

// Optional month
$this->validation->set_rules('report_month', 'report month', 'valid_month');

Optional Fields: If a date/time field is optional (not required), the validation rule will only check the format when a value is provided. An empty value will pass validation.

Complete Form Example

PHP
public function submit(): void {
    // Set validation rules
    $this->validation->set_rules('event_title', 'event title', 'required|min_length[3]');
    $this->validation->set_rules('event_date', 'event date', 'required|valid_date');
    $this->validation->set_rules('start_time', 'start time', 'required|valid_time');
    $this->validation->set_rules('end_time', 'end time', 'required|valid_time|callback_end_after_start');
    
    if ($this->validation->run() === true) {
        $data['event_title'] = post('event_title', true);
        $data['event_date'] = post('event_date', true);
        $data['start_time'] = post('start_time', true);
        $data['end_time'] = post('end_time', true);
        
        $this->db->insert($data, 'events');
        set_flashdata('Event created successfully');
        redirect('events/manage');
    } else {
        $this->create();
    }
}

Custom Validation with Callbacks

For business logic validation beyond format checking, use custom callback methods:

Date Range Validation

PHP
public function submit(): void {
    $this->validation->set_rules('start_date', 'start date', 'required|valid_date');
    $this->validation->set_rules('end_date', 'end date', 'required|valid_date|callback_end_after_start');
    
    if ($this->validation->run() === true) {
        // Process form
    } else {
        $this->create();
    }
}

public function callback_end_after_start($end_date): string|bool {
    $start_date = post('start_date', true);
    
    if ($start_date === '' || $end_date === '') {
        return true; // Let required rule handle empty values
    }
    
    if (strtotime($end_date) <= strtotime($start_date)) {
        return 'The {label} must be after the start date.';
    }
    
    return true;
}

Time Range Validation

PHP
public function submit(): void {
    $this->validation->set_rules('opening_time', 'opening time', 'required|valid_time');
    $this->validation->set_rules('closing_time', 'closing time', 'required|valid_time|callback_closing_after_opening');
    
    if ($this->validation->run() === true) {
        // Process form
    } else {
        $this->create();
    }
}

public function callback_closing_after_opening($closing_time): string|bool {
    $opening_time = post('opening_time', true);
    
    if ($opening_time === '' || $closing_time === '') {
        return true;
    }
    
    if (strtotime($closing_time) <= strtotime($opening_time)) {
        return 'The {label} must be after the opening time.';
    }
    
    return true;
}

Future Date Only

PHP
public function submit(): void {
    $this->validation->set_rules('event_date', 'event date', 'required|valid_date|callback_must_be_future');
    
    if ($this->validation->run() === true) {
        // Process form
    } else {
        $this->create();
    }
}

public function callback_must_be_future($date): string|bool {
    if ($date === '') {
        return true;
    }
    
    $today = date('Y-m-d');
    
    if (strtotime($date) < strtotime($today)) {
        return 'The {label} must be today or in the future.';
    }
    
    return true;
}

Past Date Only

PHP
public function callback_must_be_past($date): string|bool {
    if ($date === '') {
        return true;
    }
    
    $today = date('Y-m-d');
    
    if (strtotime($date) > strtotime($today)) {
        return 'The {label} cannot be in the future.';
    }
    
    return true;
}

Business Hours Validation

PHP
public function callback_during_business_hours($time): string|bool {
    if ($time === '') {
        return true;
    }
    
    $hour = (int) substr($time, 0, 2);
    
    if ($hour < 9 || $hour >= 17) {
        return 'The {label} must be between 9:00 AM and 5:00 PM.';
    }
    
    return true;
}

Weekday Only Validation

PHP
public function callback_must_be_weekday($date): string|bool {
    if ($date === '') {
        return true;
    }
    
    $day_of_week = (int) date('N', strtotime($date)); // 1=Monday, 7=Sunday
    
    if ($day_of_week >= 6) {
        return 'The {label} must be a weekday (Monday-Friday).';
    }
    
    return true;
}

Date Within Range

PHP
public function callback_within_90_days($date): string|bool {
    if ($date === '') {
        return true;
    }
    
    $today = strtotime(date('Y-m-d'));
    $selected = strtotime($date);
    $max_date = strtotime('+90 days', $today);
    
    if ($selected < $today || $selected > $max_date) {
        return 'The {label} must be within the next 90 days.';
    }
    
    return true;
}

Minimum Age Validation

PHP
public function callback_minimum_age_18($birth_date): string|bool {
    if ($birth_date === '') {
        return true;
    }
    
    $today = new DateTime();
    $dob = new DateTime($birth_date);
    $age = $today->diff($dob)->y;
    
    if ($age < 18) {
        return 'You must be at least 18 years old.';
    }
    
    return true;
}

DateTime Range Validation

PHP
public function callback_event_end_after_start($event_end): string|bool {
    $event_start = post('event_start', true);
    
    if ($event_start === '' || $event_end === '') {
        return true;
    }
    
    // Convert to timestamps for comparison
    $start = strtotime(str_replace('T', ' ', $event_start));
    $end = strtotime(str_replace('T', ' ', $event_end));
    
    if ($end <= $start) {
        return 'The {label} must be after the event start time.';
    }
    
    // Check if event is too long (e.g., max 8 hours)
    $duration_hours = ($end - $start) / 3600;
    if ($duration_hours > 8) {
        return 'Event duration cannot exceed 8 hours.';
    }
    
    return true;
}

Appointment Slot Validation

PHP
public function callback_valid_appointment_time($datetime): string|bool {
    if ($datetime === '') {
        return true;
    }
    
    // Convert to DateTime object
    $dt = new DateTime(str_replace('T', ' ', $datetime));
    
    // Check if weekend
    $day_of_week = (int) $dt->format('N');
    if ($day_of_week >= 6) {
        return 'Appointments are only available Monday through Friday.';
    }
    
    // Check if during business hours
    $hour = (int) $dt->format('H');
    if ($hour < 9 || $hour >= 17) {
        return 'Appointments must be between 9:00 AM and 5:00 PM.';
    }
    
    // Check if on the hour or half-hour
    $minute = (int) $dt->format('i');
    if ($minute !== 0 && $minute !== 30) {
        return 'Appointments must be scheduled on the hour or half-hour.';
    }
    
    // Check if slot is available
    $sql = "SELECT COUNT(*) as count FROM appointments 
            WHERE scheduled_at = :datetime";
    $result = $this->db->query_bind($sql, ['datetime' => $dt->format('Y-m-d H:i:s')], 'object');
    
    if ($result->count > 0) {
        return 'This appointment slot is already booked.';
    }
    
    return true;
}

Validation Error Messages

Default error messages for built-in rules:

Rule Default Error Message
valid_date "The [field label] must be a valid date in YYYY-MM-DD format."
valid_time "The [field label] must be a valid time in HH:MM or HH:MM:SS format."
valid_datetime_local "The [field label] must be a valid datetime in YYYY-MM-DDTHH:MM format."
valid_month "The [field label] must be a valid month in YYYY-MM format."
valid_week "The [field label] must be a valid week in YYYY-W## format."

Real-World Validation Scenarios

Booking System

PHP
public function submit_booking(): void {
    $this->validation->set_rules('booking_date', 'booking date', 'required|valid_date|callback_valid_booking_date');
    $this->validation->set_rules('time_slot', 'time slot', 'required|valid_time|callback_available_time_slot');
    
    if ($this->validation->run() === true) {
        // Process booking
    } else {
        $this->booking_form();
    }
}

public function callback_valid_booking_date($date): string|bool {
    // Must be at least 24 hours in advance
    $tomorrow = date('Y-m-d', strtotime('+1 day'));
    if (strtotime($date) < strtotime($tomorrow)) {
        return 'Bookings must be made at least 24 hours in advance.';
    }
    
    // Cannot be more than 90 days in advance
    $max_date = date('Y-m-d', strtotime('+90 days'));
    if (strtotime($date) > strtotime($max_date)) {
        return 'Bookings cannot be made more than 90 days in advance.';
    }
    
    return true;
}

public function callback_available_time_slot($time): string|bool {
    $date = post('booking_date', true);
    
    if ($date === '' || $time === '') {
        return true;
    }
    
    // Check if slot is available
    $sql = "SELECT COUNT(*) as count FROM bookings 
            WHERE booking_date = :date AND time_slot = :time";
    $result = $this->db->query_bind($sql, [
        'date' => $date,
        'time' => $time
    ], 'object');
    
    if ($result->count > 0) {
        return 'This time slot is no longer available.';
    }
    
    return true;
}

Project Timeline

PHP
public function submit(): void {
    $this->validation->set_rules('project_start', 'project start date', 'required|valid_date|callback_reasonable_start_date');
    $this->validation->set_rules('project_end', 'project end date', 'required|valid_date|callback_reasonable_timeline');
    
    if ($this->validation->run() === true) {
        // Process project
    } else {
        $this->create();
    }
}

public function callback_reasonable_start_date($start_date): string|bool {
    $today = date('Y-m-d');
    $max_future = date('Y-m-d', strtotime('+1 year'));
    
    if (strtotime($start_date) < strtotime($today)) {
        return 'The {label} cannot be in the past.';
    }
    
    if (strtotime($start_date) > strtotime($max_future)) {
        return 'The {label} cannot be more than 1 year in the future.';
    }
    
    return true;
}

public function callback_reasonable_timeline($end_date): string|bool {
    $start_date = post('project_start', true);
    
    if ($start_date === '' || $end_date === '') {
        return true;
    }
    
    $start = strtotime($start_date);
    $end = strtotime($end_date);
    
    // End must be after start
    if ($end <= $start) {
        return 'The {label} must be after the project start date.';
    }
    
    // Project must be at least 1 week long
    $min_end = strtotime('+1 week', $start);
    if ($end < $min_end) {
        return 'Project must be at least 1 week long.';
    }
    
    // Project cannot exceed 2 years
    $max_end = strtotime('+2 years', $start);
    if ($end > $max_end) {
        return 'Project duration cannot exceed 2 years.';
    }
    
    return true;
}

Combining Multiple Validations

PHP
public function submit_event(): void {
    // Event details
    $this->validation->set_rules('event_title', 'event title', 'required|min_length[5]');
    
    // Date and time validations
    $this->validation->set_rules('event_date', 'event date', 'required|valid_date|callback_must_be_future|callback_must_be_weekday');
    $this->validation->set_rules('start_time', 'start time', 'required|valid_time|callback_during_business_hours');
    $this->validation->set_rules('end_time', 'end time', 'required|valid_time|callback_end_after_start|callback_reasonable_duration');
    
    // Registration deadline
    $this->validation->set_rules('registration_deadline', 'registration deadline', 'required|valid_datetime_local|callback_before_event');
    
    if ($this->validation->run() === true) {
        // Process event
    } else {
        $this->create();
    }
}

Important Notes

  • Built-in validation rules check format AND validity (e.g., valid_date rejects Feb 30)
  • Use callbacks for business logic validation (ranges, availability, etc.)
  • Empty values pass validation unless required is specified
  • Always return true or an error string from callbacks
  • Use {label} in error messages for automatic label substitution
  • Callbacks should handle empty values gracefully (let required rule handle them)
  • Browser validation is a convenience, not security - always validate server-side
  • Consider timezone implications when validating datetime values

Best Practice: Use built-in rules for format validation and custom callbacks for business logic. This separation keeps your code clean and maintainable.

We're continually improving the Trongate documentation. If anything is incorrect, unclear, incomplete, or could be better, we'd genuinely appreciate your input.

Share your thoughts in the Documentation Feedback.

Leave Feedback About This Page