Dates & Timezones
Timezones
Timestamp columns are not standardized across tables. Some are stored in UTC, some in Bucharest time, some on the clinic's own clock, and some are plain dates with no time at all. Check this page before you filter or group by a date column.
Codes
Code | Meaning |
|---|---|
UTC | Coordinated Universal Time |
BUC | Bucharest wall-clock time (Europe/Bucharest). Earlier versions of this page called it "EEDT". It is UTC+2 in winter and UTC+3 in summer, so always convert from 'Europe/Bucharest', never a fixed offset. |
LOCAL | The clinic's wall-clock time, in the clinic's IANA timezone from clinics_view.timezone |
DAY | Date only, the clinic's calendar day. No time component. |
LEGACY | Nothing in the current platform writes this column. Don't report on it. |
Rules of thumb
- Audit columns (created_at, updated_at, deleted_at) are BUC, with one exception: notification_periods writes them as UTC.
- Most newer *_at columns that aren't audit columns are UTC.
- If a column isn't listed on this page, don't assume BUC. Contact us before building a report on it.
- appointment and custom_events store two copies of each time, a local one and a _utc one. They are stored separately and can drift apart after a daylight saving change, a clinic timezone change, or a bulk edit of a recurring series. For reporting, use the _utc columns and convert with clinics_view.timezone.
Financial
Column | Use | TZ |
|---|---|---|
sale.created_at / updated_at / deleted_at | Record audit | BUC |
sale.taxable_at | Tax point. Takes issued_at, else closed_at, else checkout_at, else the first item, else created_at | UTC |
sale.closed_at | When the sale closed | UTC |
sale.first_line_item_added_at | When the first item was added | UTC |
sale.checkout_at | When the sale moved to ready for checkout. Only a 2023 backfill writes it, so it is stale or NULL on recent sales | UTC |
sale.hidden_at | Hidden from the client. Mixed: sales created by pet parents write it in BUC | UTC / BUC |
sale.payment_date | When the status became paid. This is the only LOCAL datetime on sale | LOCAL |
charges.made_at | When the payment was actually made. Use this one | UTC |
charges.date | LEGACY, replaced by made_at in 2023. Old rows are mixed: online and terminal payments in BUC, the rest LOCAL. Newer rows get the database default CURRENT_TIMESTAMP. Deprecated | Mixed |
charges.payout_date / payout_arrival_date | Stripe payout created / arrived | UTC |
charges.created_at / updated_at / deleted_at | Audit | BUC |
payments.made_at | When the allocation was made (copied from the charge) | UTC |
payments.date | LEGACY, same story as charges.date. Deprecated | Mixed |
payments.created_at / updated_at / deleted_at | Audit | BUC |
new_invoice.date | Invoice date printed on the invoice | DAY |
new_invoice.issued_at | Exact issue time | UTC |
predefined_discounts.disabled_at | When the discount was disabled | UTC |
treatments.date | LEGACY | n/a |
treatments / sold_service_packages created_at / updated_at / deleted_at | Line item audit | BUC |
sold_service_package_products.expiration_date | Expiry of the batch used | DAY |
Stock & Services
Column | Use | TZ |
|---|---|---|
reception_notes.date / doc_date / due_date | Received date, supplier invoice date, payment due date. These changed from date-only to datetime in July to August 2025. Older rows are bare midnight values. Newer rows are clinic noon stored as UTC. Use DATE(col) | UTC (older rows: date at midnight) |
reception_notes.payment_date | LEGACY | n/a |
products.disabled_at | Product deactivated | UTC |
products.sent_to_vetcove_at | Last push to Vetcove | UTC |
products.stock_alert_sent_on | Low stock alert sent | UTC |
products.inventory_start_date | LEGACY (2020 seed data) | n/a |
product_reminders.due_date | Reminder due date | DAY |
Clients & Patients
Column | Use | TZ |
|---|---|---|
users.created_date | LEGACY. No longer written since 2020 | n/a |
users.birthdate | Pet parent's date of birth | DAY |
users.terms_agreed_on | Terms acceptance. Mixed: LOCAL when staff save, BUC on self-registration. Staff overwrite it on every edit, so it is not a reliable "agreed on" date | Mixed |
users.has_app | First app login | BUC |
clients.last_visit_date | Latest visit or direct sale at the clinic | DAY |
pet.birthday | Date of birth. 1980-01-01 is a placeholder meaning unknown | DAY |
pet.created_date | LEGACY | n/a |
patients.archived_at | Archived at this clinic | UTC |
vetpetarchive.created_at | First time the pet was archived at the clinic (kept on re-archive) | BUC |
Medical
Column | Use | TZ |
|---|---|---|
new_file_cases.date | Record (visit) date | DAY |
new_file_cases.opened_at / closed_at | Record opened / closed | BUC |
new_file_cases.follow_up_date | LEGACY. Nothing writes it | n/a |
prescriptions.prescribed_at / expires_at | Issued / expires. Rows from integrations may be midnight | UTC |
Appointments & Operational
Column | Use | TZ |
|---|---|---|
appointment.datetime / end_datetime | Start / end on the clinic clock. Recommended visits store the date only, with no end | LOCAL |
appointment.datetime_start_utc / datetime_end_utc | Start / end. The app treats these as the source of truth | UTC |
appointment.last_confirmed_at | Last confirmed | UTC |
appointment.self_checked_in_at / self_check_in_scheduled_at | Self check-in done / check-in prompt due | UTC |
appointment.last_instance_datetime | Last generated recurrence (parent rows) | UTC |
appointment.done_at | When a recommended visit was marked done | DAY |
appointment.notifications_sent_on | Historical only. The writer was removed in April 2026 | BUC |
appointment.time | LEGACY | n/a |
appointment_requests.date / start_time | Requested slot | LOCAL |
appointments_reschedules.date / start_time | New confirmed slot | LOCAL |
custom_events.datetime_start / datetime_end | Calendar block or task on the clinic clock (rows before 2026-06-09 were corrected) | LOCAL |
custom_events.datetime_start_utc / datetime_end_utc, last_instance_datetime | Same instants as above | UTC |
notification_periods.send_at | Scheduled send time. Use this one | UTC |
notification_periods.datetime | Deprecated since December 2025. It can drift from send_at | UTC |
notification_periods.created_at / updated_at | Written as UTC, unlike other tables | UTC |
reminder_protocol_usage.due_date / administration_date | Due / given date | DAY |
reminder_protocol_usage.*_utc | UTC instant of clinic-local midnight. DATE() of these gives the previous day for clinics east of UTC | UTC |
Converting to clinic local time
Use CONVERT_TIMEZONE with the clinic's zone from clinics_view.timezone. Pick the source zone to match the column's code above.
From a UTC column:
SELECT
a.id,
CONVERT_TIMEZONE('UTC', c.timezone, a.datetime_start_utc) AS start_local
FROM appointment a
JOIN clinics_view c ON c.id = a.clinic_id;From a BUC column:
SELECT
s.id,
CONVERT_TIMEZONE('Europe/Bucharest', c.timezone, s.created_at) AS created_local
FROM sale s
JOIN clinics_view c ON c.id = s.clinic_id;For a single clinic, you can pass the zone name directly, for example 'America/Toronto'.