Skip to content

Commit 394aa71

Browse files
committed
fix: do not rotate timestamptz to UTC
In order to be timezone-agnostic, we submit partition bounds without timezone offsets, like: VALUES FROM ('YYYY-MM-DD 00:00:00') TO ('YYYY-MM-DD 00:00:00') The time zone of the session is automatically applied by Postgres. When reading back these values, we get for instance YYYY-MM-DD 00:00:00+02 if the current time zone offset is +02. If we rotate this timestamp locally to UTC and resubmit it without the offset, it is now incorrect (unless the server happens to be configured in UTC). The fix consists of ignoring the offset when reading the current bounds, to be consistently timezone-agnostic. The offset always corresponds implicitly to the time zone of the SQL session, and must not be forced in the value of the timestamp. The SQL time zone can be changed dynamically with the PGTZ environment variable if desired. Signed-off-by: Daniel Vérité <[email protected]>
1 parent 6abfb46 commit 394aa71

File tree

4 files changed

+85
-3
lines changed

4 files changed

+85
-3
lines changed

pkg/ppm/bounds.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ func parseBoundAsUUIDv7(partition postgresql.PartitionResult) (lowerBound, upper
114114
}
115115

116116
func convertToDateTimeWithoutTimezone(bound time.Time) time.Time {
117-
parsedTime, err := time.Parse("2006-01-02 15:04:05", bound.UTC().Format("2006-01-02 15:04:05"))
117+
/* Remove the time zone offset without rotating the timestamp to UTC */
118+
parsedTime, err := time.Parse("2006-01-02 15:04:05", bound.Format("2006-01-02 15:04:05"))
118119
if err != nil {
119120
return time.Time{}
120121
}

pkg/ppm/bounds_internal_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ func TestParseBounds(t *testing.T) {
4545
LowerBound: "2024-01-01 23:30:00-01",
4646
UpperBound: "2025-02-03 00:30:00+01",
4747
},
48-
"2024-01-02T00:30:00Z",
49-
"2025-02-02T23:30:00Z",
48+
"2024-01-01T23:30:00Z",
49+
"2025-02-03T00:30:00Z",
5050
},
5151
{
5252
"UUIDv7 bounds",

scripts/bats/30_provisioning.bats

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,3 +364,55 @@ EOF
364364
run list_existing_partitions "unittest" "public" "${TABLE}"
365365
assert_output "$expected2"
366366
}
367+
368+
@test "Test a timestamptz key with provisioning crossing a DST transition " {
369+
local TABLE="test_tz1"
370+
local INTERVAL=weekly
371+
local RETENTION=3
372+
local PREPROVISIONED=2
373+
374+
declare -x PGTZ="Europe/Paris"
375+
376+
create_table_timestamptz_range ${TABLE}
377+
378+
local CONFIGURATION=$(basic_configuration ${TABLE} weekly created_at $RETENTION $PREPROVISIONED)
379+
local CONFIGURATION_FILE=$(generate_configuration_file "${CONFIGURATION}")
380+
381+
PPM_WORK_DATE="2025-03-06" run "$PPM_PROG" run provisioning -c ${CONFIGURATION_FILE}
382+
assert_success
383+
assert_output --partial "All partitions are correctly provisioned"
384+
385+
local expected_1=$(cat <<EOF
386+
public|${TABLE}_2025_w07|2025-02-10 00:00:00+01|2025-02-17 00:00:00+01
387+
public|${TABLE}_2025_w08|2025-02-17 00:00:00+01|2025-02-24 00:00:00+01
388+
public|${TABLE}_2025_w09|2025-02-24 00:00:00+01|2025-03-03 00:00:00+01
389+
public|${TABLE}_2025_w10|2025-03-03 00:00:00+01|2025-03-10 00:00:00+01
390+
public|${TABLE}_2025_w11|2025-03-10 00:00:00+01|2025-03-17 00:00:00+01
391+
public|${TABLE}_2025_w12|2025-03-17 00:00:00+01|2025-03-24 00:00:00+01
392+
EOF
393+
)
394+
395+
run list_existing_partitions "unittest" "public" ${TABLE}
396+
assert_output "$expected_1"
397+
398+
# Now advance one week up to the end of March 2025
399+
# The transition to summer time (GMT+1 => GMT+2) occurs at 2025-05-30 02:00 => 03:00
400+
401+
PPM_WORK_DATE="2025-03-13" run "$PPM_PROG" run provisioning -c ${CONFIGURATION_FILE}
402+
assert_success
403+
assert_output --partial "All partitions are correctly provisioned"
404+
405+
local expected_2=$(cat <<EOF
406+
public|${TABLE}_2025_w07|2025-02-10 00:00:00+01|2025-02-17 00:00:00+01
407+
public|${TABLE}_2025_w08|2025-02-17 00:00:00+01|2025-02-24 00:00:00+01
408+
public|${TABLE}_2025_w09|2025-02-24 00:00:00+01|2025-03-03 00:00:00+01
409+
public|${TABLE}_2025_w10|2025-03-03 00:00:00+01|2025-03-10 00:00:00+01
410+
public|${TABLE}_2025_w11|2025-03-10 00:00:00+01|2025-03-17 00:00:00+01
411+
public|${TABLE}_2025_w12|2025-03-17 00:00:00+01|2025-03-24 00:00:00+01
412+
public|${TABLE}_2025_w13|2025-03-24 00:00:00+01|2025-03-31 00:00:00+02
413+
EOF
414+
)
415+
run list_existing_partitions "unittest" "public" ${TABLE}
416+
assert_output "$expected_2"
417+
418+
}

scripts/bats/test/libs/seeds.bash

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ EOQ
3939
execute_sql "${QUERY}"
4040
}
4141

42+
create_table_timestamptz_range() {
43+
local TABLE="$1"
44+
45+
read -r -d '' QUERY <<EOQ ||
46+
CREATE TABLE ${TABLE} (
47+
id BIGSERIAL,
48+
value INT,
49+
created_at TIMESTAMPTZ NOT NULL
50+
) PARTITION BY RANGE (created_at);
51+
EOQ
52+
execute_sql "${QUERY}"
53+
}
54+
4255
generate_configuration_file() {
4356
local PARTITION_CONFIGURATION=$1
4457
local CONFIGURATION_TEMPLATE_FILE=configuration/template.yaml
@@ -51,3 +64,19 @@ generate_configuration_file() {
5164

5265
echo $FILENAME
5366
}
67+
68+
# Return a common configuration
69+
# Arguments: table interval partition-key retention preprovisioned
70+
basic_configuration() {
71+
cat << EOF_conf
72+
partitions:
73+
unittest:
74+
schema: public
75+
table: $1
76+
interval: $2
77+
partitionKey: $3
78+
cleanupPolicy: drop
79+
retention: $4
80+
preProvisioned: $5
81+
EOF_conf
82+
}

0 commit comments

Comments
 (0)