Month Input Fields
The month input field allows users to select a month and year. It provides a user-friendly month picker and submits data in YYYY-MM format.
Browser Display vs Submitted Format: Browsers display months according to user locale, but always submit in consistent YYYY-MM format that your PHP code can rely on.
Basic Usage
Use form_month() to create a month input field:
echo form_month('billing_month');
// Output: <input type="month" name="billing_month">
Demonstration
Below is the native HTML5 month input rendered by form_month():
Function Signature
function form_month(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 format |
null |
$attributes |
array |
Additional HTML attributes |
[] |
With a Default Value
echo form_month('report_month', '2025-12');
// Output: <input type="month" name="report_month" value="2025-12">
Setting Current Month as Default
$current_month = date('Y-m');
echo form_month('subscription_month', $current_month);
Automatic Zero-Padding: PHP's date('Y-m') automatically zero-pads the month, producing values like 2025-01 for January.
With Attributes
$attributes = [
'min' => '2025-01',
'max' => '2025-12',
'required' => true
];
echo form_month('target_month', '', $attributes);
Output:
<input type="month" name="target_month" value="" min="2025-01" max="2025-12" 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_month() function in action? We've made a working demo that you can download. Check out the complete Monthly Reports demo module example on GitHub:
https://github.com/trongate/Trongate-v2-Monthly-Reports-Module
This repository provides a ready-to-use example of building a monthly reporting system using the Trongate PHP framework (version 2). It includes pagination, form validation, secure admin access, native HTML5 month input handling, and clean separation of concerns.
Common Attributes
| Attribute |
Purpose |
Example |
min |
Earliest selectable month (YYYY-MM) |
['min' => '2025-01'] |
max |
Latest selectable month (YYYY-MM) |
['max' => '2025-12'] |
required |
Field must have a value |
['required' => true] |
readonly |
Prevent user editing |
['readonly' => true] |
disabled |
Disable the input |
['disabled' => true] |
How to Repopulate Forms After Validation Errors
Controller Example
public function create(): void {
// Fetch posted value (returns empty string if form hasn't been submitted)
$data['billing_month'] = post('billing_month', true);
$data['report_month'] = post('report_month', true);
// Pass data to view
$this->view('billing_form', $data);
}
View Example
<?php
echo form_open('billing/submit');
echo form_label('Billing Month');
echo form_month('billing_month', $billing_month);
echo form_label('Report Month');
echo form_month('report_month', $report_month);
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: Monthly Report Selection
Controller:
public function generate_report(): void {
$data['headline'] = 'Generate Monthly Report';
$data['report_month'] = post('report_month', true);
// Default to current month if not submitted
if ($data['report_month'] === '') {
$data['report_month'] = date('Y-m');
}
$data['form_location'] = BASE_URL . 'reports/submit';
$this->templates->admin($data);
}
public function submit(): void {
$this->validation->set_rules('report_month', 'report month', 'required|valid_month');
if ($this->validation->run() === true) {
$report_month = post('report_month', true);
// Generate report for selected month
$report_data = $this->generate_monthly_data($report_month);
set_flashdata('Report generated for ' . $report_month);
redirect('reports/view/' . $report_month);
} else {
$this->generate_report();
}
}
private function generate_monthly_data(string $month): array {
// Parse month: 2025-12 becomes year=2025, month=12
list($year, $month_num) = explode('-', $month);
// Query data for this month
$sql = "SELECT * FROM sales
WHERE YEAR(sale_date) = :year
AND MONTH(sale_date) = :month";
return $this->db->query_bind($sql, [
'year' => $year,
'month' => $month_num
], 'array');
}
View:
<h1><?= $headline ?></h1>
<?= validation_errors() ?>
<?php
echo form_open($form_location);
echo form_label('Report Month');
$attrs = [
'max' => date('Y-m'), // Can't generate reports for future months
'required' => true
];
echo form_month('report_month', $report_month, $attrs);
echo form_submit('submit', 'Generate Report');
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, 'subscriptions');
$data['customer_name'] = $record->customer_name;
$data['billing_month'] = $record->billing_month; // Already in YYYY-MM format
} else {
// New record OR validation error - use POST data
$data['customer_name'] = post('customer_name', true);
$data['billing_month'] = post('billing_month', true);
}
$data['form_location'] = BASE_URL . 'subscriptions/submit/' . $update_id;
$this->view('subscription_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
Month inputs submit in YYYY-MM format, which can be stored directly in a VARCHAR column:
Table Schema
CREATE TABLE subscriptions (
id INT PRIMARY KEY AUTO_INCREMENT,
customer_name VARCHAR(255),
billing_month VARCHAR(7), -- Stores YYYY-MM
amount DECIMAL(10, 2),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Inserting Data
public function submit(): void {
$this->validation->set_rules('billing_month', 'billing month', 'required|valid_month');
if ($this->validation->run() === true) {
$data['customer_name'] = post('customer_name', true);
$data['billing_month'] = post('billing_month', true); // e.g., "2025-12"
$this->db->insert($data, 'subscriptions');
redirect('subscriptions/manage');
} else {
$this->create();
}
}
Direct Storage: The YYYY-MM format from HTML5 month inputs can be stored directly in a VARCHAR(7) column. No conversion is needed, and the data remains human-readable.
Converting Month to Date Range
To query data for a specific month, you'll often need to convert to a date range:
// Input: 2025-12
$month = post('billing_month', true);
// Get first and last day of the month
$first_day = $month . '-01';
$last_day = date('Y-m-t', strtotime($first_day)); // 2025-12-31
// Use in queries
$sql = "SELECT * FROM transactions
WHERE transaction_date BETWEEN :start AND :end
ORDER BY transaction_date";
$data = $this->db->query_bind($sql, [
'start' => $first_day,
'end' => $last_day
], 'array');
Querying by Month
Using Date Functions
// Get all records for a specific month
$month = '2025-12';
list($year, $month_num) = explode('-', $month);
$sql = "SELECT * FROM sales
WHERE YEAR(sale_date) = :year
AND MONTH(sale_date) = :month
ORDER BY sale_date";
$results = $this->db->query_bind($sql, [
'year' => $year,
'month' => $month_num
], 'array');
Using Date Range
$month = '2025-12';
$first_day = $month . '-01';
$last_day = date('Y-m-t', strtotime($first_day));
$sql = "SELECT * FROM sales
WHERE sale_date BETWEEN :start AND :end
ORDER BY sale_date";
$results = $this->db->query_bind($sql, [
'start' => $first_day,
'end' => $last_day
], 'array');
Formatting Months for Display
When displaying months to users, you'll often want to format them in a more readable way:
// From database or form: 2025-12
// Simple PHP approach
$date = new DateTime('2025-12-01');
echo $date->format('F Y'); // December 2025
echo $date->format('M Y'); // Dec 2025
echo $date->format('m/Y'); // 12/2025
// Locale-aware formatting (requires intl extension)
$date = new DateTime('2025-12-01');
$formatter = new IntlDateFormatter(
'en_US',
IntlDateFormatter::LONG,
IntlDateFormatter::NONE,
null,
null,
'MMMM yyyy'
);
echo $formatter->format($date); // December 2025
// For French users
$formatter = new IntlDateFormatter(
'fr_FR',
IntlDateFormatter::LONG,
IntlDateFormatter::NONE,
null,
null,
'MMMM yyyy'
);
echo $formatter->format($date); // décembre 2025
Validation
Use the valid_month validation rule to ensure the submitted value is in the correct format:
$this->validation->set_rules('billing_month', 'billing month', 'required|valid_month');
This validates that the value matches YYYY-MM format and represents a valid month.
Client vs Server Validation: While browsers validate month formats client-side, always use server-side validation with valid_month. Client validation can be bypassed and doesn't protect your database.
See the Validating Date and Time Data chapter for complete validation rule documentation.
Restricting Month Selection
Current Year Only
$current_year = date('Y');
$attributes = [
'min' => $current_year . '-01',
'max' => $current_year . '-12',
'required' => true
];
echo form_month('report_month', $report_month, $attributes);
Last 12 Months
$max = date('Y-m');
$min = date('Y-m', strtotime('-12 months'));
$attributes = [
'min' => $min,
'max' => $max,
'required' => true
];
echo form_month('period', $period, $attributes);
Future Months (Budget Planning)
$current_date = new DateTime();
$min_month = $current_date->format('Y-m');
$max_date = clone $current_date;
$max_date->modify('+12 months');
$max_month = $max_date->format('Y-m');
$attributes = [
'min' => $min_month,
'max' => $max_month,
'required' => true
];
echo form_month('budget_month', $budget_month, $attributes);
Common Use Cases
- Billing and subscriptions - Monthly billing cycles and recurring payments
- Financial reports - Monthly revenue reports, P&L statements, expense tracking
- Planning and budgets - Monthly budgets, forecasts, and financial planning
- Performance reviews - Monthly KPIs, metrics, and performance tracking
- Content calendars - Editorial planning and content scheduling by month
- Inventory reports - Monthly stock levels and inventory analysis
- Sales tracking - Monthly sales targets, quotas, and achievement
- Employee records - Start months, anniversary months, review periods
Browser Rendering
Different browsers provide different month picker interfaces:
- Chrome/Edge: Dropdown with month and year selectors
- Firefox: Similar dropdown interface with clear navigation
- Safari: Native month picker wheel optimized for the platform
- Mobile: Native month picker optimized for touch input
Excellent Browser Support: Month inputs have excellent support across all modern browsers and mobile devices, providing a consistent user experience.
Important Notes
- Value format is always
YYYY-MM (e.g., 2025-12)
- Store as
VARCHAR(7) in database
- No conversion needed between form and database
- PHP's
date('Y-m') automatically zero-pads months
- Use for monthly periods, not specific dates within a month
- Browser displays in user's locale but always submits in YYYY-MM format
- Min/max attributes restrict the selectable month range
- Browser validates format automatically, but server validation is still required
Value Format: The value attribute must be in YYYY-MM format. Values like 12/2025 or 2025-December will not work and the field will appear empty.