← All posts

The purchase moves to yesterday: taking the day from event_timestamp in the GA4 export

event_timestamp is in UTC, while event_date follows the property's time zone. Take the day from the timestamp and events near midnight land on the wrong day.

Step 1 of 6 A purchase at 00:30 on 29 October in Madrid, the property's time zone.
  1. A purchase at 00:30 on 29 October in Madrid, the property's time zone.
  2. The export stores event_date = 20261029, in the property's time zone. But event_timestamp is UTC: 23:30 on the 28th.
  3. Take the day with DATE(TIMESTAMP_MICROS(event_timestamp)) and the purchase lands on the 28th. Daily totals shift.
  4. The cause: the two columns use different clocks. Anything near midnight changes day.
  5. The fix: group by event_date, or convert the timestamp with the property's time zone.
  6. Check it on closed days: daily totals match GA4 again.

Every event in the GA4 export carries two dates. event_date is a text string in YYYYMMDD format. event_timestamp is a number: microseconds since 1970. Both seem to say the same thing, and for most events they do. For those that happen near midnight they don’t, because each one uses a different clock.

What a shifted day looks like

A report of purchases per day built with DATE(TIMESTAMP_MICROS(event_timestamp)):

  • The daily totals don’t match the GA4 report, even though the total for the month is almost the same.
  • The gap is concentrated in the first or last hours of the day, depending on your time zone: in Madrid, purchases between midnight and 1 a.m. (2 a.m. in summer) move to the previous day.
  • The first and last day of a period lose or gain events at the edges: a month filtered by the timestamp includes a few hours of the month before and leaves out the last hours of its own.
  • Hourly breakdowns are shifted by the same offset: peaks appear at the wrong time.

Why event_timestamp gives a different day

The export schema defines them differently:

  • event_date is the date the event was logged, in the time zone of the property (the one set in GA4 for reporting).
  • event_timestamp is the moment, in microseconds and in UTC, when Google Analytics received the event.

TIMESTAMP_MICROS turns the number into a TIMESTAMP, and DATE() with no time zone takes the date in UTC. In a property set to Europe/Madrid, a purchase at 00:30 on 29 October (UTC+1 in winter) is 23:30 on the 28th in UTC: event_date says 20261029 and your query says 2026-10-28. Google’s developers blog recommends exactly this: if you use event_timestamp, adjust it to the reporting time zone before comparing it with the interface.

How to find a shifted date

  1. Search your queries for DATE(TIMESTAMP_MICROS(event_timestamp)), EXTRACT(DATE FROM ...) or FORMAT_TIMESTAMP with no time zone argument.
  2. For a closed day, compare the number of events grouped by event_date with the number grouped by the date of the timestamp. If they differ, the query is using UTC.
  3. Check the property’s time zone in GA4, under Admin, Property details: that’s the zone event_date uses.

How to group by the right day

  • To group by day, use event_date. It’s already in the property’s time zone. To treat it as a date, PARSE_DATE('%Y%m%d', event_date).

  • If you need the timestamp (for hours, or to calculate times between events), convert it with the property’s time zone:

    DATE(TIMESTAMP_MICROS(event_timestamp), 'Europe/Madrid') AS day,
    EXTRACT(HOUR FROM TIMESTAMP_MICROS(event_timestamp)
      AT TIME ZONE 'Europe/Madrid') AS hour
    

    Use a zone name like Europe/Madrid rather than a fixed offset such as +01:00, so daylight saving time is applied.

  • Filter the tables with _TABLE_SUFFIX and the days with the same criterion as the grouping, so you don’t cut the edges of the period differently.

  • Compare closed days: daily tables can still receive late events for up to three days after the date.

Documentation: BigQuery Export schema and Bridge the gap between the GA UI and BigQuery export.

How to check each event lands on its day

  • Grouping by event_date and grouping by the converted timestamp give the same daily totals.
  • The daily totals for closed days match the GA4 report, within the usual small gap.
  • Hourly peaks appear at the local times when your traffic actually happens.

The time, the time zone and the dates in the example are illustrative.