Просмотр исходного кода

fix: Date and Time no longer throws on omitted amount, bounds negative offsets

The runner does not merge configSchema defaults into config before
calling execute, so an omitted amount arrived as undefined and the
round-1 isNaN guard rejected it. Omitted, null or empty amount is now
treated as a legitimate no-op shift of zero, same as every other
field in this file. Also the offset range check used a flat 14-hour
bound for both signs, so -13:00 and -14:00 were silently accepted;
negative offsets are now bounded at -12:00, matching the message that
was already thrown.
fszontagh 1 месяц назад
Родитель
Сommit
3796d2bda4
3 измененных файлов с 30 добавлено и 5 удалено
  1. 6 2
      nodes/core/datetime.js
  2. 17 0
      tests/nodes/datetime-errors-offset-negative.json
  3. 7 3
      tests/nodes/datetime.json

+ 6 - 2
nodes/core/datetime.js

@@ -108,7 +108,8 @@ function offsetMinutes(offset) {
     }
     }
     const hours = parseInt(match[2], 10);
     const hours = parseInt(match[2], 10);
     const mins = parseInt(match[3], 10);
     const mins = parseInt(match[3], 10);
-    if (hours > 14 || mins > 59) {
+    const maxHours = match[1] === '-' ? 12 : 14;
+    if (hours > maxHours || mins > 59) {
         throw new Error('Date and Time: offset "' + offset +
         throw new Error('Date and Time: offset "' + offset +
             '" is out of range. Real offsets run from -12:00 to +14:00');
             '" is out of range. Real offsets run from -12:00 to +14:00');
     }
     }
@@ -196,7 +197,10 @@ async function execute(config, input, context) {
         return result;
         return result;
     }
     }
 
 
-    const amount = Number(config.amount);
+    const rawAmount = config.amount === undefined || config.amount === null || config.amount === ''
+        ? 0
+        : config.amount;
+    const amount = Number(rawAmount);
     if (isNaN(amount)) {
     if (isNaN(amount)) {
         throw new Error('Date and Time: amount must be a number, got "' + config.amount + '"');
         throw new Error('Date and Time: amount must be a number, got "' + config.amount + '"');
     }
     }

+ 17 - 0
tests/nodes/datetime-errors-offset-negative.json

@@ -0,0 +1,17 @@
+{
+  "name": "verify-datetime-errors-offset-negative",
+  "nodes": [
+    {"id": "n1", "name": "Trigger", "type": "click-trigger", "position": {"x": 0, "y": 0}, "config": {}},
+    {"id": "n2", "name": "Fixture", "type": "code", "position": {"x": 0, "y": 100},
+     "config": {"code": "return { when: '2026-01-15T10:30:00Z' };"}},
+    {"id": "n3", "name": "DatetimeOutOfRangeNegativeOffset", "type": "datetime", "position": {"x": 0, "y": 200},
+     "config": {"operation": "format", "inputField": "data.result.when", "offset": "-13:00", "outputField": "value"}}
+  ],
+  "connections": [
+    {"sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "n2", "targetInput": "data"},
+    {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n3", "targetInput": "data"}
+  ],
+  "expect": {
+    "n3": {"status": "failed"}
+  }
+}

+ 7 - 3
tests/nodes/datetime.json

@@ -15,7 +15,9 @@
     {"id": "n7", "name": "Now", "type": "datetime", "position": {"x": 250, "y": 200},
     {"id": "n7", "name": "Now", "type": "datetime", "position": {"x": 250, "y": 200},
      "config": {"operation": "now"}},
      "config": {"operation": "now"}},
     {"id": "n8", "name": "FormatMaxOffset", "type": "datetime", "position": {"x": 350, "y": 200},
     {"id": "n8", "name": "FormatMaxOffset", "type": "datetime", "position": {"x": 350, "y": 200},
-     "config": {"operation": "format", "inputField": "data.result.when", "format": "YYYY-MM-DD HH:mm:ss", "offset": "+14:00", "outputField": "value"}}
+     "config": {"operation": "format", "inputField": "data.result.when", "format": "YYYY-MM-DD HH:mm:ss", "offset": "+14:00", "outputField": "value"}},
+    {"id": "n9", "name": "AddOmittedAmount", "type": "datetime", "position": {"x": 450, "y": 200},
+     "config": {"operation": "add", "inputField": "data.result.when", "unit": "days", "format": "YYYY-MM-DD HH:mm:ss", "outputField": "value"}}
   ],
   ],
   "connections": [
   "connections": [
     {"sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "n2", "targetInput": "data"},
     {"sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "n2", "targetInput": "data"},
@@ -24,7 +26,8 @@
     {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n5", "targetInput": "data"},
     {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n5", "targetInput": "data"},
     {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n6", "targetInput": "data"},
     {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n6", "targetInput": "data"},
     {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n7", "targetInput": "data"},
     {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n7", "targetInput": "data"},
-    {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n8", "targetInput": "data"}
+    {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n8", "targetInput": "data"},
+    {"sourceNodeId": "n2", "sourceOutput": "main", "targetNodeId": "n9", "targetInput": "data"}
   ],
   ],
   "expect": {
   "expect": {
     "n3": {"status": "completed", "output": {"value": "2026-01-15 10:30:00"}},
     "n3": {"status": "completed", "output": {"value": "2026-01-15 10:30:00"}},
@@ -32,6 +35,7 @@
     "n5": {"status": "completed", "output": {"value": "2026-01-18"}},
     "n5": {"status": "completed", "output": {"value": "2026-01-18"}},
     "n6": {"status": "completed", "output": {"value": 5}},
     "n6": {"status": "completed", "output": {"value": 5}},
     "n7": {"status": "completed"},
     "n7": {"status": "completed"},
-    "n8": {"status": "completed", "output": {"value": "2026-01-16 00:30:00"}}
+    "n8": {"status": "completed", "output": {"value": "2026-01-16 00:30:00"}},
+    "n9": {"status": "completed", "output": {"value": "2026-01-15 10:30:00"}}
   }
   }
 }
 }