2.4 · Dates, regular expressions & emails

Level 2 · PHP & the Web

2.4Dates, regular expressions & emails

Goal: Handle dates with DateTime (timezone, storage format), validate with filter_var (rather than homemade regex) and send emails via dedicated libraries — relying on proven tools.
Estimated time: 11 min · self-paced reading · If you only have 5 min: read “The essentials in 30s” + the diagram.

The essentials in 30 seconds

  • Dates with DateTime: create, format for display (->format), compute gaps (->diff) — timezone configured, otherwise wrong times.
  • Store ISO, display local: store YYYY-MM-DD (sortable, unambiguous) and format only for display — never a localized format in the database.
  • filter_var first, regex if needed, library for emails: email and URL via filter_var, business patterns in tested regex, sending via PHPMailer or Symfony Mailer — never mail() in serious production.

Running mini-case — Riad dates, checks and writes to members

Final act of the project: Riad timestamps registrations (2.1) and photos (2.3) with DateTime — after losing an afternoon to shifted times because the timezone was not configured. He checks member numbers (precise internal format) with a tested regex, but validates emails with filter_var rather than a homemade regex. And for the newsletter, he drops mail() for a dedicated library that handles authentication and deliverability. The level 2 project is delivered: forms, sessions, files, dates.

Epilogue to thread 2.1-2.3: Riad's site handles dates, patterns and emails with the right tools.

1. Dates: DateTime, timezone and storage

Dates are everywhere (creation, events, durations) and modern PHP handles them OBJECT-ORIENTED with DateTime — and DateTimeImmutable, preferable because it cannot be modified: create (new DateTime('2026-07-25'), new DateTime() for now), FORMAT for display (->format('d/m/Y')), compute DIFFERENCES (->diff() between two dates, which handles leap years and uneven months), add/subtract durations. The old procedural functions (date(), time(), strtotime()) exist — you will meet them. Two cautions: the timezone (to configure explicitly, otherwise all times are shifted — Riad's trap #1; for international use, store in UTC and convert for display) and the storage format: store in the standard ISO format (YYYY-MM-DD, also the database one — sortable, unambiguous) and FORMAT only for display — never the reverse.

2. Regex: powerful, but not for everything

Regular expressions (regex) describe PATTERNS in text: checking a format (postal code, business reference), extracting parts, replacing by pattern. In PHP: preg_match() (does it match?), preg_match_all(), preg_replace(), preg_split(). They are POWERFUL but notoriously hard (cryptic syntax, easy to write badly — too strict, too lax, sometimes slow). Hence the rule: for COMMON validations (email, URL, IP, integer), do NOT write your regex — use filter_var() (FILTER_VALIDATE_EMAIL, FILTER_VALIDATE_URL), proven and reliable (correctly validating an email with regex is surprisingly hard). Reserve regex for SPECIFIC patterns not covered by filters — like Riad's member number — testing them carefully and commenting them. For SIMPLE handling (search, replace, split), prefer string functions, more readable and faster.

3. Emails: dedicated library, locked headers

The native mail() function is BASIC and unreliable (deliverability, spam, server configuration): to send seriously, use DEDICATED LIBRARIES (PHPMailer, Symfony Mailer) which handle SMTP authentication, attachments, HTML and deliverability — or third-party sending services. SECURITY point: never insert unchecked user data into email HEADERS (header injection flaw). So Riad's newsletter goes out via a library, with headers built from controlled values. Cross-cutting principle of this section: rely on PROVEN TOOLS (DateTime, filter_var, email libraries) rather than hand-crafting everything.

Common misinterpretation: “A homemade regex for emails is more precise than filter_var.” No, it is the reverse: the specification of valid emails is formidably complex, and homemade regexes are almost always either too strict (they reject valid emails) or too lax (they accept anything) — not to mention untested edge cases. filter_var with FILTER_VALIDATE_EMAIL applies proven, maintained validation. Riad's rule: common case covered by a filter = filter_var, no discussion; regex reserved for patterns specific to his need, tested and commented.
Dates, patterns, emails The right proven tools DateTimeCreate, format, ->diff; timezone + ISO+Regex vs filter_varfilter_var for email/URL; regex = specific patterns+EmailsDedicated library (not mail()) Rely on proven tools rather than doing everything by hand.
The right proven tools
Text version of the diagram (SEO / screen readers / translation): 1) Riad handles DATES with DateTime: creation, formatting for display, gaps with ->diff — timezone configured, storage in ISO, never a localized format in the database. 2) For PATTERNS, he validates emails and URLs with filter_var (proven) and reserves tested regex for specific business formats. 3) He sends his EMAILS via a dedicated library (authentication, deliverability), with no unchecked data in the headers. Lesson: the right proven tool for each need, no homemade tinkering.

Did you know?

Riad lost an afternoon to shifted times: the server timezone was not the Constantine one.
True: an unconfigured timezone skews every timestamp — the #1 date trap.

One line of timezone is worth an afternoon of debugging.

🔎 Concrete example — 🔎 Concrete example — Riad's registration, dated and validated

DateTime + filter_var, as with Riad:

$d = new DateTime('2026-07-25');
echo $d->format('d/m/Y');
// storage: ISO format 2026-07-25
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
  echo htmlspecialchars($email, ENT_QUOTES, 'UTF-8');
}

Reading: date creation and formatting, ISO storage, reliable validation with no homemade regex.

Lesson: configure the timezone; store ISO, format for display.

Section vocabulary

Dates (DateTime)
Modern PHP handles dates with DateTime/DateTimeImmutable: create, FORMAT for display (->format), compute differences (->diff), add/subtract.

Simple example: Riad timestamps each registration with new DateTime().

What it is not: duration calculations by hand on timestamps.

Reflex: handling a date = DateTime.

Timezone & storage format
Configure the TIMEZONE (otherwise wrong times); STORE in the standard ISO format (YYYY-MM-DD) and FORMAT only for display.

Simple example: Riad stores 2026-07-25 and displays 25/07/2026.

What it is not: a localized date stored as-is in the database.

Reflex: store ISO, display local.

Regular expressions (regex)
A mini-language describing patterns in text; preg_match (does it match?), preg_replace (replace) — powerful but hard.

Simple example: Riad checks the member number with a tested regex.

What it is not: a homemade regex for validating emails.

Reflex: specific business pattern = tested and commented regex.

filter_var for validating
For common validations (email, URL…), prefer filter_var (FILTER_VALIDATE_EMAIL…) to a homemade regex — more reliable.

Simple example: Each member email goes through FILTER_VALIDATE_EMAIL.

What it is not: a regex copied without testing for emails.

Reflex: common case = proven filter.

Sending emails
The native mail() function is basic and unreliable; use dedicated libraries (PHPMailer, Symfony Mailer) — never unchecked data in headers.

Simple example: Riad's newsletter goes out via a library with authenticated SMTP.

What it is not: hand-crafted mail() for bulk sending.

Reflex: serious email = dedicated library.

In practice — In practice — Dates, regex and emails

  1. Handle dates with DateTime: create, format for display (->format('d/m/Y')), compute a difference (->diff); configure the timezone.
  2. Remember: store dates in the standard (ISO) format and FORMAT only for display (never store a localized format).
  3. Validate with filter_var (email, URL) rather than a homemade regex; reserve regex (preg_match) for specific patterns.
  4. To send email seriously, use a dedicated library (PHPMailer / Symfony Mailer) rather than mail(); never inject unchecked data into headers.
You handle dates with DateTime (timezone, storage format), validate with filter_var (rather than homemade regex) and send emails via dedicated libraries — relying on proven tools.

Ready-to-say script — defending proven tools in code review (30 s)

“For dates I use DateTime with the timezone configured, I store in ISO and format for display — no more shifted times. Emails go through filter_var, not a homemade regex. And sending goes via a library with authenticated SMTP, not mail(). Three proven tools, zero tinkering.”

Say it while showing the fixed timezone bug and a homemade regex replaced by filter_var.

Copy-paste template — date, gap and email validation

format('d/m/Y');
$aujourdhui = new DateTime();
$ecart = $inscription->diff($aujourdhui);
echo $ecart->format('%a days of membership');
// storage: $inscription->format('Y-m-d H:i:s')
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
  echo 'Invalid email.';
}

Member seniority

Riad computes years of membership with ->diff: leap years and uneven months handled, no manual calculation.

Business reference

The member number follows a strict internal format: dedicated regex, tested on edge cases, commented.

Welcome email

Address validated by filter_var, sending via SMTP library: the welcome arrives instead of ending in spam.

Legacy procedural code

A date() or strtotime() met during maintenance: Riad reads it, then migrates to DateTime for any handling.

Caution: Never insert unchecked user data into email headers: an attacker injects their own headers (hidden recipients, forged content) and hijacks your server for spam. Build headers from controlled values and go through a library that protects them. This guide gives the principle — have any real sending validated before going to production.

✓ Best practices

  • Use DateTime/DateTimeImmutable; configure the timezone.
  • Store dates in ISO, format only for display.
  • Validate email/URL with filter_var; send emails via a dedicated library.

✗ Common pitfalls

  • Timezone not configured (shifted times) — trap #1.
  • Storing a date in localized format (ambiguous, unsortable).
  • Writing a homemade regex to validate an email (hard, unreliable).

Key takeaways

  • DATES: modern PHP uses the DateTime/DateTimeImmutable class (create, ->format for display, ->diff for differences). Caution: TIMEZONE (otherwise wrong times) and STORAGE FORMAT (ISO YYYY-MM-DD, format only for display).
  • REGEX (preg_match, preg_replace) = patterns in text, powerful but hard. For COMMON validations (email, URL), prefer filter_var (FILTER_VALIDATE_EMAIL) to a homemade regex.
  • EMAILS: the native mail() function is basic/unreliable; use DEDICATED LIBRARIES (PHPMailer, Symfony Mailer) for reliability and deliverability.
  • Cross-cutting principle: rely on PROVEN TOOLS (DateTime, filter_var, email libraries) rather than doing everything by hand. Security: no unchecked data in email headers.

48h challenge — apply it tomorrow

Timestamp a real data item with DateTime (timezone configured, ISO storage, formatted display, a real ->diff), replace a homemade email regex with filter_var, and prepare sending via library instead of mail().

Section exit checklist
  • I can handle a date (create, format, compute a gap) with no timezone error.
  • I know when to use filter_var and when a regex is justified.
  • I have prepared email sending via library, headers locked.
Self-rating 1-5: do I store ISO and format for display?do I pick the proven tool rather than homemade tinkering?

Frequently asked questions

What are the common traps with dates in PHP, and how do I avoid them?

(1) Badly configured TIMEZONE (trap #1): shifted times, wrong timestamps. Configure it explicitly (date_default_timezone_set() or date.timezone); for international use, store in UTC and convert for display. (2) Confusing STORAGE and DISPLAY: never store a localized format — store in ISO (YYYY-MM-DD, sortable, unambiguous, the database standard) and format ONLY for display (->format('d/m/Y')). (3) Old functions (date(), strtotime()): prefer DateTime/DateTimeImmutable for handling. (4) Manual calculations (subtracted timestamps): tricky (leap years, uneven months) — use ->diff(). (5) Daylight saving: DateTime with the right timezones handles it. (6) Parsing: DateTime::createFromFormat() with a precise format, then validation.

Should I learn regular expressions, and when should I use (or avoid) them?

Yes, the BASICS — you will meet them everywhere — but without obsession. AVOID: (1) COMMON validations (email, URL, IP, integer) — use filter_var (FILTER_VALIDATE_EMAIL…): validating an email with regex is surprisingly hard; homemade regexes are too strict or too lax; (2) SIMPLE handling — prefer string functions (str_contains, str_replace, explode). JUSTIFIED: SPECIFIC patterns not covered (internal format, business reference, precise extraction) — carefully tested (edge cases) and COMMENTED (unreadable otherwise). A good developer knows when to use a regex and when to prefer the dedicated tool — no regex everywhere out of bravado.

Check your understanding

How do I validate an email in PHP?

Additional resources (optional, to go further)

Level 2 examFresh, harder questions to test what you've learned