You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@drill.apache.org by "Paul Rogers (Jira)" <ji...@apache.org> on 2022/01/02 21:30:00 UTC

[jira] [Created] (DRILL-8101) Resolve the TIMESTAMP madness

Paul Rogers created DRILL-8101:
----------------------------------

             Summary: Resolve the TIMESTAMP madness
                 Key: DRILL-8101
                 URL: https://issues.apache.org/jira/browse/DRILL-8101
             Project: Apache Drill
          Issue Type: Bug
    Affects Versions: 1.19.0
            Reporter: Paul Rogers


Drill's TIMESAMP type tries to be two different things at the same time, causing incorrect results when the two interpretations collide.

Drill has the classic DATE and TIME data types. A DATE is just that: a day wherever you happen to be. Your birthday goes from midnight to midnight in the time zone where you find yourself. If you happen to travel around the world, you can make your birthday last almost 48 hours as midnight of your birthday starts at the international date line, circles the globe, followed by the midnight of the next day.

Similarly, a time is a time where you are. 12:00PM is noon (more-or-less) as determined by the sun. 12:00PM occurs once in every time zone every day. Since there are many time zones, there are many noons each day.

These are both examples of local time. Most databases combine these two ideas to get a DATETIME: a date and time wherever you are.

In our modern world, knowing something occurred on 2022-01-02 12:00:00 is not good enough. Did it occur at that time in my time zone or yours? If the event is a user login, or a network breach, then it occurred once, at a specific time, it did not occur many times: once in each time zone. Hence, machines often use UTC time to coordinate.

Unix-like systems also define the idea of a "timestamp", the number of seconds (or milliseconds or nanoseconds) since 1970-01-01 00:00:00. This is the time reported by Java in the {{System.currentTime()}} function. It is the time most often found in machine-generated logs. It may be as a number (ms since the epoch) or as an ISO-formatted string.

Thus, users of Drill would expect to find a "timestamp" type that represents a UTC timestamp in Unix format. The will be disappointed, however.

Drill's TIMESTAMP type is essentially a DATETIME type: it is a date/time in an unspecified timezone and that zone can be whatever you want it to be. UTC? Fine. Local? OK. Nairobi? Sure, why not.

This works fine as long as _all_ your data is in the same time zone, and you don't need a concept of "now". As described in DRILL-8099 and DRILL-8100, this is how the authors of CTAS thought of it: read Parquet data straight into Drill with no conversion, then write it back out to JSON with no conversion. Both work with UTC, so the result is fine: who cares that the 32-bit number, when in Drill, had no implied time zone? It is just a number we read then write. All good.

It is even possible to compute the difference of two DATETIMEs with unspecified time zone: that's what an INTERVAL does. As long as the times are actually in the same zone (UTC, say, or local, or Nairobi), then all is fine.

Everything collapses, however, when someone wants to know, "but how long ago was that event"? "Long enough ago that I need to raise the escalation level?" Drill has the INTERVAL type to give us the difference, but how do I get "now"? Drill has {{CURRENT_TIMESTAMP}}. But, how we have a problem, what timezone is that time in? UTC? My local timezone? Nairobi? And, what if my data is UTC but {{CURRENT_TIMESTAMP}} is local? Or visa-versa? The whole house of cards comes crashing down.

Over the years, this bug has appeared again and again. Sometimes people change the logic to assume TIMESTAMP is UTC. Sometimes things are changed to assume TIMESTAMP is local time (I've been guilty of this). Sometimes we just punt, and require that the machine (or test) run only in UTC, since that's the only place the two systems coincide.

But, in fact, I believe that the original designers of Drill meant TIMESTAMP to have _no_ timezone: two TIMESTAMP values could be in entirely different (unknown) timezones! One can see vestiges of this in the value vector code. It seems the original engineers imagined a "TIMESTAMP_WITH_ZONE" type, similar to Java's (or Joda's) {{ZonedDateTime}} type. Other bits of code (Parquet) refers to a never-built "TIMESTAMPZ" type for a UTC timestamp. When faced with the {{CURRENT_TIMESTAMP}} issue, fixes started down the path of saying that TIMESTAMP is local time, but this is probably a misunderstanding of the original design, forced upon us by the gaps in that original design.

Further, each time we make a change (such as DRILL-8099 and DRILL-8100), we change behavior, potentially breaking a kludge that someone found to kinda-sorta make things work.

Since computers can't deal with ambiguity the way humans can, we need a solution. It is not good enough for you to think "TIMESTAMP is UTC" and me to think "TIMESTAMP is local" and for Bob to think "TIMESTAMP is Java's {{LocalDateTime}}, it has no zone." The software needs to work one way. (Software is a pain that way.)

One way to solve the issue is to introduce two new types: DATETIME and UTC_TIMESTAMP. DATETIME has no timezone. It is not local time: it is an unknown, unspecified timezone, as in all other SQL tools. It is noon on your birthday, wherever you happen to be. UTC_TIMESTAMP is a Unix-like timestamp.

We then need functions to convert. Here, we can just follow Java's {{java.time}} package: to compare a UTC_TIMESTAMP with a DATETIME, you have to specify a timezone.

This will leave the current TIMESTAMP to settle down into a LOCAL_TIMESTAMP, which will turn out to be useless and will (we can hope) fade away.

All readers and writers that work with data that is known to be UTC (such as Parquet) will use UTC_TIMESTAMP. Where the zone is not known (JDBC DATETIME), then the data type will be DATETIME.

The functions that deal with the current time need new versions: "NOW" for "CURRENT_UTC_TIMESTAMP". The "AGE" function would use a UTC baseline for UTC_TIMESTAMP, perhaps local time for a DATETIME argument.

A design should spell out the details. Since time handing is HARD, the design should lean heavily on the {{java.time}} package and just follow the enormous work that went into that package.

After years of fighting this beast, I've come to the conclusion that only the two new types (and the deprecation and gradual fading away of the "local" TIMESTAMP type) can finally resolve our never-ending stream of bugs in this area.



--
This message was sent by Atlassian Jira
(v8.20.1#820001)