Date Input Fields
The date input field is the most commonly used date/time input type. It provides users with a calendar picker and always submits data in the standard YYYY-MM-DD format.
Basic Usage
Use form_date() to create a date input field:
echo form_date('due_date');
// Output: <input type="date" name="due_date">
Demonstration
Below is the native HTML5 date input rendered by form_date():
Function Signature
function form_date(string $name, ?string $value = null, array $attributes = []): string
Parameters
| Parameter |
Type |
Description |
Default |
$name |
string |
The name attribute for the input (required) |
N/A |
$value |
string|null |
The value in YYYY-MM-DD format |
null |
$attributes |
array |
Additional HTML attributes |
[] |
With a Default Value
echo form_date('due_date', '2025-12-31');
// Output: <input type="date" name="due_date" value="2025-12-31">
With Attributes
$attributes = [
'min' => '2025-01-01',
'max' => '2025-12-31',
'required' => true
];
echo form_date('due_date', null, $attributes);
Output:
<input type="date" name="due_date" min="2025-01-01" max="2025-12-31" required>
Boolean Attributes: Pass true for boolean attributes like required, readonly, or disabled. This generates clean HTML5 syntax: <input required>
Downloadable Demo: Want to see the form_date() function in action? We've got you covered! Check out the complete Friends module example on GitHub:
https://github.com/trongate/Trongate-v2-Friends-Module
This GitHub repository demonstrates a full-featured CRUD (Create, Read, Update, Delete) application for tracking friends' birthdays. Check it out and learn by exploring a full working example.
Common Attributes
| Attribute |
Purpose |
Example |
min |
Earliest selectable date (YYYY-MM-DD) |
['min' => '2025-01-01'] |
max |
Latest selectable date (YYYY-MM-DD) |
['max' => '2025-12-31'] |
required |
Field must have a value |
['required' => true] |
readonly |
Prevent user editing |
['readonly' => true] |
disabled |
Disable the input |
['disabled' => true] |
Setting Today as Default
$today = date('Y-m-d');
echo form_date('event_date', $today);
How to Repopulate Forms After Validation Errors
When a form fails validation, you need to redisplay it with the user's entered value. In Trongate, this follows the MVC pattern:
Controller Example
public function create(): void {
// Fetch posted value (returns empty string if form hasn't been submitted)
$data['due_date'] = post('due_date', true);
$data['start_date'] = post('start_date', true);
// Pass data to view
$this->view('task_form', $data);
}
View Example
<?php
echo form_open('tasks/submit');
echo form_label('Due Date');
echo form_date('due_date', $due_date);
echo form_label('Start Date');
echo form_date('start_date', $start_date);
echo form_submit('submit', 'Save');
echo form_close();
?>
The controller fetches the posted data using post(), then passes it to the view. The view receives these values as variables.
Form Repopulation Pattern: Always use post('field_name', true) in the controller to fetch submitted values. The true parameter trims whitespace, ensuring clean data.
Real-World Example: Task Due Date
Controller:
public function create(): void {
$data['headline'] = 'Create Task';
$data['due_date'] = post('due_date', true);
$data['form_location'] = BASE_URL . 'tasks/submit';
$this->templates->admin($data);
}
public function submit(): void {
$this->validation->set_rules('task_title', 'task title', 'required');
$this->validation->set_rules('due_date', 'due date', 'required|valid_date');
if ($this->validation->run() === true) {
$data['task_title'] = post('task_title', true);
$data['due_date'] = post('due_date', true);
$new_id = $this->db->insert($data, 'tasks');
set_flashdata('Task successfully created');
redirect('tasks/show/' . $new_id);
} else {
$this->create(); // Redisplay form with errors
}
}
View:
<h1><?= $headline ?></h1>
<?= validation_errors() ?>
<?php
echo form_open($form_location);
echo form_label('Task Title');
echo form_input('task_title', $task_title);
echo form_label('Due Date');
echo form_date('due_date', $due_date);
echo form_submit('submit', 'Create Task');
echo form_close();
?>
Setting Min and Max Dates Dynamically
Often you'll want to restrict the date range based on business logic:
// Controller
public function create(): void {
$data['booking_date'] = post('booking_date', true);
// Can't book in the past or more than 90 days ahead
$data['min_date'] = date('Y-m-d'); // Today
$data['max_date'] = date('Y-m-d', strtotime('+90 days'));
$this->view('booking_form', $data);
}
// View
$attributes = [
'min' => $min_date,
'max' => $max_date,
'required' => true
];
echo form_date('booking_date', $booking_date, $attributes);
Working with the Create/Update Pattern
Here's the standard pattern for date fields that work for both creating and updating records:
public function create(): void {
$update_id = segment(3, 'int');
if ($update_id > 0 && REQUEST_TYPE === 'GET') {
// Editing existing record - load from database
$record = $this->db->get_where($update_id, 'events');
$data['event_date'] = $record->event_date; // Already in YYYY-MM-DD format
} else {
// New record OR validation error - use POST data
$data['event_date'] = post('event_date', true);
}
$data['form_location'] = BASE_URL . 'events/submit/' . $update_id;
$this->view('event_form', $data);
}
Type-Casting Segments: Always use segment(3, 'int') when expecting numeric IDs. This prevents type-related bugs and improves code clarity.
Database Storage
Date inputs submit in YYYY-MM-DD format, which maps directly to the MySQL DATE type:
Table Schema
CREATE TABLE tasks (
id INT PRIMARY KEY AUTO_INCREMENT,
task_title VARCHAR(255),
due_date DATE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Inserting Data
public function submit(): void {
$this->validation->set_rules('due_date', 'due date', 'required|valid_date');
if ($this->validation->run() === true) {
$data['task_title'] = post('task_title', true);
$data['due_date'] = post('due_date', true); // Already in YYYY-MM-DD
$this->db->insert($data, 'tasks');
redirect('tasks/manage');
} else {
$this->create();
}
}
No Conversion Needed: The YYYY-MM-DD format from HTML5 date inputs matches MySQL's DATE type exactly. You can insert the value directly without any conversion.
Formatting Dates for Display
When displaying dates to users, you'll often want to format them in a more readable way:
// From database: 2025-12-27
// Simple PHP approach
$date = new DateTime('2025-12-27');
echo $date->format('F j, Y'); // December 27, 2025
echo $date->format('M d, Y'); // Dec 27, 2025
echo $date->format('d/m/Y'); // 27/12/2025
// Locale-aware formatting (requires intl extension)
$date = new DateTime('2025-12-27');
$formatter = new IntlDateFormatter(
'en_US',
IntlDateFormatter::LONG,
IntlDateFormatter::NONE
);
echo $formatter->format($date); // December 27, 2025
// For French users
$formatter = new IntlDateFormatter(
'fr_FR',
IntlDateFormatter::LONG,
IntlDateFormatter::NONE
);
echo $formatter->format($date); // 27 décembre 2025
Validation
Use the valid_date validation rule to ensure the submitted value is in the correct format:
$this->validation->set_rules('due_date', 'due date', 'required|valid_date');
This validates that the value:
- Matches the
YYYY-MM-DD format
- Represents a valid calendar date
- Properly handles leap years
Client vs Server Validation: While browsers validate date formats client-side, always use server-side validation with valid_date. Client validation can be bypassed and doesn't protect your database.
Setting a Date Range Example
Here's a complete example of a form that requires a start date and end date, where the end date must be after the start date:
// Controller
public function create(): void {
$data['start_date'] = post('start_date', true);
$data['end_date'] = post('end_date', true);
$data['today'] = date('Y-m-d');
$this->view('project_form', $data);
}
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) {
// Save data
$data['start_date'] = post('start_date', true);
$data['end_date'] = post('end_date', true);
$this->db->insert($data, 'projects');
set_flashdata('Project created successfully');
redirect('projects/manage');
} 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;
}
// View
<?php
echo form_open('projects/submit');
echo form_label('Start Date');
$start_attrs = ['min' => $today, 'required' => true];
echo form_date('start_date', $start_date, $start_attrs);
echo form_label('End Date');
$end_attrs = ['min' => $today, 'required' => true];
echo form_date('end_date', $end_date, $end_attrs);
echo form_submit('submit', 'Create Project');
echo form_close();
?>