Repository: jsonata-js/jsonata (js)

Issue:
fromMillis not handling PN picture string
I'm trying to format a timestamp with the AM/PM marker in uppercase with `$fromMillis()`. Looking at the JSONata doc https://docs.jsonata.org/date-time-functions#frommillis which points to https://www.w3.org/TR/xpath-functions-31/#date-picture-string I can see some [examples](https://www.w3.org/TR/xpath-functions-31/#date-time-examples) with [PN] for upper case (`3:58 PM`  `format-time($t, "[h]:[m01] [PN]", "en", (), ()) ` ) and [Pn] for lower case (`3:58:45 pm` `format-time($t, "[h]:[m01]:[s01] [Pn]", "en", (), ())`.

I've tried out some equivalent picture values on https://try.jsonata.org/ with:
`$fromMillis($millis(), "[h]:[m01] [Pn]")` which also returns `"10:17 am"` (Correct)
`$fromMillis($millis(), "[h]:[m01] [PN]")` which returns `"10:17 am"` (I believe this is incorrect and should be `"10:17 AM"`)

I've been able to work around this with `$uppercase()` so it's a minor issue but I wanted to raise here to confirm whether the behaviour is incorrect for [PN] or if I'm misunderstanding how this should work. Thanks.

Pull request description:
Add upper/lower presentation format for am/pm in fromMillis
Fixes #574

Updates [$fromMillis() function](https://docs.jsonata.org/date-time-functions#frommillis) to support am/pm `PN` presentation format to output AM/PM and adds test cases for `PN` and `Pn` (existing test case for default presentation covers just `P`).

Note: I did not implement [PNn] for Am/Pm, it's not shown in the examples https://www.w3.org/TR/xpath-functions-31/#date-time-examples so I assumed it's not commonly used and did not implement it to keep the fix simple. Happy to revisit that if needed.

Signed-off-by: Alex Woodgate <ajwoodgate@gmail.com>

Patch:
diff --git a/src/datetime.js b/src/datetime.js
index 00d80ea..0121765 100644
--- a/src/datetime.js
+++ b/src/datetime.js
@@ -904,6 +904,13 @@ const dateTime = (function () {
                 if (offset === 0 && markerSpec.presentation2 === 't') {
                     componentValue = 'Z';
                 }
+            } else if (markerSpec.component === 'P') {
+                // §9.8.4.7 Formatting Other Components
+                // Formatting P for am/pm
+                // getDateTimeFragment() always returns am/pm lower case so check for UPPER here
+                if (markerSpec.names === tcase.UPPER) {
+                    componentValue = componentValue.toUpperCase();
+                }
             }
             return componentValue;
         };


Test patch:
diff --git a/test/test-suite/groups/function-fromMillis/formatDateTime.json b/test/test-suite/groups/function-fromMillis/formatDateTime.json
index 96e1962..c08a9ea 100644
--- a/test/test-suite/groups/function-fromMillis/formatDateTime.json
+++ b/test/test-suite/groups/function-fromMillis/formatDateTime.json
@@ -539,6 +539,22 @@
             "2018-10-21T13:05:00.000Z"
         ]
     },
+    {
+        "function": "#fromMillis",
+        "category": "Upper case AM/PM presentation",
+        "description": "am/pm presentation should be set to uppercase AM",
+        "expr": "$fromMillis(1521801216617, '[F], [D]/[M]/[Y] [h]:[m]:[s] [PN]')",
+        "data": {},
+        "result": "friday, 23/3/2018 10:33:36 AM"
+    },
+    {
+        "function": "#fromMillis",
+        "category": "Lower case AM/PM presentation",
+        "description": "am/pm presentation should be set to lowercase am",
+        "expr": "$fromMillis(1521801216617, '[F], [D]/[M]/[Y] [h]:[m]:[s] [Pn]')",
+        "data": {},
+        "result": "friday, 23/3/2018 10:33:36 am"
+    },
     {
         "function": "#fromMillis",
         "category": "error",