From 74f6bb506b0e48f179a7138c8f4e72f18179c330 Mon Sep 17 00:00:00 2001 From: Dmitrii Semenov Date: Tue, 16 Apr 2024 09:15:23 -0400 Subject: [PATCH 1/6] Implements lowBoundary and highBoundary --- sof-js/src/path.js | 83 ++++++++++ tests/fn_boundary.json | 348 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 427 insertions(+), 4 deletions(-) diff --git a/sof-js/src/path.js b/sof-js/src/path.js index 4c2166c..ad5f478 100644 --- a/sof-js/src/path.js +++ b/sof-js/src/path.js @@ -1,4 +1,10 @@ import { default as fhirpath } from 'fhirpath' +// @note: These are not exported by main export, but could be because they are useful +import { FP_Date, FP_DateTime, FP_Time, timeRE, dateTimeRE } from 'fhirpath/src/types'; + +// @note this is not exported by fhirpath/src/types but should be +let dateRE = new RegExp( + '^[0-9][0-9][0-9][0-9](-[0-9][0-9](-[0-9][0-9])?)?$'); const identity = (ctx, v) => [v] @@ -24,6 +30,81 @@ function getReferenceKey(nodes, opts) { }) } +function lowBoundary(nodes) { + return nodes.flatMap((node) => { + if (node == null) { + return null; + } + if (node.match(timeRE)) { + const picoSeconds = (node.split(".")[1] || "").padEnd(9, 0); + const time = new FP_Time(node.split(".")[0])._dateAtPrecision(2); + return time.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds); + } + // @note for some examples of Dates and DateTimes, both are matched by this regex + // else if(node.match(dateRE)) { + // let [year, month, day] = node.split('-'); + // if (!day) { + // day = '01' + // } + // if (!month) { + // month = '01' + // } + // return `${year}-${month}-${day}`; + // } + // if (node.match(dateTimeRE)) { + // const picoSeconds = (node.split(".")[1] || "").padEnd(9, 0); + // const hasTimeZone = (node.split('-').length == 4 || node.includes('+')); + // const dateTime = new FP_DateTime(node.split(".")[0])._dateAtPrecision(5); + // const date = dateTime.toISOString().split("T")[0]; + // const time = dateTime.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds); + // if (hasTimeZone) { + // //let timeZone = + + // } else { + // time.concat("+14:00") + // } + // return date.concat("T", time); + // } + return [node]; + }) +} + +function highBoundary(nodes) { + return nodes.flatMap((node) => { + if (node == null) { + return null; + } + if (node.match(timeRE)) { + const hasSeconds = node.split(":").length == 3; + const picoSeconds = (node.split(".")[1] || "").padEnd(9, 9); + const time = new FP_Time(node.split(".")[0])._dateAtPrecision(2); + if (!hasSeconds) time.setSeconds(59); + return time.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds); + } + // @note for some examples of Dates and DateTimes, both are matched by this regex + // else if (node.match(dateRE)) { + // let [year, month, day] = node.split('-'); + // if (!month) { + // month = '12'; + // day = '31'; + // } + // if (!day) { + // if (month == "02") { + // (year % 4) ? day = '28' : day = '29'; + // } + // else if (["04", "06", "09", "11"].includes(month)) { + // day = '30'; + // } + // else { + // day = '31'; + // } + // } + // return `${year}-${month}-${day}`; + // } + return [node]; + }) +} + function ofType(ctx, nodes, a1, a2, a3) { console.log('of type nodes', nodes, a1, a2, a3) return 'ups' @@ -52,6 +133,8 @@ let fhirpath_options = { getResourceKey: { fn: getResourceKey, arity: { 0: [] } }, getReferenceKey: { fn: getReferenceKey, arity: { 0: [], 1: ['TypeSpecifier'] } }, identity: { fn: (nodes) => nodes, arity: { 0: [] } }, + lowBoundary: { fn: lowBoundary, arity: { 0: [] }, nullable: true}, + highBoundary: { fn: highBoundary, arity: { 0: [] }, nullable: true}, } } diff --git a/tests/fn_boundary.json b/tests/fn_boundary.json index a76996c..d54a442 100644 --- a/tests/fn_boundary.json +++ b/tests/fn_boundary.json @@ -38,10 +38,126 @@ }, "valueTime": "12:34" }, + { + "resourceType": "Observation", + "id": "o5", + "code": { + "text": "code" + }, + "valueTime": "12:34:01" + }, + { + "resourceType": "Observation", + "id": "o6", + "code": { + "text": "code" + }, + "valueTime": "12:34:00.01" + }, + { + "resourceType": "Observation", + "id": "o7", + "code": { + "text": "code" + }, + "valueTime": "12:34:00.000001" + }, + { + "resourceType": "Observation", + "id": "o8", + "code": { + "text": "code" + }, + "valueTime": "12:34:15.4621" + }, + { + "resourceType": "Observation", + "id": "o9", + "code": { + "text": "code" + }, + "valueTime": "12:34:59.00000036" + }, + { + "resourceType": "Observation", + "id": "o10", + "code": { + "text": "code" + }, + "valueTime": "12:34:08.7" + }, + { + "resourceType": "Observation", + "id": "o11", + "code": { + "text": "code" + }, + "valueTime": "12:34:27.918272" + }, + { + "resourceType": "Observation", + "id": "o12", + "code": { + "text": "code" + }, + "valueTime": "12:34:42.987654321" + }, + { + "resourceType": "Observation", + "id": "o13", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2010-10-10T15:21" + }, + { + "resourceType": "Observation", + "id": "o14", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2010-10-10T15:21:18" + }, + { + "resourceType": "Observation", + "id": "o15", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2010-10-10T15:21:01:898998" + }, { "resourceType": "Patient", "id": "p1", "birthDate": "1970-06" + }, + { + "resourceType": "Patient", + "id": "p2", + "birthDate": "1999-08" + }, + { + "resourceType": "Patient", + "id": "p3", + "birthDate": "2003" + }, + { + "resourceType": "Patient", + "id": "p4", + "birthDate": "2000-02" + }, + { + "resourceType": "Patient", + "id": "p5", + "birthDate": "2001-02" + }, + { + "resourceType": "Patient", + "id": "p6", + "birthDate": "1994-05-18" } ], "tests": [ @@ -156,7 +272,7 @@ }, { "id": "o2", - "datetime": "2010-10-10T00:00:00.000+14:00" + "datetime": "2010-10-10T00:00:00.000000000+14:00" }, { "id": "o3", @@ -165,6 +281,50 @@ { "id": "o4", "datetime": null + }, + { + "id": "o5", + "datetime": null + }, + { + "id": "o6", + "datetime": null + }, + { + "id": "o7", + "datetime": null + }, + { + "id": "o8", + "datetime": null + }, + { + "id": "o9", + "datetime": null + }, + { + "id": "o10", + "datetime": null + }, + { + "id": "o11", + "datetime": null + }, + { + "id": "o12", + "datetime": null + }, + { + "id": "o13", + "datetime": "2010-10-10T15:21:00.000000000+14:00" + }, + { + "id": "o14", + "datetime": "2010-10-10T15:21:18.000000000+14:00" + }, + { + "id": "o15", + "datetime": "2010-10-10T15:21:01:898998000+14:00" } ] }, @@ -197,7 +357,15 @@ }, { "id": "o2", - "datetime": "2010-10-10T23:59:59.999-12:00" + "datetime": "2010-10-10T23:59:59.999999999-12:00" + }, + { + "id": "o3", + "datetime": null + }, + { + "id": "o4", + "datetime": null }, { "id": "o3", @@ -206,6 +374,50 @@ { "id": "o4", "datetime": null + }, + { + "id": "o5", + "datetime": null + }, + { + "id": "o6", + "datetime": null + }, + { + "id": "o7", + "datetime": null + }, + { + "id": "o8", + "datetime": null + }, + { + "id": "o9", + "datetime": null + }, + { + "id": "o10", + "datetime": null + }, + { + "id": "o11", + "datetime": null + }, + { + "id": "o12", + "datetime": null + }, + { + "id": "o13", + "datetime": "2010-10-10T15:21:59.999999999-12:00" + }, + { + "id": "o14", + "datetime": "2010-10-10T15:21:18.999999999-12:00" + }, + { + "id": "o15", + "datetime": "2010-10-10T15:21:01:898998999-12:00" } ] }, @@ -235,6 +447,26 @@ { "id": "p1", "date": "1970-06-01" + }, + { + "id": "p2", + "date": "1999-08-01" + }, + { + "id": "p3", + "date": "2003-01-01" + }, + { + "id": "p4", + "date": "2000-02-01" + }, + { + "id": "p5", + "date": "2001-02-01" + }, + { + "id": "p6", + "date": "1994-05-18" } ] }, @@ -264,6 +496,26 @@ { "id": "p1", "date": "1970-06-30" + }, + { + "id": "p2", + "date": "1999-08-31" + }, + { + "id": "p3", + "date": "2003-12-31" + }, + { + "id": "p4", + "date": "2000-02-29" + }, + { + "id": "p5", + "date": "2001-02-28" + }, + { + "id": "p6", + "date": "1994-05-18" } ] }, @@ -304,7 +556,51 @@ }, { "id": "o4", - "time": "12:34:00.000" + "time": "12:34:00.000000000" + }, + { + "id": "o5", + "time": "12:34:01.000000000" + }, + { + "id": "o6", + "time": "12:34:00.010000000" + }, + { + "id": "o7", + "time": "12:34:00.000001000" + }, + { + "id": "o8", + "time": "12:34:15.462100000" + }, + { + "id": "o9", + "time": "12:34:59.000000360" + }, + { + "id": "o10", + "time": "12:34:08.700000000" + }, + { + "id": "o11", + "time": "12:34:27.918272000" + }, + { + "id": "o12", + "time": "12:34:42.987654321" + }, + { + "id": "o13", + "time": null + }, + { + "id": "o14", + "time": null + }, + { + "id": "o15", + "time": null } ] }, @@ -345,7 +641,51 @@ }, { "id": "o4", - "time": "12:34:59.999" + "time": "12:34:59.999999999" + }, + { + "id": "o5", + "time": "12:34:01.999999999" + }, + { + "id": "o6", + "time": "12:34:00.019999999" + }, + { + "id": "o7", + "time": "12:34:00.000001999" + }, + { + "id": "o8", + "time": "12:34:15.462199999" + }, + { + "id": "o9", + "time": "12:34:59.000000369" + }, + { + "id": "o10", + "time": "12:34:08.799999999" + }, + { + "id": "o11", + "time": "12:34:27.918272999" + }, + { + "id": "o12", + "time": "12:34:42.987654321" + }, + { + "id": "o13", + "time": null + }, + { + "id": "o14", + "time": null + }, + { + "id": "o15", + "time": null } ] } From 611df3c610be2b2fde552eff52b86102bf6c2457 Mon Sep 17 00:00:00 2001 From: Dmitrii Semenov Date: Thu, 2 May 2024 23:58:17 -0400 Subject: [PATCH 2/6] Solved issue with determining date and dateTime --- sof-js/src/path.js | 148 ++++++++------ tests/fn_boundary.json | 432 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 508 insertions(+), 72 deletions(-) diff --git a/sof-js/src/path.js b/sof-js/src/path.js index ad5f478..53e73fc 100644 --- a/sof-js/src/path.js +++ b/sof-js/src/path.js @@ -1,10 +1,6 @@ import { default as fhirpath } from 'fhirpath' -// @note: These are not exported by main export, but could be because they are useful -import { FP_Date, FP_DateTime, FP_Time, timeRE, dateTimeRE } from 'fhirpath/src/types'; - -// @note this is not exported by fhirpath/src/types but should be -let dateRE = new RegExp( - '^[0-9][0-9][0-9][0-9](-[0-9][0-9](-[0-9][0-9])?)?$'); +import fhir_r4_model from 'fhirpath/fhir-context/r4' +import { FP_DateTime, FP_Time } from 'fhirpath/src/types'; const identity = (ctx, v) => [v] @@ -30,41 +26,66 @@ function getReferenceKey(nodes, opts) { }) } +function checkDate (date, type) { + let [year, month, day] = date.split('-'); + if (type == "low") { + if (!day) { + day = '01' + } + if (!month) { + month = '01' + } + } else { + if (!month) { + month = '12'; + day = '31'; + } + if (!day) { + if (month == "02") { + (year % 4) ? day = '28' : day = '29'; + } + else if (["04", "06", "09", "11"].includes(month)) { + day = '30'; + } + else { + day = '31'; + } + } + } + return `${year}-${month}-${day}`; +} + function lowBoundary(nodes) { return nodes.flatMap((node) => { if (node == null) { return null; } - if (node.match(timeRE)) { - const picoSeconds = (node.split(".")[1] || "").padEnd(9, 0); - const time = new FP_Time(node.split(".")[0])._dateAtPrecision(2); + const { name: type } = node.getTypeInfo(); + if (type === "time") { + const picoSeconds = (node.data.split(".")[1] || "").padEnd(9, 0); + const time = new FP_Time(node.data.split(".")[0])._dateAtPrecision(2); return time.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds); } - // @note for some examples of Dates and DateTimes, both are matched by this regex - // else if(node.match(dateRE)) { - // let [year, month, day] = node.split('-'); - // if (!day) { - // day = '01' - // } - // if (!month) { - // month = '01' - // } - // return `${year}-${month}-${day}`; - // } - // if (node.match(dateTimeRE)) { - // const picoSeconds = (node.split(".")[1] || "").padEnd(9, 0); - // const hasTimeZone = (node.split('-').length == 4 || node.includes('+')); - // const dateTime = new FP_DateTime(node.split(".")[0])._dateAtPrecision(5); - // const date = dateTime.toISOString().split("T")[0]; - // const time = dateTime.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds); - // if (hasTimeZone) { - // //let timeZone = - - // } else { - // time.concat("+14:00") - // } - // return date.concat("T", time); - // } + else if(type === "date") { + return checkDate(node.data, "low"); + } + else if (type === 'dateTime') { + var timeHMS, timeZone, picoSeconds; + var [date, time] = node.data.split("T"); + if (date) var dateObj = checkDate(date, "low"); + if (time) { + var matchResult = time.match(/(\d{2}:\d{2}(?::\d{2})?)(?:\.(\d+))?(Z|[\+\-]\d{2}:\d{2})?/); + if (matchResult) { + [, timeHMS, picoSeconds, timeZone] = matchResult; + } + } + if (timeHMS) dateObj = date.concat("T", timeHMS); + const dateTime = new FP_DateTime(dateObj)._dateAtPrecision(5); + const newDate = dateTime.toISOString().split("T")[0]; + if (picoSeconds == undefined) picoSeconds = "0"; + const newTime = dateTime.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds.padEnd(9, 0)); + return newDate.concat("T", newTime, (timeZone == undefined) ? timeZone = "+14:00" : timeZone); + } return [node]; }) } @@ -74,33 +95,38 @@ function highBoundary(nodes) { if (node == null) { return null; } - if (node.match(timeRE)) { - const hasSeconds = node.split(":").length == 3; - const picoSeconds = (node.split(".")[1] || "").padEnd(9, 9); - const time = new FP_Time(node.split(".")[0])._dateAtPrecision(2); + const { name: type } = node.getTypeInfo(); + if (type === "time") { + const hasSeconds = node.data.split(":").length == 3; + const picoSeconds = (node.data.split(".")[1] || "").padEnd(9, 9); + const time = new FP_Time(node.data.split(".")[0])._dateAtPrecision(2); if (!hasSeconds) time.setSeconds(59); return time.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds); } - // @note for some examples of Dates and DateTimes, both are matched by this regex - // else if (node.match(dateRE)) { - // let [year, month, day] = node.split('-'); - // if (!month) { - // month = '12'; - // day = '31'; - // } - // if (!day) { - // if (month == "02") { - // (year % 4) ? day = '28' : day = '29'; - // } - // else if (["04", "06", "09", "11"].includes(month)) { - // day = '30'; - // } - // else { - // day = '31'; - // } - // } - // return `${year}-${month}-${day}`; - // } + else if (type === "date") { + return checkDate(node.data, "high"); + } + else if (type === "dateTime") { + var timeHMS, timeZone, picoSeconds; + var [date, time] = node.data.split("T"); + if (date) var dateObj = checkDate(date, "high"); + if (time) { + var matchResult = time.match(/(\d{2}:\d{2}(?::\d{2})?)(?:\.(\d+))?(Z|[\+\-]\d{2}:\d{2})?/); + if (matchResult) { + [, timeHMS, picoSeconds, timeZone] = matchResult; + } + var [hours, minutes, seconds] = timeHMS.split(":"); + } + if (timeHMS) dateObj = date.concat("T", timeHMS); + const dateTime = new FP_DateTime(dateObj)._dateAtPrecision(5); + const newDate = dateTime.toISOString().split("T")[0]; + if (hours == undefined) dateTime.setHours(23); + if (minutes == undefined) dateTime.setMinutes(59); + if (seconds == undefined) dateTime.setSeconds(59); + if (picoSeconds == undefined) picoSeconds = "9"; + const newTime = dateTime.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds.padEnd(9, 9)); + return newDate.concat("T", newTime, (timeZone == undefined) ? timeZone = "-12:00" : timeZone); + } return [node]; }) } @@ -133,8 +159,8 @@ let fhirpath_options = { getResourceKey: { fn: getResourceKey, arity: { 0: [] } }, getReferenceKey: { fn: getReferenceKey, arity: { 0: [], 1: ['TypeSpecifier'] } }, identity: { fn: (nodes) => nodes, arity: { 0: [] } }, - lowBoundary: { fn: lowBoundary, arity: { 0: [] }, nullable: true}, - highBoundary: { fn: highBoundary, arity: { 0: [] }, nullable: true}, + lowBoundary: { fn: lowBoundary, arity: { 0: [] }, nullable: true, internalStructures: true}, + highBoundary: { fn: highBoundary, arity: { 0: [] }, nullable: true, internalStructures: true}, } } @@ -155,7 +181,7 @@ function process_constants(constants) { } export function fhirpath_evaluate(data, path, constants = []) { - return fhirpath.evaluate(data, rewrite_path(path), process_constants(constants), null, fhirpath_options); + return fhirpath.evaluate(data, rewrite_path(path), process_constants(constants), fhir_r4_model, fhirpath_options); } export function fhirpath_validate(path) { diff --git a/tests/fn_boundary.json b/tests/fn_boundary.json index d54a442..87bfcaa 100644 --- a/tests/fn_boundary.json +++ b/tests/fn_boundary.json @@ -127,7 +127,97 @@ "text": "code" }, "status": "final", - "valueDateTime": "2010-10-10T15:21:01:898998" + "valueDateTime": "2010-10-10T13:41:01.898998" + }, + { + "resourceType": "Observation", + "id": "o16", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2010-10-10T11:45:50.12345-10:00" + }, + { + "resourceType": "Observation", + "id": "o17", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2010-10-10T19:07:13.123456789+09:00" + }, + { + "resourceType": "Observation", + "id": "o18", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2010-10-10T07:39:01.5+05:00" + }, + { + "resourceType": "Observation", + "id": "o19", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2020-05-17T18:19:43.6789Z" + }, + { + "resourceType": "Observation", + "id": "o20", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2022-03-15T12:30:45-08:00" + }, + { + "resourceType": "Observation", + "id": "o21", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2018-03-15T21:43-05:00" + }, + { + "resourceType": "Observation", + "id": "o22", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2016-07-23T14:35Z" + }, + { + "resourceType": "Observation", + "id": "o23", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2009-11-27T05:18+03:00" + }, + { + "resourceType": "Observation", + "id": "o24", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2005" + }, + { + "resourceType": "Observation", + "id": "o25", + "code": { + "text": "code" + }, + "status": "final", + "valueDateTime": "2007-03" }, { "resourceType": "Patient", @@ -199,6 +289,90 @@ { "id": "o4", "decimal": null + }, + { + "id": "o5", + "decimal": null + }, + { + "id": "o6", + "decimal": null + }, + { + "id": "o7", + "decimal": null + }, + { + "id": "o8", + "decimal": null + }, + { + "id": "o9", + "decimal": null + }, + { + "id": "o10", + "decimal": null + }, + { + "id": "o11", + "decimal": null + }, + { + "id": "o12", + "decimal": null + }, + { + "id": "o13", + "decimal": null + }, + { + "id": "o14", + "decimal": null + }, + { + "id": "o15", + "decimal": null + }, + { + "id": "o16", + "decimal": null + }, + { + "id": "o17", + "decimal": null + }, + { + "id": "o18", + "decimal": null + }, + { + "id": "o19", + "decimal": null + }, + { + "id": "o20", + "decimal": null + }, + { + "id": "o21", + "decimal": null + }, + { + "id": "o22", + "decimal": null + }, + { + "id": "o23", + "decimal": null + }, + { + "id": "o24", + "decimal": null + }, + { + "id": "o25", + "decimal": null } ] }, @@ -240,6 +414,90 @@ { "id": "o4", "decimal": null + }, + { + "id": "o5", + "decimal": null + }, + { + "id": "o6", + "decimal": null + }, + { + "id": "o7", + "decimal": null + }, + { + "id": "o8", + "decimal": null + }, + { + "id": "o9", + "decimal": null + }, + { + "id": "o10", + "decimal": null + }, + { + "id": "o11", + "decimal": null + }, + { + "id": "o12", + "decimal": null + }, + { + "id": "o13", + "decimal": null + }, + { + "id": "o14", + "decimal": null + }, + { + "id": "o15", + "decimal": null + }, + { + "id": "o16", + "decimal": null + }, + { + "id": "o17", + "decimal": null + }, + { + "id": "o18", + "decimal": null + }, + { + "id": "o19", + "decimal": null + }, + { + "id": "o20", + "decimal": null + }, + { + "id": "o21", + "decimal": null + }, + { + "id": "o22", + "decimal": null + }, + { + "id": "o23", + "decimal": null + }, + { + "id": "o24", + "decimal": null + }, + { + "id": "o25", + "decimal": null } ] }, @@ -324,7 +582,47 @@ }, { "id": "o15", - "datetime": "2010-10-10T15:21:01:898998000+14:00" + "datetime": "2010-10-10T13:41:01.898998000+14:00" + }, + { + "id": "o16", + "datetime": "2010-10-10T11:45:50.123450000-10:00" + }, + { + "id": "o17", + "datetime": "2010-10-10T19:07:13.123456789+09:00" + }, + { + "id": "o18", + "datetime": "2010-10-10T07:39:01.500000000+05:00" + }, + { + "id": "o19", + "datetime": "2020-05-17T18:19:43.678900000Z" + }, + { + "id": "o20", + "datetime": "2022-03-15T12:30:45.000000000-08:00" + }, + { + "id": "o21", + "datetime": "2018-03-15T21:43:00.000000000-05:00" + }, + { + "id": "o22", + "datetime": "2016-07-23T14:35:00.000000000Z" + }, + { + "id": "o23", + "datetime": "2009-11-27T05:18:00.000000000+03:00" + }, + { + "id": "o24", + "datetime": "2005-01-01T00:00:00.000000000+14:00" + }, + { + "id": "o25", + "datetime": "2007-03-01T00:00:00.000000000+14:00" } ] }, @@ -367,14 +665,6 @@ "id": "o4", "datetime": null }, - { - "id": "o3", - "datetime": null - }, - { - "id": "o4", - "datetime": null - }, { "id": "o5", "datetime": null @@ -417,7 +707,47 @@ }, { "id": "o15", - "datetime": "2010-10-10T15:21:01:898998999-12:00" + "datetime": "2010-10-10T13:41:01.898998999-12:00" + }, + { + "id": "o16", + "datetime": "2010-10-10T11:45:50.123459999-10:00" + }, + { + "id": "o17", + "datetime": "2010-10-10T19:07:13.123456789+09:00" + }, + { + "id": "o18", + "datetime": "2010-10-10T07:39:01.599999999+05:00" + }, + { + "id": "o19", + "datetime": "2020-05-17T18:19:43.678999999Z" + }, + { + "id": "o20", + "datetime": "2022-03-15T12:30:45.999999999-08:00" + }, + { + "id": "o21", + "datetime": "2018-03-15T21:43:59.999999999-05:00" + }, + { + "id": "o22", + "datetime": "2016-07-23T14:35:59.999999999Z" + }, + { + "id": "o23", + "datetime": "2009-11-27T05:18:59.999999999+03:00" + }, + { + "id": "o24", + "datetime": "2005-12-31T23:59:59.999999999-12:00" + }, + { + "id": "o25", + "datetime": "2007-03-31T23:59:59.999999999-12:00" } ] }, @@ -601,6 +931,46 @@ { "id": "o15", "time": null + }, + { + "id": "o16", + "time": null + }, + { + "id": "o17", + "time": null + }, + { + "id": "o18", + "time": null + }, + { + "id": "o19", + "time": null + }, + { + "id": "o20", + "time": null + }, + { + "id": "o21", + "time": null + }, + { + "id": "o22", + "time": null + }, + { + "id": "o23", + "time": null + }, + { + "id": "o24", + "time": null + }, + { + "id": "o25", + "time": null } ] }, @@ -686,6 +1056,46 @@ { "id": "o15", "time": null + }, + { + "id": "o16", + "time": null + }, + { + "id": "o17", + "time": null + }, + { + "id": "o18", + "time": null + }, + { + "id": "o19", + "time": null + }, + { + "id": "o20", + "time": null + }, + { + "id": "o21", + "time": null + }, + { + "id": "o22", + "time": null + }, + { + "id": "o23", + "time": null + }, + { + "id": "o24", + "time": null + }, + { + "id": "o25", + "time": null } ] } From ad029901a64f46e1f17c99d3e3f61261755a1ebf Mon Sep 17 00:00:00 2001 From: Dmitrii Semenov Date: Fri, 10 May 2024 14:44:40 -0400 Subject: [PATCH 3/6] Updated leap year logic, added tests for it --- sof-js/src/path.js | 2 +- tests/fn_boundary.json | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/sof-js/src/path.js b/sof-js/src/path.js index 53e73fc..259ee0f 100644 --- a/sof-js/src/path.js +++ b/sof-js/src/path.js @@ -42,7 +42,7 @@ function checkDate (date, type) { } if (!day) { if (month == "02") { - (year % 4) ? day = '28' : day = '29'; + (year % 4 || !(year % 100) && year % 400) ? day = '28' : day = '29'; } else if (["04", "06", "09", "11"].includes(month)) { day = '30'; diff --git a/tests/fn_boundary.json b/tests/fn_boundary.json index 87bfcaa..f7ade3a 100644 --- a/tests/fn_boundary.json +++ b/tests/fn_boundary.json @@ -248,6 +248,21 @@ "resourceType": "Patient", "id": "p6", "birthDate": "1994-05-18" + }, + { + "resourceType": "Patient", + "id": "p7", + "birthDate": "2100-02" + }, + { + "resourceType": "Patient", + "id": "p8", + "birthDate": "1900-02" + }, + { + "resourceType": "Patient", + "id": "p9", + "birthDate": "2024-02" } ], "tests": [ @@ -797,6 +812,18 @@ { "id": "p6", "date": "1994-05-18" + }, + { + "id": "p7", + "date": "2100-02-01" + }, + { + "id": "p8", + "date": "1900-02-01" + }, + { + "id": "p9", + "date": "2024-02-01" } ] }, @@ -846,6 +873,18 @@ { "id": "p6", "date": "1994-05-18" + }, + { + "id": "p7", + "date": "2100-02-28" + }, + { + "id": "p8", + "date": "1900-02-28" + }, + { + "id": "p9", + "date": "2024-02-29" } ] }, From a81e64511973c06b8ce269b8c78192d18960b452 Mon Sep 17 00:00:00 2001 From: Dmitrii Semenov Date: Thu, 16 May 2024 11:42:35 -0400 Subject: [PATCH 4/6] Preliminary code for decimal --- sof-js/src/path.js | 16 ++- tests/fn_boundary.json | 214 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 226 insertions(+), 4 deletions(-) diff --git a/sof-js/src/path.js b/sof-js/src/path.js index 259ee0f..c29b0ce 100644 --- a/sof-js/src/path.js +++ b/sof-js/src/path.js @@ -61,7 +61,12 @@ function lowBoundary(nodes) { return null; } const { name: type } = node.getTypeInfo(); - if (type === "time") { + if (type === "decimal") { + let integer = (node.data.toString().split(".")[0] || "").length; + let precision = (node.data.toString().split(".")[1] || "").length; + return (integer > 8 || precision > 8) ? null : parseFloat(node.data.toFixed(8)); + } + else if (type === "time") { const picoSeconds = (node.data.split(".")[1] || "").padEnd(9, 0); const time = new FP_Time(node.data.split(".")[0])._dateAtPrecision(2); return time.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds); @@ -96,7 +101,14 @@ function highBoundary(nodes) { return null; } const { name: type } = node.getTypeInfo(); - if (type === "time") { + if (type === "decimal") { + let integer = (node.data.toString().split(".")[0] || "").length; + let precision = (node.data.toString().split(".")[1] || "").length; + return (integer > 8 || precision > 8) + ? null + : parseFloat(node.data.toString().split(".")[0] + '.' + (node.data.toString().split(".")[1] || "").padEnd(8, 9)); + } + else if (type === "time") { const hasSeconds = node.data.split(":").length == 3; const picoSeconds = (node.data.split(".")[1] || "").padEnd(9, 9); const time = new FP_Time(node.data.split(".")[0])._dateAtPrecision(2); diff --git a/tests/fn_boundary.json b/tests/fn_boundary.json index f7ade3a..0be9962 100644 --- a/tests/fn_boundary.json +++ b/tests/fn_boundary.json @@ -219,6 +219,72 @@ "status": "final", "valueDateTime": "2007-03" }, + { + "resourceType": "Observation", + "id": "o26", + "code": { + "text": "code" + }, + "status": "final", + "valueQuantity": { + "value": 1.587 + } + }, + { + "resourceType": "Observation", + "id": "o27", + "code": { + "text": "code" + }, + "status": "final", + "valueQuantity": { + "value": 10235.66 + } + }, + { + "resourceType": "Observation", + "id": "o28", + "code": { + "text": "code" + }, + "status": "final", + "valueQuantity": { + "value": 353600.89123 + } + }, + { + "resourceType": "Observation", + "id": "o29", + "code": { + "text": "code" + }, + "status": "final", + "valueQuantity": { + "value": -353600.89123 + } + }, + { + "resourceType": "Observation", + "id": "o30", + "code": { + "text": "code" + }, + "status": "final", + "valueQuantity": { + "value": 0.1 + } + }, + { + "resourceType": "Observation", + "id": "o31", + "code": { + "text": "code" + }, + "status": "final", + "valueQuantity": { + "value": 123456789123.1234 + } + }, { "resourceType": "Patient", "id": "p1", @@ -291,7 +357,7 @@ "expect": [ { "id": "o1", - "decimal": 0.95 + "decimal": 1.00000000 }, { "id": "o2", @@ -388,6 +454,30 @@ { "id": "o25", "decimal": null + }, + { + "id": "o26", + "decimal": 1.58700000 + }, + { + "id": "o27", + "decimal": 10235.66000000 + }, + { + "id": "o28", + "decimal": 353600.89123000 + }, + { + "id": "o29", + "decimal": -353600.89123000 + }, + { + "id": "o30", + "decimal": 0.10000000 + }, + { + "id": "o31", + "decimal": null } ] }, @@ -416,7 +506,7 @@ "expect": [ { "id": "o1", - "decimal": 1.05 + "decimal": 1.99999999 }, { "id": "o2", @@ -513,6 +603,30 @@ { "id": "o25", "decimal": null + }, + { + "id": "o26", + "decimal": 1.58799999 + }, + { + "id": "o27", + "decimal": 10235.66999999 + }, + { + "id": "o28", + "decimal": 353600.89123999 + }, + { + "id": "o29", + "decimal": -353600.89123999 + }, + { + "id": "o30", + "decimal": 0.19999999 + }, + { + "id": "o31", + "decimal": null } ] }, @@ -638,6 +752,30 @@ { "id": "o25", "datetime": "2007-03-01T00:00:00.000000000+14:00" + }, + { + "id": "o26", + "datetime": null + }, + { + "id": "o27", + "datetime": null + }, + { + "id": "o28", + "datetime": null + }, + { + "id": "o29", + "datetime": null + }, + { + "id": "o30", + "datetime": null + }, + { + "id": "o31", + "datetime": null } ] }, @@ -763,6 +901,30 @@ { "id": "o25", "datetime": "2007-03-31T23:59:59.999999999-12:00" + }, + { + "id": "o26", + "datetime": null + }, + { + "id": "o27", + "datetime": null + }, + { + "id": "o28", + "datetime": null + }, + { + "id": "o29", + "datetime": null + }, + { + "id": "o30", + "datetime": null + }, + { + "id": "o31", + "datetime": null } ] }, @@ -1010,6 +1172,30 @@ { "id": "o25", "time": null + }, + { + "id": "o26", + "time": null + }, + { + "id": "o27", + "time": null + }, + { + "id": "o28", + "time": null + }, + { + "id": "o29", + "time": null + }, + { + "id": "o30", + "time": null + }, + { + "id": "o31", + "time": null } ] }, @@ -1135,6 +1321,30 @@ { "id": "o25", "time": null + }, + { + "id": "o26", + "time": null + }, + { + "id": "o27", + "time": null + }, + { + "id": "o28", + "time": null + }, + { + "id": "o29", + "time": null + }, + { + "id": "o30", + "time": null + }, + { + "id": "o31", + "time": null } ] } From 618dc183ce91aaca4f683610523c4b6d3f3309cd Mon Sep 17 00:00:00 2001 From: Dmitrii Semenov Date: Fri, 17 May 2024 15:12:20 -0400 Subject: [PATCH 5/6] Fixed 0 precesion for highBoundary --- sof-js/src/path.js | 2 +- tests/fn_boundary.json | 72 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/sof-js/src/path.js b/sof-js/src/path.js index c29b0ce..8a88c16 100644 --- a/sof-js/src/path.js +++ b/sof-js/src/path.js @@ -106,7 +106,7 @@ function highBoundary(nodes) { let precision = (node.data.toString().split(".")[1] || "").length; return (integer > 8 || precision > 8) ? null - : parseFloat(node.data.toString().split(".")[0] + '.' + (node.data.toString().split(".")[1] || "").padEnd(8, 9)); + : parseFloat(node.data.toString().split(".")[0] + '.' + (node.data.toString().split(".")[1] || "0").padEnd(8, 9)); } else if (type === "time") { const hasSeconds = node.data.split(":").length == 3; diff --git a/tests/fn_boundary.json b/tests/fn_boundary.json index 0be9962..1117113 100644 --- a/tests/fn_boundary.json +++ b/tests/fn_boundary.json @@ -285,6 +285,28 @@ "value": 123456789123.1234 } }, + { + "resourceType": "Observation", + "id": "o32", + "code": { + "text": "code" + }, + "status": "final", + "valueQuantity": { + "value": 0.007 + } + }, + { + "resourceType": "Observation", + "id": "o33", + "code": { + "text": "code" + }, + "status": "final", + "valueQuantity": { + "value": 15.080 + } + }, { "resourceType": "Patient", "id": "p1", @@ -478,6 +500,14 @@ { "id": "o31", "decimal": null + }, + { + "id": "o32", + "decimal": 0.00700000 + }, + { + "id": "o33", + "decimal": 15.08000000 } ] }, @@ -506,7 +536,7 @@ "expect": [ { "id": "o1", - "decimal": 1.99999999 + "decimal": 1.09999999 }, { "id": "o2", @@ -627,6 +657,14 @@ { "id": "o31", "decimal": null + }, + { + "id": "o32", + "decimal": 0.00799999 + }, + { + "id": "o33", + "decimal": 15.0809999 } ] }, @@ -776,6 +814,14 @@ { "id": "o31", "datetime": null + }, + { + "id": "o32", + "datetime": null + }, + { + "id": "o33", + "datetime": null } ] }, @@ -925,6 +971,14 @@ { "id": "o31", "datetime": null + }, + { + "id": "o32", + "datetime": null + }, + { + "id": "o33", + "datetime": null } ] }, @@ -1196,6 +1250,14 @@ { "id": "o31", "time": null + }, + { + "id": "o32", + "time": null + }, + { + "id": "o33", + "time": null } ] }, @@ -1345,6 +1407,14 @@ { "id": "o31", "time": null + }, + { + "id": "o32", + "time": null + }, + { + "id": "o33", + "time": null } ] } From 3f482f0f00dad41f18f641811d68709f621f48c2 Mon Sep 17 00:00:00 2001 From: Dmitrii Semenov Date: Wed, 19 Jun 2024 22:04:36 -0400 Subject: [PATCH 6/6] Changed tests for review feedback --- tests/fn_boundary.json | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/fn_boundary.json b/tests/fn_boundary.json index 1117113..f762733 100644 --- a/tests/fn_boundary.json +++ b/tests/fn_boundary.json @@ -109,7 +109,7 @@ "text": "code" }, "status": "final", - "valueDateTime": "2010-10-10T15:21" + "valueDateTime": "1900-02" }, { "resourceType": "Observation", @@ -479,35 +479,35 @@ }, { "id": "o26", - "decimal": 1.58700000 + "decimal": 1.58700000000000000 }, { "id": "o27", - "decimal": 10235.66000000 + "decimal": 10235.66000000000000000 }, { "id": "o28", - "decimal": 353600.89123000 + "decimal": 353600.89123000000000000 }, { "id": "o29", - "decimal": -353600.89123000 + "decimal": -353600.89123000000000000 }, { "id": "o30", - "decimal": 0.10000000 + "decimal": 0.10000000000000000 }, { "id": "o31", - "decimal": null + "decimal": 123456789123.12340000000000000 }, { "id": "o32", - "decimal": 0.00700000 + "decimal": 0.00700000000000000 }, { "id": "o33", - "decimal": 15.08000000 + "decimal": 15.08000000000000000 } ] }, @@ -636,35 +636,35 @@ }, { "id": "o26", - "decimal": 1.58799999 + "decimal": 1.58799999999999999 }, { "id": "o27", - "decimal": 10235.66999999 + "decimal": 10235.66999999999999999 }, { "id": "o28", - "decimal": 353600.89123999 + "decimal": 353600.89123999999999999 }, { "id": "o29", - "decimal": -353600.89123999 + "decimal": -353600.89123999999999999 }, { "id": "o30", - "decimal": 0.19999999 + "decimal": 0.19999999999999999 }, { "id": "o31", - "decimal": null + "decimal": 123456789123.12349999999999999 }, { "id": "o32", - "decimal": 0.00799999 + "decimal": 0.00799999999999999 }, { "id": "o33", - "decimal": 15.0809999 + "decimal": 15.08099999999999999 } ] }, @@ -741,7 +741,7 @@ }, { "id": "o13", - "datetime": "2010-10-10T15:21:00.000000000+14:00" + "datetime": "1900-02-01T00:00:00.000000000+14:00" }, { "id": "o14", @@ -898,7 +898,7 @@ }, { "id": "o13", - "datetime": "2010-10-10T15:21:59.999999999-12:00" + "datetime": "1900-02-28T23:59:59.999999999-12:00" }, { "id": "o14",