Repository: jsonata-js/jsonata
Pull request #545 (2021-10-15 03:57:23)

toMillis: Allow width formatting without separators
# Bug Summary

Width formatting doesn't appear to be respected without a separator. Corresponding issue: https://github.com/jsonata-js/jsonata/issues/546

# Versions

JSONata `1.8.5`
Node `v14.17.0`

# Detailed Information

Reference spec for width formatting: https://www.w3.org/TR/xpath-functions-31/#date-time-examples

At the moment, it appears the width formatting is not honored within `$toMillis` due to the way the regex is being generated. For an example input `201802`, I should be able to specify a matcher `[Y0001][M01]` which will result in a parsed value of `2018/02/01`. At the moment, the library incorrectly grabs too many characters due to the `+` regex so I end up with `20180/02/01`


## Test Cases

| Input | Picture | Output (timestamp) | Output (pretty) |
| --- | --- | --- | --- |
| 201802 | `[Y0001][M01]` | 1517443200000 | Thu, 01 Feb 2018 00:00:00 GMT |
| 201802 | `[Y,*-4][M01]` | 1517443200000 | Thu, 01 Feb 2018 00:00:00 GMT |
| 20180205 | `[Y0001][M01][D01]]` | 1517788800000 | Mon, 05 Feb 2018 00:00:00 GMT |


Patch:
diff --git a/src/datetime.js b/src/datetime.js
index 15500bd..00d80ea 100644
--- a/src/datetime.js
+++ b/src/datetime.js
@@ -612,7 +612,7 @@ const dateTime = (function () {
                             def.integerFormat.mandatoryDigits = def.width.min;
                         }
                     }
-                    if (def.component === 'Y') {
+                    if ('YMD'.indexOf(def.component) !== -1) {
                         // §9.8.4.4
                         def.n = -1;
                         if (def.width && def.width.max !== undefined) {
@@ -983,6 +983,7 @@ const dateTime = (function () {
                         return offsetHours * 60 + offsetMinutes;
                     };
                 } else if (part.integerFormat) {
+                    part.integerFormat.n = part.n;
                     res = generateRegex(part.integerFormat);
                 } else {
                     // must be a month or day name
@@ -1027,6 +1028,17 @@ const dateTime = (function () {
         } else { // type === 'integer'
             matcher.type = 'integer';
             const isUpper = formatSpec.case === tcase.UPPER;
+            let occurrences;
+            if(formatSpec.n && formatSpec.n > 0){
+                if(formatSpec.optionalDigits === 0){
+                    occurrences = `{${formatSpec.n}}`;
+                } else {
+                    occurrences = `{${formatSpec.n - formatSpec.optionalDigits},${formatSpec.n}}`;
+                }
+            } else {
+                occurrences = '+';
+            }
+
             switch (formatSpec.primary) {
                 case formats.LETTERS:
                     matcher.regex = isUpper ? '[A-Z]+' : '[a-z]+';
@@ -1047,7 +1059,7 @@ const dateTime = (function () {
                     };
                     break;
                 case formats.DECIMAL:
-                    matcher.regex = '[0-9]+';
+                    matcher.regex = `[0-9]${occurrences}`;
                     if (formatSpec.ordinal) {
                         // ordinals
                         matcher.regex += '(?:th|st|nd|rd)';


Test patch:
diff --git a/test/test-suite/groups/function-tomillis/case010.json b/test/test-suite/groups/function-tomillis/case010.json
new file mode 100644
index 0000000..b053352
--- /dev/null
+++ b/test/test-suite/groups/function-tomillis/case010.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$toMillis(\"201802\", \"[Y0001][M01]\")",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": 1517443200000
+}
diff --git a/test/test-suite/groups/function-tomillis/case011.json b/test/test-suite/groups/function-tomillis/case011.json
new file mode 100644
index 0000000..40efab2
--- /dev/null
+++ b/test/test-suite/groups/function-tomillis/case011.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$toMillis(\"201802\", \"[Y,*-4][M01]\")",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": 1517443200000
+}
diff --git a/test/test-suite/groups/function-tomillis/case012.json b/test/test-suite/groups/function-tomillis/case012.json
new file mode 100644
index 0000000..dc8a0d5
--- /dev/null
+++ b/test/test-suite/groups/function-tomillis/case012.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$toMillis(\"20180205\", \"[Y0001][M01][D01]\")",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": 1517788800000
+}