Time Input Fields
The time input field allows users to select a time of day. It provides a user-friendly time picker and submits data in HH:MM or HH:MM:SS format.
Browser Display vs Submitted Format: Browsers display times according to user locale (12-hour with AM/PM in US, 24-hour in Europe), but always submit in consistent 24-hour format that your PHP code can rely on.
Basic Usage
Use form_time() to create a time input field:
echo form_time('start_time');
// Output: <input type="time" name="start_time">
Demonstration
Below is the native HTML5 time input rendered by form_time():
Function Signature
function form_time(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 HH:MM or HH:MM:SS format |
null |
$attributes |
array |
Additional HTML attributes |
[] |
With a Default Value
echo form_time('start_time', '09:00');
// Output: <input type="time" name="start_time" value="09:00">
With Attributes
$attributes = [
'min' => '09:00',
'max' => '17:00',
'required' => true
];
echo form_time('meeting_time', '', $attributes);
Output:
<input type="time" name="meeting_time" value="" min="09:00" max="17:00" 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_time() function in action? A fully working demo is just one click away! Check out the complete Room Bookings demo module example on GitHub:
https://github.com/trongate/Trongate-v2-Room-Bookings
This repository provides a ready-to-use example of building a room booking system using the Trongate PHP framework (version 2). It includes pagination, form validation, secure admin access, native HTML5 time input handling, custom time range validation, and clean separation of concerns.
Common Attributes
| Attribute |
Purpose |
Example |
min |
Earliest selectable time (HH:MM) |
['min' => '09:00'] |
max |
Latest selectable time (HH:MM) |
['max' => '17:00'] |
step |
Time interval in seconds |
['step' => 900] (15 min) |
required |
Field must have a value |
['required' => true] |
readonly |
Prevent user editing |
['readonly' => true] |
disabled |
Disable the input |
['disabled' => true] |
Understanding the Step Attribute
The step attribute controls the time interval in seconds:
| Step Value |
Interval |
Example |
60 |
1 minute |
09:00, 09:01, 09:02... |
300 |
5 minutes |
09:00, 09:05, 09:10... |
900 |
15 minutes |
09:00, 09:15, 09:30... |
1800 |
30 minutes |
09:00, 09:30, 10:00... |
3600 |
1 hour |
09:00, 10:00, 11:00... |
1 |
1 second |
09:00:00, 09:00:01... |
// 15-minute intervals
$attributes = ['step' => 900];
echo form_time('appointment_time', '', $attributes);
// 30-minute intervals
$attributes = ['step' => 1800];
echo form_time('meeting_time', '', $attributes);
Default Step: If no step is specified, browsers typically default to 1-minute intervals (step="60").
Setting Current Time as Default
$now = date('H:i');
echo form_time('clock_in_time', $now);
How to Repopulate Forms After Validation Errors
When a form fails validation, you need to redisplay it with the user's entered value:
Controller Example
public function create(): void {
// Fetch posted value (returns empty string if form hasn't been submitted)
$data['start_time'] = post('start_time', true);
$data['end_time'] = post('end_time', true);
// Pass data to view
$this->view('schedule_form', $data);
}
View Example
<?php
echo form_open('schedules/submit');
echo form_label('Start Time');
echo form_time('start_time', $start_time);
echo form_label('End Time');
echo form_time('end_time', $end_time);
echo form_submit('submit', 'Save');
echo form_close();
?>
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: Store Opening Hours
Controller:
public function create(): void {
$data['headline'] = 'Set Store Hours';
$data['opening_time'] = post('opening_time', true);
$data['closing_time'] = post('closing_time', true);
$data['form_location'] = BASE_URL . 'settings/submit';
$this->templates->admin($data);
}
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) {
$data['opening_time'] = post('opening_time', true);
$data['closing_time'] = post('closing_time', true);
$this->db->update(1, $data, 'store_settings');
set_flashdata('Store hours updated successfully');
redirect('settings/manage');
} 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; // Let required rule handle empty values
}
if (strtotime($closing_time) <= strtotime($opening_time)) {
return 'The {label} must be after the opening time.';
}
return true;
}
View:
<h1><?= $headline ?></h1>
<?= validation_errors() ?>
<?php
echo form_open($form_location);
echo form_label('Opening Time');
$opening_attrs = ['step' => 900, 'required' => true]; // 15-min intervals
echo form_time('opening_time', $opening_time, $opening_attrs);
echo form_label('Closing Time');
$closing_attrs = ['step' => 900, 'required' => true];
echo form_time('closing_time', $closing_time, $closing_attrs);
echo form_submit('submit', 'Save Hours');
echo form_close();
?>
Working with the Create/Update Pattern
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, 'appointments');
$data['appointment_time'] = $record->appointment_time; // Already in HH:MM:SS format
} else {
// New record OR validation error - use POST data
$data['appointment_time'] = post('appointment_time', true);
}
$data['form_location'] = BASE_URL . 'appointments/submit/' . $update_id;
$this->view('appointment_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
Time inputs submit in HH:MM or HH:MM:SS format, which maps to the MySQL TIME type:
Table Schema
CREATE TABLE store_hours (
id INT PRIMARY KEY AUTO_INCREMENT,
day_of_week VARCHAR(10),
opening_time TIME,
closing_time TIME
);
Inserting Data
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');
if ($this->validation->run() === true) {
$data['day_of_week'] = post('day_of_week', true);
$data['opening_time'] = post('opening_time', true); // e.g., "09:00"
$data['closing_time'] = post('closing_time', true); // e.g., "17:00"
$this->db->insert($data, 'store_hours');
redirect('settings/manage');
} else {
$this->create();
}
}
Direct Storage: HTML5 time inputs submit in formats that MySQL's TIME type accepts directly. If the user submits 09:00, MySQL automatically stores it as 09:00:00.
Formatting Times for Display
When displaying times to users, you may want different formats:
// From database: 14:30:00
// 12-hour format with AM/PM
$time = new DateTime('14:30:00');
echo $time->format('g:i A'); // 2:30 PM
echo $time->format('h:i A'); // 02:30 PM
// 24-hour format
echo $time->format('H:i'); // 14:30
// With seconds
echo $time->format('H:i:s'); // 14:30:00
// Locale-aware formatting
$formatter = new IntlDateFormatter(
'en_US',
IntlDateFormatter::NONE,
IntlDateFormatter::SHORT
);
$datetime = new DateTime('1970-01-01 14:30:00'); // Need a date for formatter
echo $formatter->format($datetime); // 2:30 PM
// For French users (24-hour format is standard)
$formatter = new IntlDateFormatter(
'fr_FR',
IntlDateFormatter::NONE,
IntlDateFormatter::SHORT
);
echo $formatter->format($datetime); // 14:30
Validation
Use the valid_time validation rule to ensure the submitted value is in the correct format:
$this->validation->set_rules('start_time', 'start time', 'required|valid_time');
This validates that the value matches HH:MM or HH:MM:SS format and represents a valid time.
Client vs Server Validation: While browsers validate time formats client-side, always use server-side validation with valid_time. Client validation can be bypassed and doesn't protect your database.
See the Validating Date and Time Data chapter for complete validation rule documentation.
Business Hours Example
Here's a complete example for managing daily business hours:
// Controller
public function edit_hours(): void {
$data['days'] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
foreach ($data['days'] as $day) {
$field_open = strtolower($day) . '_open';
$field_close = strtolower($day) . '_close';
$data[$field_open] = post($field_open, true);
$data[$field_close] = post($field_close, true);
}
$this->view('hours_form', $data);
}
public function submit_hours(): void {
$days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
foreach ($days as $day) {
$this->validation->set_rules($day . '_open', $day . ' opening time', 'valid_time');
$this->validation->set_rules($day . '_close', $day . ' closing time', 'valid_time');
}
if ($this->validation->run() === true) {
foreach ($days as $day) {
$data['day'] = $day;
$data['opening_time'] = post($day . '_open', true);
$data['closing_time'] = post($day . '_close', true);
// Update or insert logic here
$this->db->update_where('day', $day, $data, 'business_hours');
}
set_flashdata('Hours updated successfully');
redirect('settings/hours');
} else {
$this->edit_hours();
}
}
// View
<?php
echo form_open('settings/submit_hours');
foreach ($days as $day) {
$day_lower = strtolower($day);
$field_open = $day_lower . '_open';
$field_close = $day_lower . '_close';
echo '<div class="day-row">';
echo '<h3>' . $day . '</h3>';
echo form_label('Opens');
echo form_time($field_open, $$field_open, ['step' => 900]);
echo form_label('Closes');
echo form_time($field_close, $$field_close, ['step' => 900]);
echo '</div>';
}
echo form_submit('submit', 'Save All Hours');
echo form_close();
?>
Appointment Booking Example
Restricting time selection to business hours with 30-minute intervals:
// Controller
public function book(): void {
$data['appointment_time'] = post('appointment_time', true);
$this->view('booking_form', $data);
}
// View
<?php
$time_attrs = [
'min' => '09:00',
'max' => '17:00',
'step' => 1800, // 30-minute intervals
'required' => true
];
echo form_label('Appointment Time');
echo form_time('appointment_time', $appointment_time, $time_attrs);
?>
Common Patterns
Office Hours (15-minute intervals)
$attributes = [
'min' => '08:00',
'max' => '18:00',
'step' => 900, // 15 minutes
'required' => true
];
echo form_time('meeting_time', $meeting_time, $attributes);
Class Schedule (1-hour intervals)
$attributes = [
'min' => '08:00',
'max' => '20:00',
'step' => 3600, // 1 hour
'required' => true
];
echo form_time('class_time', $class_time, $attributes);
Precise Time Entry (with seconds)
$attributes = [
'step' => 1, // 1 second
'required' => true
];
echo form_time('exact_time', $exact_time, $attributes);
12-Hour vs 24-Hour Display
The browser automatically displays times in the user's preferred format:
- US users: Typically see 12-hour format with AM/PM
- European users: Typically see 24-hour format
- Data submitted: Always in 24-hour format (HH:MM)
Best of Both Worlds: Users see time in their familiar format, but your application always receives consistent 24-hour format data. No conversion logic needed!
Browser Rendering
Different browsers provide different time picker interfaces:
- Chrome/Edge: Dropdown with hour/minute selectors and AM/PM toggle (if 12-hour)
- Firefox: Separate hour and minute input boxes with increment/decrement buttons
- Safari: Native time picker wheel
- Mobile: Native time picker optimized for the device
Important Notes
- Times are always submitted in 24-hour format (
HH:MM or HH:MM:SS)
- The
step attribute controls granularity in seconds
- Default step is 60 seconds (1 minute) if not specified
- Use MySQL's
TIME type for time-only storage
- Min/max attributes restrict the selectable time range
- Browser validates format automatically, but server validation is still required
- Display format (12h vs 24h) is determined by user's system locale
Value Format: The value attribute must be in HH:MM or HH:MM:SS format (24-hour). Values like 2:30 PM or 14.30 will not work and the field will appear empty.
Combining Date and Time
For applications that need both date and time, you have two options:
Option 1: Separate Fields
echo form_label('Event Date');
echo form_date('event_date', $event_date);
echo form_label('Event Time');
echo form_time('event_time', $event_time);
// In submit handler, combine for database
$datetime = post('event_date', true) . ' ' . post('event_time', true) . ':00';
// Result: "2025-12-27 14:30:00"
$data['event_datetime'] = $datetime;
$this->db->insert($data, 'events');
Option 2: Single datetime-local Field
// See the "DateTime Input Fields" chapter
echo form_datetime_local('event_datetime', $event_datetime);
Each approach has its uses - separate fields give more control, while datetime-local is simpler for the user.