LearnFrenzy
Sign in Take a free test

Data Types, Strings, Dates and Operators · Lesson 6 of 41

Date, Datetime and Time in Apex

6 min read Updated Nov 8, 2026 Saurabh Samir

Close dates, SLAs, renewals, business hours, reports "for last month" – date logic is a big part of Salesforce development. Apex gives you three types: Date (a calendar day), Datetime (an exact moment) and Time (a time of day). Most bugs come from mixing them up or forgetting time zones, so that's where this lesson spends its time.

In this lesson

  • Creating dates and doing date arithmetic
  • Differences and period boundaries
  • Datetime, GMT and the user's time zone
  • Formatting and parsing
  • Time values

Date: a calendar day

Date closeDate = Date.newInstance(2026, 1, 31);       // year, month (1–12), day
System.debug('Close date: ' + closeDate);

System.debug('+10 days:   ' + closeDate.addDays(10));
System.debug('+1 month:   ' + closeDate.addMonths(1));     // clamps to the month's last day
System.debug('-1 year:    ' + closeDate.addYears(-1));
System.debug('Month start: ' + closeDate.toStartOfMonth());
System.debug('Days in Feb 2028: ' + Date.daysInMonth(2028, 2));
System.debug('Leap year 2028? ' + Date.isLeapYear(2028));
System.debug('Year ' + closeDate.year() + ', month ' + closeDate.month() + ', day ' + closeDate.day());
System.debug('Day of year: ' + closeDate.dayOfYear());

Debug log

Close date: 2026-01-31 00:00:00
+10 days:   2026-02-10 00:00:00
+1 month:   2026-02-28 00:00:00
-1 year:    2025-01-31 00:00:00
Month start: 2026-01-01 00:00:00
Days in Feb 2028: 29
Leap year 2028? true
Year 2026, month 1, day 31
Day of year: 31

Two things to notice. Concatenating a Date into a String prints a midnight time (2026-01-31 00:00:00); use String.valueOf(d) for 2026-01-31 or d.format() for the user's locale. And addMonths(1) on 31 January gives the last day of February instead of overflowing into March – sensible behaviour for renewal dates. In real code you'll often start from Date.today(), which uses the running user's time zone to decide what "today" is.

Differences between dates

Date startDate = Date.newInstance(2026, 3, 15);
Date endDate = Date.newInstance(2026, 6, 10);

System.debug('daysBetween: ' + startDate.daysBetween(endDate));
System.debug('monthsBetween: ' + startDate.monthsBetween(endDate));
System.debug('Backwards: ' + endDate.daysBetween(startDate));
System.debug('Same day? ' + startDate.isSameDay(Date.newInstance(2026, 3, 15)));
System.debug('Before? ' + (startDate < endDate));

// a common question: how many days until renewal?
Date renewal = Date.newInstance(2026, 12, 1);
Date asOf = Date.newInstance(2026, 11, 8);
Integer daysLeft = asOf.daysBetween(renewal);
System.debug(daysLeft <= 30 ? 'Renewal due in ' + daysLeft + ' days' : 'Not yet');

Debug log

daysBetween: 87
monthsBetween: 3
Backwards: -87
Same day? true
Before? true
Renewal due in 23 days

monthsBetween counts calendar-month boundaries and ignores the day of the month, so 15 March to 10 June is 3 even though it's less than three full months. For "full months" logic, compare days yourself.

Datetime: a moment, stored in GMT

A Datetime is an exact instant. Salesforce stores it in GMT (UTC) and shows it to each user in their own time zone. The methods come in pairs: plain ones use the running user's time zone, Gmt ones use UTC.

Datetime meeting = Datetime.newInstanceGmt(2026, 11, 8, 9, 30, 0);    // 09:30 UTC

System.debug('GMT:      ' + meeting.formatGmt('yyyy-MM-dd HH:mm'));
System.debug('India:    ' + meeting.format('yyyy-MM-dd HH:mm z', 'Asia/Kolkata'));
System.debug('New York: ' + meeting.format('yyyy-MM-dd hh:mm a z', 'America/New_York'));
System.debug('Berlin:   ' + meeting.format('EEE d MMM yyyy, HH:mm', 'Europe/Berlin'));

System.debug('+90 minutes (GMT): ' + meeting.addMinutes(90).formatGmt('HH:mm'));
System.debug('Epoch ms: ' + meeting.getTime());
System.debug('Date part in GMT: ' + meeting.dateGmt());

// difference in hours between two Datetimes
Datetime opened = Datetime.newInstanceGmt(2026, 11, 7, 22, 0, 0);
Decimal hours = Decimal.valueOf(meeting.getTime() - opened.getTime()) / (1000 * 60 * 60);
System.debug('Hours open: ' + hours.setScale(1));

Debug log

GMT:      2026-11-08 09:30
India:    2026-11-08 15:00 IST
New York: 2026-11-08 04:30 AM EST
Berlin:   Sun 8 Nov 2026, 10:30
+90 minutes (GMT): 11:00
Epoch ms: 1794130200000
Date part in GMT: 2026-11-08 00:00:00
Hours open: 11.5

date() vs dateGmt()

myDatetime.date() returns the calendar date in the running user's time zone, dateGmt() in UTC. A case created at 22:00 UTC is "tomorrow" for a user in India. Decide deliberately which one your business rule means – and remember scheduled and batch jobs run as a specific user with their own time zone.

Formatting and parsing

Date d = Date.newInstance(2026, 11, 8);
System.debug('Date.format() (user locale): ' + d.format());
System.debug('String.valueOf (ISO):         ' + String.valueOf(d));

Datetime dt = Datetime.newInstanceGmt(2026, 11, 8, 14, 5, 0);
System.debug('ISO 8601 for APIs: ' + dt.formatGmt('yyyy-MM-dd\'T\'HH:mm:ss\'Z\''));

// parsing
Date parsed = Date.valueOf('2026-12-25');                       // ISO yyyy-MM-dd
Datetime fromJson = (Datetime) JSON.deserialize('"2026-11-08T09:30:00.000Z"', Datetime.class);
System.debug(parsed + ' | ' + fromJson.formatGmt('dd MMM yyyy HH:mm'));

Debug log

Date.format() (user locale): 08/11/2026
String.valueOf (ISO):         2026-11-08
ISO 8601 for APIs: 2026-11-08T14:05:00Z
2026-12-25 00:00:00 | 08 Nov 2026 09:30
PatternMeaningExample
yyyy / MM / ddyear / month / day2026 / 11 / 08
MMM / EEEshort month / weekday nameNov / Sun
HH / hh + a24-hour / 12-hour + AM/PM14 / 02 PM
mm / ssminutes / seconds05 / 00
ztime zone abbreviationIST

Watch the case: MM is months, mm is minutes – mixing them up is a classic bug.

Time: a time of day

Time opens = Time.newInstance(9, 0, 0, 0);
Time closes = Time.newInstance(18, 30, 0, 0);
Time callAt = Time.newInstance(17, 45, 0, 0);

System.debug('Open: ' + opens + ', closes: ' + closes);
System.debug('Within hours? ' + (callAt >= opens && callAt < closes));
System.debug('Last call slot: ' + closes.addMinutes(-30));

// combine a Date and a Time into a Datetime (in the user's time zone)
Datetime appointment = Datetime.newInstance(Date.newInstance(2026, 11, 9), opens);
System.debug('Appointment hour (user TZ): ' + appointment.hour());

Debug log

Open: 09:00:00.000Z, closes: 18:30:00.000Z
Within hours? true
Last call slot: 18:00:00.000Z
Appointment hour (user TZ): 9

For real working hours with holidays, use the BusinessHours class (BusinessHours.add(), BusinessHours.isWithin()) with the business hours your admin configured, rather than hard-coding times.

Practice

Try it yourself

  1. Write an expression for the last day of the current month.

    Show answer

    Date.today().toStartOfMonth().addMonths(1).addDays(-1) – or Date.newInstance(y, m, Date.daysInMonth(y, m)).

  2. An integration needs timestamps like 2026-11-08T09:30:00Z. Which method do you use, and why not format()?

    Show answer

    formatGmt('yyyy-MM-dd\'T\'HH:mm:ss\'Z\''). format() converts to the running user's time zone, which would make the "Z" (UTC) label wrong.

Quick check

In which time zone does Salesforce store Datetime values?

GMT (UTC).

What does Date.newInstance(2026, 1, 31).addMonths(1) return?

2026-02-28 – it clamps to the last day of the month.

What's the difference between MM and mm in a format pattern?

MM is month, mm is minutes.

Summary

  • Date is a calendar day, Datetime an exact moment in GMT, Time a time of day.
  • Use addDays/addMonths, daysBetween and toStartOfMonth for date arithmetic.
  • Plain Datetime methods use the running user's time zone; Gmt methods use UTC.
  • Format with explicit patterns and time zones; use BusinessHours for working time.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Search LearnFrenzy

Esc

Quick links