Dates and time zones
The API uses three date shapes, and a field's type in the reference tells you which: date-time is an instant, date is a calendar day, and an HH:mm string is a wall-clock time.
The Three Shapes
| Shape | Example | Meaning |
|---|---|---|
| UTC instant | "2026-10-18T14:00:00Z" | An exact moment, always returned in UTC. |
| Wall-clock twins | "startDate": "2026-10-18", "startTime": "10:00" | The instant's date and time on the church's calendar and clock. Most such records also return a read-only timeZone (an IANA id such as America/New_York); events, sermons and service plan times also return a read-only utcOffset (such as "-04:00", in effect at that record's instant). |
| Calendar day | "2026-10-18" | A day with no time and no time zone, in yyyy-MM-dd form. It is never converted. |
The Church's Time Zone
Every church account has one time zone. Wall-clock twins are expressed in that zone. "Today", whole-day filters, and date defaults (for example, a note or group member created without a date) use the church's calendar day, not the UTC day.
Instants with Wall-Clock Twins
Reads return both the instant and its twins. On writes you can send either:
- Send the twins (a date plus a time) to write a time the way the church sees it. The server converts it in the church's time zone, including daylight saving time. A missing time means
00:00, but event time slots and service plan times need astartTime. - Send the instant, if you already work in UTC.
When an object carries an instant, its twins are ignored, so echoing back everything you read changes nothing. To change a time through its twins, send them without the instant. (Event exceptions.additionDates are the exception: they are added to exceptions.addition.)
| Record | Instants | Twins | Notes |
|---|---|---|---|
Events (POST /events, PUT /events/{id}) | start, end | startDate, endDate | endDate is inclusive. Send start or startDate. |
Event time slots (times[]) | start, end, show, hide | startTime, endTime, showTime, hideTime | Times fall on the event's date. An endTime earlier than startTime ends on the next day. |
| Event repetition | starting, until.date | startingDate, until.endDate | until.endDate is inclusive. |
| Event registration | registrationStart, deadline (read-only) | matching …Date and …Time twins (read-only) | Set through startLimit and deadlineLimit, whose time instant or timeOfDay twin sets the time of day. |
| Group meetings | the meeting event | same as events | Read-only on the group (POST /groups and PUT /groups/{id} ignore it). Write the meeting as an event with its group. |
| Form coupons | start, end | startDate, startTime, endDate, endTime | An omitted endTime means 00:00, so the coupon stops at the start of endDate. |
Facility requests (PUT /facilities/requests/{id}) | start, end | startDate, startTime, endDate, endTime | endDate defaults to startDate. |
Bulk facility requests (POST /facilities/requests/bulk) | start, end | startTime, endTime only | The time of day is applied to each booked event day. An endTime earlier than startTime falls on the next day. |
| Room and resource blockouts | start, end, times[] | startDate, endDate, times[].startTime, times[].endTime | Same rules as events. |
Service plan times (POST /plans/{plan}/times, PUT /plans/{plan}/times/{id}) | date.start, date.end | startDate, startTime, endTime | Both times fall on the same day. Serving and sign-up objects echo these times read-only. |
New plans and service type times (POST /plans, POST /plans/{id}/copy, service types) | the plan's start; times[].start, times[].end | the plan's startDate; times[].startDate, startTime, endTime | Only the church day of the plan's start is used, so prefer startDate. |
Bill schedules (POST /bills, PUT /bills/{id}) | schedules[].starting, schedules[].until.date | not usable on bills | Schedules must carry starting; one with only startingDate is rejected. End one with until.date. The bill's date and due are calendar days. |
Some records return twins but have no published write: forms (start, expires and their startDate, startTime, expiresDate, expiresTime) and check-in logs (in, out, with date, inTime and outTime on the event's day). Workflow tasks (POST /workflows, PUT /workflows/{id}) hold their wall-clock settings as strings: a time as HH:mm, a day as yyyy-MM-dd.
Sermons: An Instant with Read-Only Twins
A sermon has two separate dates:
dateis the calendar day the sermon was delivered, the date shown publicly. It defaults to the church's today on create, is unchanged when omitted on update, and is not derived frompublishedOn.publishedOnis the instant the sermon becomes visible online. SendpublishedOnto set it.publishedOnDate,publishedOnTime,timeZoneandutcOffsetare derived from it, and echoed values are ignored whenpublishedOnis present. WhenpublishedOnis omitted on create, it defaults to midnight ofdatein the church's time zone.
Calendar Days
These fields are yyyy-MM-dd days with no time component:
| Record | Fields |
|---|---|
| Connections | date, due, completed |
| Notes | date (defaults to today on create) |
| Safeguards | completed, expires |
| Group memberships | start (defaults to today when a member is added), end (the member is no longer current on this day) |
| Important dates on a person | events[].date |
| Date custom fields | the response text, as yyyy-MM-dd |
| Contributions | date, see below |
| Batches | date, depositDate, settled |
| Pledges and giving campaigns | start, end (inclusive: the day still counts), campaign threshold |
| Seasonal addresses | start, end (inclusive) |
| Recurring giving schedules | start, end, next |
| Query statements | date values, as { "start", "end" } days |
| Sermons and series | sermon date, series earliest and latest |
| Attendance | meetingDate (on group attendance reports, preferred over the older date instant) |
| People and group members | lastAttendedDate |
Contribution date and timestamp. date is the day the contribution is recorded on, in the church's time zone. timestamp is the exact moment a payment gateway processed it, in UTC. timestamp is null for manually entered, imported and Pushpay-synced contributions, and is cleared when the day is changed. Report and group by date.
Daylight Saving Time
When the server turns twins into an instant, two local times need a rule:
- A time in the spring-forward gap does not exist on the clock. It moves forward by the length of the gap.
- A time in the fall-back hour happens twice. Sermons, form coupons and facility requests keep the first occurrence, still on daylight time. Event and blockout time slots, and the end of a service plan time, are always stored at the second occurrence, on standard time, even when you send the first one as an instant.
A date sent without a time means midnight; if midnight falls in a gap, the same forward rule applies. Bulk facility requests don't apply the gap rule, so don't send them a time inside the gap.
For a church in America/New_York:
| Twins sent | Instant stored | Reads back as |
|---|---|---|
2026-03-08 at 02:30 (clocks jump from 2:00 to 3:00) | 2026-03-08T07:30:00Z | 03:30, utcOffset -04:00 |
2026-11-01 at 01:30 on a sermon, coupon or facility request (1:00 to 2:00 happens twice) | 2026-11-01T05:30:00Z | 01:30, utcOffset -04:00 |
2026-11-01 at 01:30 on an event time slot | 2026-11-01T06:30:00Z | 01:30, utcOffset -05:00 |
To store the second 1:30 AM on a sermon, coupon or facility request, send the instant 2026-11-01T06:30:00Z instead.
Filtering by Date
Instant ranges. A dotted name.start / name.end pair typed date-time matches whole days in the church's time zone, inclusive at both ends. The time of day never narrows the range; send each bound as midnight UTC of the day you mean. This returns sermons published at any time from September 1 through September 30, church time:
/api/sermons?publishedOn.start=2026-09-01T00:00:00Z&publishedOn.end=2026-09-30T00:00:00Z
Three filters read each bound in the church's time zone before taking its day: date on /api/billing/invoices, /api/plans/opportunities and /api/people/{id}/serving. For these, send the church's local midnight, such as 2026-09-01T00:00:00-04:00 for a New York church; midnight UTC lands on the previous day for any church west of UTC.
Bare start and end parameters, such as the /api/events/calendar window and the facility usage feeds, are exact instants. Birthday and anniversary ranges on people and connections are compared differently and are not covered by these rules.
Calendar-day ranges. A pair typed date takes inclusive yyyy-MM-dd bounds. This returns connections dated September 1 through September 30:
/api/connections?date.start=2026-09-01&date.end=2026-09-30
A single calendar-day parameter, such as date on /api/groups/members, matches that one day. On /api/events/calendar, startDate and endDate take precedence over the start and end instants.
Custom field dates. Date custom field filters take yyyy-MM-dd bounds as {fieldId}.start and {fieldId}.end, both inclusive: /api/people?12.start=2026-01-01&12.end=2026-12-31. A bound that can't be read as a date is ignored. See filtering by custom fields.
Sending a Date-Time Where a Day Is Expected
Calendar-day fields and parameters also accept a full ISO 8601 date-time, so older clients keep working. The value is reduced to its UTC calendar day, so 2026-08-09T23:00:00-07:00 becomes 2026-08-10. Send yyyy-MM-dd to avoid the surprise. Query statement date values are the exception: there an instant is read in the church's time zone.
A date-typed field or parameter that can't be read as a date returns 400 with the offending field or parameter named. In a request body, send null or omit the field to leave a day empty; an empty string is rejected. A date custom field response that can't be read is saved empty instead.
{
"errors": { "date.Start": ["The value 'notadate' is not valid for Date.Start."] },
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400
}Examples
An event written with twins, 10:00 to 11:30 on October 18 in the church's time zone. A new event also needs a name and a calendar (its id or code; a group's meeting doesn't need one):
{
"name": "Fall Picnic",
"calendar": { "id": 1 },
"startDate": "2026-10-18",
"endDate": "2026-10-18",
"times": [{ "startTime": "10:00", "endTime": "11:30" }]
}The same event written with instants, for a church in America/New_York:
{
"name": "Fall Picnic",
"calendar": { "id": 1 },
"start": "2026-10-18T14:00:00Z",
"end": "2026-10-18T15:30:00Z",
"times": [{ "start": "2026-10-18T14:00:00Z", "end": "2026-10-18T15:30:00Z" }]
}A sermon as read:
{
"date": "2026-08-09",
"publishedOn": "2026-08-09T13:00:00Z",
"publishedOnDate": "2026-08-09",
"publishedOnTime": "09:00",
"timeZone": "America/New_York",
"utcOffset": "-04:00"
}The date fields of a sermon write (POST /sermons or PUT /sermons/{id}, alongside the other sermon fields), publishing it at 9:00 AM Eastern on August 16:
{
"date": "2026-08-16",
"publishedOn": "2026-08-16T13:00:00Z"
}A pledge's calendar days, read and written in the same form:
{
"start": "2026-01-01",
"end": "2026-12-31"
}Updated about 2 hours ago
