SQLPathLab
Practical SQL Guide · MySQL 8.0

Date ranges, month ends, and time boundaries

Use half-open ranges for days and months to avoid missing fractional seconds and preserve index opportunities.

Problem and reasoning

Do not end a monthly filter at 23:59:59. A column may store milliseconds or microseconds and may be affected by time-zone conversion. A more stable range is created_at >= the month start and created_at < the next month start.

Wrapping the date column in DATE_FORMAT, DATE, or YEAR inside WHERE can prevent ordinary index use. A range predicate applied directly to the source column is usually clearer and easier to optimize.

Applications must also define storage and display time zones. A daily report across zones needs an explicit user, business, or UTC boundary.

Examples

Sample table

Table:orders
orders
idcustomer_idproduct_idnamedepartmentamountstatuscreated_atdiscount
1 101 201 Alice Sales 1200 paid 2026-01-03 0
2 102 202 Bob IT 900 pending 2026-01-08 50
3 101 201 Cindy Sales 1500 paid 2026-02-02 100
4 104 203 David HR 700 cancelled 2026-02-15 0

Before / Not recommended

SELECT * FROM orders WHERE DATE_FORMAT(created_at, '%Y-%m') = '2026-02' ORDER BY created_at;

Before result

result
idcustomer_idproduct_idnamedepartmentamountstatuscreated_atdiscount
3 101 201 Cindy Sales 1500 paid 2026-02-02 100
4 104 203 David HR 700 cancelled 2026-02-15 0

After / Recommended

SELECT * FROM orders WHERE created_at >= '2026-02-01' AND created_at < '2026-03-01' ORDER BY created_at;

After result

result
idcustomer_idproduct_idnamedepartmentamountstatuscreated_atdiscount
3 101 201 Cindy Sales 1500 paid 2026-02-02 100
4 104 203 David HR 700 cancelled 2026-02-15 0

Why it is better:The half-open range covers every February timestamp precision and applies predicates directly to created_at.

Was this explanation clear?