You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2026/08/19 18:04:25 UTC

(plc4x) 06/12: fix(plc4go/spi): number the day of week from one, as plc4j does

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 211cd034e5125a1f0ba037388c1a3dd5b634d8b6
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed Aug 19 18:06:05 2026 +0200

    fix(plc4go/spi): number the day of week from one, as plc4j does
    
    GetDayOfWeek returned Go's time.Weekday, which counts Sunday as 0 and Saturday
    as 6. plc4j returns LocalDateTime#getDayOfWeek#getValue, the ISO-8601 numbering
    from 1 for Monday to 7 for Sunday, and every wire format carrying this field
    counts from one - a zero there means "no day given" in KNX DPT 19.001 and is
    simply invalid for S7, whose mspec calls it "one 4-bit value representing 1 - 7".
    
    S7 numbers its own field differently again, Sunday first; rotating into that
    belongs in the protocol layer, since one generic value cannot serve both S7 and
    KNX at once.
---
 plc4go/spi/values/PlcDATE_AND_LTIME.go     | 19 ++++++++-
 plc4go/spi/values/PlcDATE_AND_TIME.go      | 19 ++++++++-
 plc4go/spi/values/PlcDATE_AND_TIME_test.go | 65 ++++++++++++++++++++++++++++++
 3 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/plc4go/spi/values/PlcDATE_AND_LTIME.go b/plc4go/spi/values/PlcDATE_AND_LTIME.go
index 4bbb17c805..539cda1eb5 100644
--- a/plc4go/spi/values/PlcDATE_AND_LTIME.go
+++ b/plc4go/spi/values/PlcDATE_AND_LTIME.go
@@ -75,8 +75,25 @@ func (m PlcDATE_AND_LTIME) GetDay() uint8 {
 	return uint8(m.value.Day())
 }
 
+// GetDayOfWeek returns the ISO-8601 day of week: 1 == Monday .. 7 == Sunday.
+//
+// This mirrors plc4j's PlcDATE_AND_TIME#getDayOfWeek, which returns
+// LocalDateTime#getDayOfWeek#getValue, i.e. the same 1..7 ISO numbering. It is
+// deliberately NOT time.Weekday (0 == Sunday .. 6 == Saturday): every wire
+// format that carries this field numbers the days from 1, so a 0 either means
+// "no day given" (KNX DPT 19.001, knxnetip.mspec dayOfWeek) or is outright
+// invalid (S7 DATE_AND_TIME, s7.mspec: "one 4-bit value representing 1 - 7").
+//
+// Note that the S7 dow field uses a DIFFERENT 1..7 numbering (1 == Sunday ..
+// 7 == Saturday). Rotating into it is a protocol level concern that this generic
+// value cannot do for KNX and S7 at the same time, and the S7 data-io serializer
+// does not do it yet.
 func (m PlcDATE_AND_LTIME) GetDayOfWeek() uint8 {
-	return uint8(m.value.Weekday())
+	// time.Weekday numbers Sunday 0, ISO-8601 numbers it last as 7.
+	if weekday := m.value.Weekday(); weekday != time.Sunday {
+		return uint8(weekday)
+	}
+	return 7
 }
 
 func (m PlcDATE_AND_LTIME) GetHour() uint8 {
diff --git a/plc4go/spi/values/PlcDATE_AND_TIME.go b/plc4go/spi/values/PlcDATE_AND_TIME.go
index 5a6ade44bf..0701990cf0 100644
--- a/plc4go/spi/values/PlcDATE_AND_TIME.go
+++ b/plc4go/spi/values/PlcDATE_AND_TIME.go
@@ -73,8 +73,25 @@ func (m PlcDATE_AND_TIME) GetDay() uint8 {
 	return uint8(m.value.Day())
 }
 
+// GetDayOfWeek returns the ISO-8601 day of week: 1 == Monday .. 7 == Sunday.
+//
+// This mirrors plc4j's PlcDATE_AND_TIME#getDayOfWeek, which returns
+// LocalDateTime#getDayOfWeek#getValue, i.e. the same 1..7 ISO numbering. It is
+// deliberately NOT time.Weekday (0 == Sunday .. 6 == Saturday): every wire
+// format that carries this field numbers the days from 1, so a 0 either means
+// "no day given" (KNX DPT 19.001, knxnetip.mspec dayOfWeek) or is outright
+// invalid (S7 DATE_AND_TIME, s7.mspec: "one 4-bit value representing 1 - 7").
+//
+// Note that the S7 dow field uses a DIFFERENT 1..7 numbering (1 == Sunday ..
+// 7 == Saturday). Rotating into it is a protocol level concern that this generic
+// value cannot do for KNX and S7 at the same time, and the S7 data-io serializer
+// does not do it yet.
 func (m PlcDATE_AND_TIME) GetDayOfWeek() uint8 {
-	return uint8(m.value.Weekday())
+	// time.Weekday numbers Sunday 0, ISO-8601 numbers it last as 7.
+	if weekday := m.value.Weekday(); weekday != time.Sunday {
+		return uint8(weekday)
+	}
+	return 7
 }
 
 func (m PlcDATE_AND_TIME) GetHour() uint8 {
diff --git a/plc4go/spi/values/PlcDATE_AND_TIME_test.go b/plc4go/spi/values/PlcDATE_AND_TIME_test.go
new file mode 100644
index 0000000000..2573782db2
--- /dev/null
+++ b/plc4go/spi/values/PlcDATE_AND_TIME_test.go
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package values
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// TestPlcDATE_AND_TIME_GetDayOfWeek pins the ISO-8601 numbering (1 == Monday ..
+// 7 == Sunday) that both consumers of this getter need.
+//
+// It used to return time.Weekday, i.e. 0 == Sunday .. 6 == Saturday. That is
+// wrong for every wire format in the tree: the KNX DPT 19.001 dayOfWeek field
+// (protocols/knxnetip .. KnxDatapoint.go) reserves 0 for "no day given", and the
+// S7 DATE_AND_TIME dow nibble is documented in s7.mspec as "representing 1 - 7",
+// so a Sunday used to serialize as the invalid value 0. plc4j's
+// PlcDATE_AND_TIME#getDayOfWeek returns LocalDateTime#getDayOfWeek#getValue,
+// which is this same 1..7 numbering.
+func TestPlcDATE_AND_TIME_GetDayOfWeek(t *testing.T) {
+	// 2024-08-19 is a Monday, so the week below walks Monday to Sunday.
+	expected := []uint8{1, 2, 3, 4, 5, 6, 7}
+	weekdays := []time.Weekday{
+		time.Monday, time.Tuesday, time.Wednesday, time.Thursday,
+		time.Friday, time.Saturday, time.Sunday,
+	}
+	for i, want := range expected {
+		day := time.Date(2024, time.August, 19+i, 10, 30, 45, 0, time.UTC)
+		t.Run(weekdays[i].String(), func(t *testing.T) {
+			assert.Equal(t, weekdays[i], day.Weekday(), "fixture sanity")
+			assert.Equal(t, want, NewPlcDATE_AND_TIME(day).GetDayOfWeek())
+			assert.Equal(t, want, NewPlcDATE_AND_LTIME(day).GetDayOfWeek())
+		})
+	}
+}
+
+// TestPlcDATE_AND_TIME_GetDayOfWeekIsAlwaysInRange guards the whole domain: no
+// input may ever produce the 0 that both wire formats treat as "unset"/invalid.
+func TestPlcDATE_AND_TIME_GetDayOfWeekIsAlwaysInRange(t *testing.T) {
+	day := time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC)
+	for i := range 400 {
+		got := NewPlcDATE_AND_TIME(day.AddDate(0, 0, i)).GetDayOfWeek()
+		assert.GreaterOrEqual(t, got, uint8(1))
+		assert.LessOrEqual(t, got, uint8(7))
+	}
+}