Skip to content

Commit 1bf5ad1

Browse files
committed
1 parent 214a25b commit 1bf5ad1

8 files changed

Lines changed: 4480 additions & 40 deletions

File tree

examples/browser/cql4browsers.js

Lines changed: 107 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,28 @@ class DateTime extends AbstractDate {
14071407
}
14081408
return reduced;
14091409
}
1410+
highBoundary(precision = null) {
1411+
const precisionMap = this.isTime() ? PRECISION_TIME_VALUE_MAP : PRECISION_DATETIME_VALUE_MAP;
1412+
const unit = precisionMap.get(precision ?? (this.isTime() ? 9 : 17));
1413+
if (unit == null) {
1414+
return null;
1415+
}
1416+
const boundary = DateTime.fromLuxonDateTime(this.toLuxonUncertainty().high);
1417+
return this.isTime()
1418+
? boundary.getTime().reducedPrecision(unit)
1419+
: boundary.reducedPrecision(unit);
1420+
}
1421+
lowBoundary(precision = null) {
1422+
const precisionMap = this.isTime() ? PRECISION_TIME_VALUE_MAP : PRECISION_DATETIME_VALUE_MAP;
1423+
const unit = precisionMap.get(precision ?? (this.isTime() ? 9 : 17));
1424+
if (unit == null) {
1425+
return null;
1426+
}
1427+
const boundary = DateTime.fromLuxonDateTime(this.toLuxonUncertainty().low);
1428+
return this.isTime()
1429+
? boundary.getTime().reducedPrecision(unit)
1430+
: boundary.reducedPrecision(unit);
1431+
}
14101432
}
14111433
exports.DateTime = DateTime;
14121434
DateTime.Unit = {
@@ -1619,6 +1641,20 @@ class Date extends AbstractDate {
16191641
}
16201642
return reduced;
16211643
}
1644+
highBoundary(precision = null) {
1645+
const unit = PRECISION_DATETIME_VALUE_MAP.get(precision ?? 8);
1646+
if (unit == null || !Date.FIELDS.includes(unit)) {
1647+
return null;
1648+
}
1649+
return Date.fromLuxonDateTime(this.toLuxonUncertainty().high).reducedPrecision(unit);
1650+
}
1651+
lowBoundary(precision = null) {
1652+
const unit = PRECISION_DATETIME_VALUE_MAP.get(precision ?? 8);
1653+
if (unit == null || !Date.FIELDS.includes(unit)) {
1654+
return null;
1655+
}
1656+
return Date.fromLuxonDateTime(this.toLuxonUncertainty().low).reducedPrecision(unit);
1657+
}
16221658
}
16231659
exports.Date = Date;
16241660
Date.Unit = { YEAR: 'year', MONTH: 'month', WEEK: 'week', DAY: 'day' };
@@ -1629,25 +1665,26 @@ exports.MIN_DATE_VALUE = Date.parse(limits_1.MIN_DATE_VALUE_STRING);
16291665
exports.MAX_DATE_VALUE = Date.parse(limits_1.MAX_DATE_VALUE_STRING);
16301666
exports.MIN_TIME_VALUE = DateTime.parse(limits_1.MIN_TIME_VALUE_STRING)?.getTime();
16311667
exports.MAX_TIME_VALUE = DateTime.parse(limits_1.MAX_TIME_VALUE_STRING)?.getTime();
1632-
const DATETIME_PRECISION_VALUE_MAP = (() => {
1633-
const dtpvMap = new Map();
1634-
dtpvMap.set(DateTime.Unit.YEAR, 4);
1635-
dtpvMap.set(DateTime.Unit.MONTH, 6);
1636-
dtpvMap.set(DateTime.Unit.DAY, 8);
1637-
dtpvMap.set(DateTime.Unit.HOUR, 10);
1638-
dtpvMap.set(DateTime.Unit.MINUTE, 12);
1639-
dtpvMap.set(DateTime.Unit.SECOND, 14);
1640-
dtpvMap.set(DateTime.Unit.MILLISECOND, 17);
1641-
return dtpvMap;
1642-
})();
1643-
const TIME_PRECISION_VALUE_MAP = (() => {
1644-
const tpvMap = new Map();
1645-
tpvMap.set(DateTime.Unit.HOUR, 2);
1646-
tpvMap.set(DateTime.Unit.MINUTE, 4);
1647-
tpvMap.set(DateTime.Unit.SECOND, 6);
1648-
tpvMap.set(DateTime.Unit.MILLISECOND, 9);
1649-
return tpvMap;
1650-
})();
1668+
const DATETIME_PRECISION_VALUE_MAP = new Map([
1669+
[DateTime.Unit.YEAR, 4],
1670+
[DateTime.Unit.MONTH, 6],
1671+
[DateTime.Unit.DAY, 8],
1672+
[DateTime.Unit.HOUR, 10],
1673+
[DateTime.Unit.MINUTE, 12],
1674+
[DateTime.Unit.SECOND, 14],
1675+
[DateTime.Unit.MILLISECOND, 17]
1676+
]);
1677+
const PRECISION_DATETIME_VALUE_MAP = invertMap(DATETIME_PRECISION_VALUE_MAP);
1678+
const TIME_PRECISION_VALUE_MAP = new Map([
1679+
[DateTime.Unit.HOUR, 2],
1680+
[DateTime.Unit.MINUTE, 4],
1681+
[DateTime.Unit.SECOND, 6],
1682+
[DateTime.Unit.MILLISECOND, 9]
1683+
]);
1684+
const PRECISION_TIME_VALUE_MAP = invertMap(TIME_PRECISION_VALUE_MAP);
1685+
function invertMap(map) {
1686+
return new Map([...map.entries()].map(([k, v]) => [v, k]));
1687+
}
16511688
function compareWithDefaultResult(a, b, defaultResult) {
16521689
// return false there is a type mismatch
16531690
if ((!a.isDate || !b.isDate) && (!a.isDateTime || !b.isDateTime)) {
@@ -3484,7 +3521,7 @@ var __importStar = (this && this.__importStar) || (function () {
34843521
};
34853522
})();
34863523
Object.defineProperty(exports, "__esModule", { value: true });
3487-
exports.Predecessor = exports.Successor = exports.MaxValue = exports.MinValue = exports.Power = exports.Log = exports.Exp = exports.Ln = exports.Round = exports.Negate = exports.Abs = exports.Truncate = exports.Floor = exports.Ceiling = exports.Modulo = exports.TruncatedDivide = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = void 0;
3524+
exports.Predecessor = exports.Successor = exports.LowBoundary = exports.HighBoundary = exports.MaxValue = exports.MinValue = exports.Power = exports.Log = exports.Exp = exports.Ln = exports.Round = exports.Negate = exports.Abs = exports.Truncate = exports.Floor = exports.Ceiling = exports.Modulo = exports.TruncatedDivide = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = void 0;
34883525
const expression_1 = require("./expression");
34893526
const MathUtil = __importStar(require("../util/math"));
34903527
const quantity_1 = require("../datatypes/quantity");
@@ -3901,6 +3938,56 @@ MaxValue.MAX_VALUES = {
39013938
[elmTypes_1.ELM_DATE_TYPE]: datetime_1.MAX_DATE_VALUE,
39023939
[elmTypes_1.ELM_TIME_TYPE]: datetime_1.MAX_TIME_VALUE
39033940
};
3941+
function decimalBoundary(value, precision, boundary) {
3942+
precision ?? (precision = 8);
3943+
if (precision < 0 || precision > 8) {
3944+
return null;
3945+
}
3946+
const [whole = '0', decimalPart = ''] = value.toString().split('.');
3947+
// we can't do much w/ decimals that are using scientific notation
3948+
if (decimalPart.includes('e')) {
3949+
return value;
3950+
}
3951+
const filledDecimalPart = decimalPart
3952+
.padEnd(precision, boundary === 'high' ? '9' : '0')
3953+
.slice(0, precision);
3954+
return Number(`${whole}.${filledDecimalPart}`);
3955+
}
3956+
function boundary(value, precision, boundary) {
3957+
if (value == null) {
3958+
return null;
3959+
}
3960+
if (typeof value === 'number') {
3961+
return decimalBoundary(value, precision, boundary);
3962+
}
3963+
if (boundary === 'high') {
3964+
return value.highBoundary?.(precision);
3965+
}
3966+
else if (boundary === 'low') {
3967+
return value.lowBoundary?.(precision);
3968+
}
3969+
return null;
3970+
}
3971+
class HighBoundary extends expression_1.Expression {
3972+
constructor(json) {
3973+
super(json);
3974+
}
3975+
async exec(ctx) {
3976+
const [value, precision] = await this.execArgs(ctx);
3977+
return boundary(value, precision, 'high');
3978+
}
3979+
}
3980+
exports.HighBoundary = HighBoundary;
3981+
class LowBoundary extends expression_1.Expression {
3982+
constructor(json) {
3983+
super(json);
3984+
}
3985+
async exec(ctx) {
3986+
const [value, precision] = await this.execArgs(ctx);
3987+
return boundary(value, precision, 'low');
3988+
}
3989+
}
3990+
exports.LowBoundary = LowBoundary;
39043991
class Successor extends expression_1.Expression {
39053992
constructor(json) {
39063993
super(json);

src/datatypes/datetime.ts

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,32 @@ export class DateTime extends AbstractDate {
10051005
}
10061006
return reduced;
10071007
}
1008+
1009+
highBoundary(precision: number | null = null) {
1010+
const precisionMap = this.isTime() ? PRECISION_TIME_VALUE_MAP : PRECISION_DATETIME_VALUE_MAP;
1011+
const unit = precisionMap.get(precision ?? (this.isTime() ? 9 : 17));
1012+
if (unit == null) {
1013+
return null;
1014+
}
1015+
1016+
const boundary = DateTime.fromLuxonDateTime(this.toLuxonUncertainty().high);
1017+
return this.isTime()
1018+
? boundary.getTime().reducedPrecision(unit)
1019+
: boundary.reducedPrecision(unit);
1020+
}
1021+
1022+
lowBoundary(precision: number | null = null) {
1023+
const precisionMap = this.isTime() ? PRECISION_TIME_VALUE_MAP : PRECISION_DATETIME_VALUE_MAP;
1024+
const unit = precisionMap.get(precision ?? (this.isTime() ? 9 : 17));
1025+
if (unit == null) {
1026+
return null;
1027+
}
1028+
1029+
const boundary = DateTime.fromLuxonDateTime(this.toLuxonUncertainty().low);
1030+
return this.isTime()
1031+
? boundary.getTime().reducedPrecision(unit)
1032+
: boundary.reducedPrecision(unit);
1033+
}
10081034
}
10091035

10101036
export class Date extends AbstractDate {
@@ -1228,6 +1254,24 @@ export class Date extends AbstractDate {
12281254
}
12291255
return reduced;
12301256
}
1257+
1258+
highBoundary(precision: number | null = null) {
1259+
const unit = PRECISION_DATETIME_VALUE_MAP.get(precision ?? 8);
1260+
if (unit == null || !Date.FIELDS.includes(unit)) {
1261+
return null;
1262+
}
1263+
1264+
return Date.fromLuxonDateTime(this.toLuxonUncertainty().high).reducedPrecision(unit);
1265+
}
1266+
1267+
lowBoundary(precision: number | null = null) {
1268+
const unit = PRECISION_DATETIME_VALUE_MAP.get(precision ?? 8);
1269+
if (unit == null || !Date.FIELDS.includes(unit)) {
1270+
return null;
1271+
}
1272+
1273+
return Date.fromLuxonDateTime(this.toLuxonUncertainty().low).reducedPrecision(unit);
1274+
}
12311275
}
12321276

12331277
export const MIN_DATETIME_VALUE = DateTime.parse(MIN_DATETIME_VALUE_STRING);
@@ -1237,26 +1281,30 @@ export const MAX_DATE_VALUE = Date.parse(MAX_DATE_VALUE_STRING);
12371281
export const MIN_TIME_VALUE = DateTime.parse(MIN_TIME_VALUE_STRING)?.getTime();
12381282
export const MAX_TIME_VALUE = DateTime.parse(MAX_TIME_VALUE_STRING)?.getTime();
12391283

1240-
const DATETIME_PRECISION_VALUE_MAP = (() => {
1241-
const dtpvMap = new Map();
1242-
dtpvMap.set(DateTime.Unit.YEAR, 4);
1243-
dtpvMap.set(DateTime.Unit.MONTH, 6);
1244-
dtpvMap.set(DateTime.Unit.DAY, 8);
1245-
dtpvMap.set(DateTime.Unit.HOUR, 10);
1246-
dtpvMap.set(DateTime.Unit.MINUTE, 12);
1247-
dtpvMap.set(DateTime.Unit.SECOND, 14);
1248-
dtpvMap.set(DateTime.Unit.MILLISECOND, 17);
1249-
return dtpvMap;
1250-
})();
1251-
1252-
const TIME_PRECISION_VALUE_MAP = (() => {
1253-
const tpvMap = new Map();
1254-
tpvMap.set(DateTime.Unit.HOUR, 2);
1255-
tpvMap.set(DateTime.Unit.MINUTE, 4);
1256-
tpvMap.set(DateTime.Unit.SECOND, 6);
1257-
tpvMap.set(DateTime.Unit.MILLISECOND, 9);
1258-
return tpvMap;
1259-
})();
1284+
const DATETIME_PRECISION_VALUE_MAP = new Map<string | null, number>([
1285+
[DateTime.Unit.YEAR, 4],
1286+
[DateTime.Unit.MONTH, 6],
1287+
[DateTime.Unit.DAY, 8],
1288+
[DateTime.Unit.HOUR, 10],
1289+
[DateTime.Unit.MINUTE, 12],
1290+
[DateTime.Unit.SECOND, 14],
1291+
[DateTime.Unit.MILLISECOND, 17]
1292+
]);
1293+
1294+
const PRECISION_DATETIME_VALUE_MAP = invertMap(DATETIME_PRECISION_VALUE_MAP);
1295+
1296+
const TIME_PRECISION_VALUE_MAP = new Map<string | null, number>([
1297+
[DateTime.Unit.HOUR, 2],
1298+
[DateTime.Unit.MINUTE, 4],
1299+
[DateTime.Unit.SECOND, 6],
1300+
[DateTime.Unit.MILLISECOND, 9]
1301+
]);
1302+
1303+
const PRECISION_TIME_VALUE_MAP = invertMap(TIME_PRECISION_VALUE_MAP);
1304+
1305+
function invertMap<K, V>(map: Map<K, V>): Map<V, K> {
1306+
return new Map([...map.entries()].map(([k, v]) => [v, k]));
1307+
}
12601308

12611309
function compareWithDefaultResult(a: any, b: any, defaultResult: any) {
12621310
// return false there is a type mismatch

src/elm/arithmetic.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Uncertainty } from '../datatypes/uncertainty';
55
import { Context } from '../runtime/context';
66
import { build } from './builder';
77
import {
8+
Date as CQLDate,
89
DateTime,
910
MAX_DATE_VALUE,
1011
MAX_DATETIME_VALUE,
@@ -469,6 +470,69 @@ export class MaxValue extends Expression {
469470
}
470471
}
471472

473+
function decimalBoundary(value: number, precision: number | null, boundary: 'high' | 'low') {
474+
precision ??= 8;
475+
if (precision < 0 || precision > 8) {
476+
return null;
477+
}
478+
479+
const [whole = '0', decimalPart = ''] = value.toString().split('.');
480+
// we can't do much w/ decimals that are using scientific notation
481+
if (decimalPart.includes('e')) {
482+
return value;
483+
}
484+
485+
const filledDecimalPart = decimalPart
486+
.padEnd(precision, boundary === 'high' ? '9' : '0')
487+
.slice(0, precision);
488+
489+
return Number(`${whole}.${filledDecimalPart}`);
490+
}
491+
492+
function boundary(
493+
value: CQLDate | DateTime | number,
494+
precision: number | null,
495+
boundary: 'high' | 'low'
496+
) {
497+
if (value == null) {
498+
return null;
499+
}
500+
501+
if (typeof value === 'number') {
502+
return decimalBoundary(value, precision, boundary);
503+
}
504+
505+
if (boundary === 'high') {
506+
return value.highBoundary?.(precision);
507+
} else if (boundary === 'low') {
508+
return value.lowBoundary?.(precision);
509+
}
510+
511+
return null;
512+
}
513+
514+
export class HighBoundary extends Expression {
515+
constructor(json: any) {
516+
super(json);
517+
}
518+
519+
async exec(ctx: Context) {
520+
const [value, precision] = await this.execArgs(ctx);
521+
return boundary(value, precision, 'high');
522+
}
523+
}
524+
525+
export class LowBoundary extends Expression {
526+
constructor(json: any) {
527+
super(json);
528+
}
529+
530+
async exec(ctx: Context) {
531+
const [value, precision] = await this.execArgs(ctx);
532+
return boundary(value, precision, 'low');
533+
}
534+
}
535+
472536
export class Successor extends Expression {
473537
constructor(json: any) {
474538
super(json);

0 commit comments

Comments
 (0)