Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use passed in executionDateTime timeZoneOffset when available #313

Merged
merged 5 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions examples/browser/cql4browsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -1500,12 +1500,12 @@ class Date extends AbstractDate {
}
return str;
}
getDateTime() {
getDateTime(timeZoneOffset) {
// from the spec: the result will be a DateTime with the time components unspecified,
// except for the timezone offset, which will be set to the timezone offset of the evaluation
// request timestamp. (this last part is achieved by just not passing in timezone offset)
// request timestamp. (this last part is achieved by passing in the timeZoneOffset from the context)
if (this.year != null && this.month != null && this.day != null) {
return new DateTime(this.year, this.month, this.day, null, null, null, null);
return new DateTime(this.year, this.month, this.day, null, null, null, null, timeZoneOffset);
// from spec: no component may be specified at a precision below an unspecified precision.
// For example, hour may be null, but if it is, minute, second, and millisecond must all be null as well.
}
Expand Down Expand Up @@ -3920,29 +3920,31 @@ class CalculateAgeAt extends expression_1.Expression {
}
async exec(ctx) {
const [birthDate, asOf] = await this.execArgs(ctx);
return calculateAge(this.precision, birthDate, asOf);
const timeZoneOffset = ctx.getExecutionDateTime().timezoneOffset;
return calculateAge(this.precision, birthDate, asOf, timeZoneOffset);
}
}
exports.CalculateAgeAt = CalculateAgeAt;
/**
* Calculates the age as of a certain date based on the passed in birth date. If the asOf date is
* a Date, then birth date will be converted to a Date (if necessary) before calculation is
* performed. If the asOf is a DateTime, then the birth date will be convertedto a DateTime (if
* performed. If the asOf is a DateTime, then the birth date will be converted to a DateTime (if
* necessary) before calculation is performed. The result is an integer or uncertainty specifying
* the age in the requested precision units.
* @param precision - the precision as specified in the ELM (e.g., Year, Month, Week, etc.)
* @param birthDate - the birth date to use for age calculations (may be Date or DateTime)
* @param asOf - the date on which the age should be calculated (may be Date or DateTime)
* @param timeZoneOffset - the passed in timeZoneOffset (if it exists)
* @returns the age as an integer or uncertainty in the requested precision units
*/
function calculateAge(precision, birthDate, asOf) {
function calculateAge(precision, birthDate, asOf, timeZoneOffset) {
if (birthDate != null && asOf != null) {
// Ensure we use like types (Date or DateTime) based on asOf type
if (asOf.isDate && birthDate.isDateTime) {
birthDate = birthDate.getDate();
}
else if (asOf.isDateTime && birthDate.isDate) {
birthDate = birthDate.getDateTime();
birthDate = birthDate.getDateTime(timeZoneOffset);
}
const result = birthDate.durationBetween(asOf, precision.toLowerCase());
if (result === null || result === void 0 ? void 0 : result.isPoint()) {
Expand Down Expand Up @@ -7311,7 +7313,8 @@ class ToDateTime extends expression_1.Expression {
return null;
}
else if (arg.isDate) {
return arg.getDateTime();
const timezoneOffset = ctx.getExecutionDateTime().timezoneOffset;
return arg.getDateTime(timezoneOffset);
}
else {
return datetime_1.DateTime.parse(arg.toString());
Expand Down
54 changes: 36 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/datatypes/datetime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1199,12 +1199,12 @@ export class Date extends AbstractDate {
return str;
}

getDateTime() {
getDateTime(timeZoneOffset?: number | null) {
// from the spec: the result will be a DateTime with the time components unspecified,
// except for the timezone offset, which will be set to the timezone offset of the evaluation
// request timestamp. (this last part is achieved by just not passing in timezone offset)
// request timestamp. (this last part is achieved by passing in the timeZoneOffset from the context)
if (this.year != null && this.month != null && this.day != null) {
return new DateTime(this.year, this.month, this.day, null, null, null, null);
return new DateTime(this.year, this.month, this.day, null, null, null, null, timeZoneOffset);
// from spec: no component may be specified at a precision below an unspecified precision.
// For example, hour may be null, but if it is, minute, second, and millisecond must all be null as well.
} else {
Expand Down
11 changes: 7 additions & 4 deletions src/elm/clinical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,32 +279,35 @@ export class CalculateAgeAt extends Expression {

async exec(ctx: Context) {
const [birthDate, asOf] = await this.execArgs(ctx);
return calculateAge(this.precision, birthDate, asOf);
const timeZoneOffset = ctx.getExecutionDateTime().timezoneOffset;
return calculateAge(this.precision, birthDate, asOf, timeZoneOffset);
}
}

/**
* Calculates the age as of a certain date based on the passed in birth date. If the asOf date is
* a Date, then birth date will be converted to a Date (if necessary) before calculation is
* performed. If the asOf is a DateTime, then the birth date will be convertedto a DateTime (if
* performed. If the asOf is a DateTime, then the birth date will be converted to a DateTime (if
* necessary) before calculation is performed. The result is an integer or uncertainty specifying
* the age in the requested precision units.
* @param precision - the precision as specified in the ELM (e.g., Year, Month, Week, etc.)
* @param birthDate - the birth date to use for age calculations (may be Date or DateTime)
* @param asOf - the date on which the age should be calculated (may be Date or DateTime)
* @param timeZoneOffset - the passed in timeZoneOffset (if it exists)
elsaperelli marked this conversation as resolved.
Show resolved Hide resolved
* @returns the age as an integer or uncertainty in the requested precision units
*/
function calculateAge(
precision: string,
birthDate?: dt.Date | dt.DateTime,
asOf?: dt.Date | dt.DateTime
asOf?: dt.Date | dt.DateTime,
timeZoneOffset?: number | null
) {
if (birthDate != null && asOf != null) {
// Ensure we use like types (Date or DateTime) based on asOf type
if (asOf.isDate && birthDate.isDateTime) {
birthDate = (birthDate as dt.DateTime).getDate();
} else if (asOf.isDateTime && birthDate.isDate) {
birthDate = birthDate.getDateTime();
birthDate = birthDate.getDateTime(timeZoneOffset);
}
const result = birthDate.durationBetween(asOf, precision.toLowerCase());
if (result?.isPoint()) {
Expand Down
3 changes: 2 additions & 1 deletion src/elm/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ export class ToDateTime extends Expression {
if (arg == null) {
return null;
} else if (arg.isDate) {
return arg.getDateTime();
const timezoneOffset = ctx.getExecutionDateTime().timezoneOffset;
return arg.getDateTime(timezoneOffset);
} else {
return DateTime.parse(arg.toString());
}
Expand Down
29 changes: 29 additions & 0 deletions test/datatypes/date-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -838,3 +838,32 @@ describe('Date.getPrecisionValue', () => {
Date.parse('2012-10-25').getPrecisionValue().should.equal(8);
});
});

describe('Date.getDateTime', () => {
it('should return a DateTime that has the passed in timeZoneOffset', () => {
const d = new Date(2000, 12, 1);
const dateTime = d.getDateTime(2);
dateTime.year.should.equal(2000);
dateTime.month.should.equal(12);
dateTime.day.should.equal(1);
dateTime.timezoneOffset.should.equal(2);
});

it('should return a DateTime with a timeZoneOffset when one is not passed in', () => {
const d = new Date(2000, 12, 1);
const dateTime = d.getDateTime();
dateTime.year.should.equal(2000);
dateTime.month.should.equal(12);
dateTime.day.should.equal(1);
dateTime.timezoneOffset.should.equal((new jsDate().getTimezoneOffset() / 60) * -1);
});

it('should return a DateTime without a timeZoneOffset when a null timeZoneOffset is passed in', () => {
const d = new Date(2000, 12, 1);
const dateTime = d.getDateTime(null);
dateTime.year.should.equal(2000);
dateTime.month.should.equal(12);
dateTime.day.should.equal(1);
should(dateTime.timezoneOffset).be.null();
});
});
51 changes: 50 additions & 1 deletion test/elm/clinical/clinical-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import setup from '../../setup';
const vsets = require('./valuesets');
import * as DT from '../../../src/datatypes/datatypes';
import { Uncertainty } from '../../../src/datatypes/uncertainty';
const { p1, p2, p3, p4, p5 } = require('./patients');
const { p1, p2, p3, p4, p5, p6, p7 } = require('./patients');
import { PatientSource } from '../../../src/cql-patient';
const data = require('./data');

Expand Down Expand Up @@ -500,4 +500,53 @@ describe('CalculateAgeAt', () => {
setup(this, data, [p3]);
(await this.calculateAgeInYearsDateArg.exec(this.ctx)).should.eql(new Uncertainty(17, 18));
});

it('should convert date to DateTime, give 18 since the timeZoneOffset is 0', async function () {
setup(this, data, [p6]);
// Execute these tests as if it is 2024-01-01 at 00:00:00.0000 GMT
this.ctx.executionDateTime = new DT.DateTime(2024, 1, 1, 0, 0, 0, 0, 0);
// Change it to the Date class
this.ctx.patient.birthDate = new DT.Date(2005, 12, 31);

(await this.ageAtJanuary1DateTimeArg.exec(this.ctx)).should.equal(18);
});

it('should convert date to DateTime, give 18 since the timeZoneOffset on the parent is 0', async function () {
setup(this, data, [p6]);
this.ctx.executionDateTime = undefined;
// Execute these tests as if it is 2024-01-01 at 00:00:00.0000 GMT
this.ctx.parent.getExecutionDateTime = () => {
return new DT.DateTime(2024, 1, 1, 0, 0, 0, 0, 0);
};
// Change it to the Date class
this.ctx.patient.birthDate = new DT.Date(2005, 12, 31);

(await this.ageAtJanuary1DateTimeArg.exec(this.ctx)).should.equal(18);
});

it('should convert date to DateTime, but give an Uncertainty since the timeZoneOffset is not passed in and therefore equal to the timezone of the evaluation request timestamp', async function () {
cmoesel marked this conversation as resolved.
Show resolved Hide resolved
setup(this, data, [p6]);
// Change it to the Date class
this.ctx.patient.birthDate = new DT.Date(2005, 12, 31);

(await this.ageAtJanuary1DateTimeArg.exec(this.ctx)).should.eql(new Uncertainty(17, 18));
});

it('should give 18 since the timeZoneOffset is 0 and both birthDate and asOf are DateTimes', async function () {
setup(this, data, [p7]);
// Execute these tests as if it is 2024-01-01 at 00:00:00.0000 GMT
this.ctx.executionDateTime = new DT.DateTime(2024, 1, 1, 0, 0, 0, 0, 0);

(await this.ageAtJanuary1DateTimeArg.exec(this.ctx)).should.equal(18);
});

it('should give 18 since the timeZoneOffset is 0 and both birthDate and asOf are Dates', async function () {
setup(this, data, [p6]);
// Execute these tests as if it is 2024-01-01 at 00:00:00.0000 GMT
this.ctx.executionDateTime = new DT.DateTime(2024, 1, 1, 0, 0, 0, 0, 0);
// Change it to the Date class
this.ctx.patient.birthDate = new DT.Date(2005, 12, 31);

(await this.ageAtJanuary1DateArg.exec(this.ctx)).should.equal(18);
});
});
4 changes: 3 additions & 1 deletion test/elm/clinical/data.cql
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,6 @@ define AgeAt1975: AgeInYearsAt(DateTime(1975))
define AgeInYearsDateTimeArg: AgeInYearsAt(DateTime(2012, 12, 1))
define CalculateAgeInYearsDateTimeArg: CalculateAgeInYearsAt(@1994-12-01T23:59:00.000+00:00, DateTime(2012, 12, 1))
define AgeInYearsDateArg: AgeInYearsAt(Date(2012, 12, 1))
define CalculateAgeInYearsDateArg: CalculateAgeInYearsAt(@1994-12-01T23:59:00.000+00:00, Date(2012, 12, 1))
define CalculateAgeInYearsDateArg: CalculateAgeInYearsAt(@1994-12-01T23:59:00.000+00:00, Date(2012, 12, 1))
define AgeAtJanuary1DateTimeArg: AgeInYearsAt(@2024-01-01T00:00:00.000+00:00)
define AgeAtJanuary1DateArg: AgeInYearsAt(Date(2024, 1, 1))
Loading
Loading