Orders query update_at comparison bug?

I’m issuing an orders query using the following query string as a parameter:

updated_at:>'2025-03-13T16:04:28Z' updated_at:<now

and the first order returned has

"updatedAt": "2025-03-13T16:04:28Z"

It appears that :> in my query is being treated as :>=. This appears to be a bug?

The extensions portion of the result appears to have parsed the query correctly.

        "query": "updated_at:>'2025-03-13T16:04:28Z' updated_at:<now",
        "parsed": {
          "field": "updated_at",
          "range_gt": "2025-03-13T11:04:28-05:00",
          "range_lt": "2025-08-11T16:01:15-05:00"
        }

(time there converted to the shop time zone)

A workaround for this is to add milliseconds to the query:

updated_at:>'2025-03-13T16:04:28.999Z' updated_at:<now

I’m guessing that Shopify stores the date in milliseconds but doesn’t provide that precision in the Order object from an orders query. I would prefer to (optionally) see the full precision in the Order object.

Nice find.

You may also need to use conditional keywords like AND or OR to help make sure the logic is only include orders that meet both of your conditions:

updated_at:>'2025-03-13T16:04:28.999Z' AND updated_at:<now

What I’m working on here is a polling strategy for new and updated orders. (Yes, I know about webhooks and may use them in the future.) The polling issue is that since Shopify doesn’t send milliseconds in the updatedAt value, to avoid missing any updates, my next poll has to start with the same second as the last update in the previous poll, meaning that I’ll have to re-process some orders.

Does anyone know how the milliseconds are rounded in the Order object? If that is predictable, then I could rely on it to avoid reprocessing.

E.g. if .499 is always rounded down and .500 is always rounded up, then my query could be :>= seconds.500.

Hey @Mitchell_Claborn, I looked in to this and the behaviour you are seeing is expected.

There isn’t rounding happening, but moreso if you’re requesting everything after 2025-03-13T16:04:28 the time used will be 2025-03-13T16:04:28.000. The reason for this is to ensure all orders within the same second are captured.

The easiest solution since you are using a range is to use a specific date for :< so you know where to start with your next query, instead of relying on the timestamp of the last returned order.

@KyleG-Shopify Thanks for that info. If I use

:< now

will “now” resolve to 0 milliseconds?

@KyleG-Shopify any thoughts on this? will “now” resolve to 0 milliseconds?

Hey @Mitchell_Claborn sorry for the delay. I was off on vacation.

I did some testing and looked in to our logs, and “now” appears to resolve to microsecond precision.

For your polling strategy, using specific timestamps instead of “now” is definitely the better approach. Since “now” gives you unpredictable microsecond precision, your workaround of tracking the exact updatedAt timestamp of your last processed order and adding .999Z milliseconds gives you consistent, repeatable boundaries between polling cycles. This eliminates any uncertainty about which orders were already processed.