Skip to content

Commit

Permalink
[PORT] Add getNextViableDate, getPreviousViableDate, getNextViableTim…
Browse files Browse the repository at this point in the history
…e, getPreviousViableTime pre-built functions (#2601)

* init

* init

* init

* init

* init

* init

* init

* init

* add comments

* refine

* refine

* fix error

* refine

* small fix

* retrigger

* add date functions

* solve conflicts

* retrigger

* fix incorrect function description

* fix type signatures

Co-authored-by: hond <[email protected]>
  • Loading branch information
cosmicshuai and Danieladu authored Aug 7, 2020
1 parent a8b0dc9 commit 87c0be3
Show file tree
Hide file tree
Showing 11 changed files with 405 additions and 2 deletions.
2 changes: 0 additions & 2 deletions libraries/adaptive-expressions/src/builtinFunctions/concat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,10 @@ export class Concat extends ExpressionEvaluator {
});
}


private static commonStringify(input: any): string {
if (input === null || input === undefined) {
return '';
}

if (Array.isArray(input)) {
return input.toString();
} else if (typeof input === 'object') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* @module adaptive-expressions
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { ExpressionEvaluator } from '../expressionEvaluator';
import { Expression } from '../expression';
import { ReturnType } from '../returnType';
import { ExpressionType } from '../expressionType';
import { FunctionUtils } from '../functionUtils';
import { MemoryInterface } from '../memory/memoryInterface';
import { Options } from '../options';
import { TimeZoneConverter } from '../timeZoneConverter';
import { tz } from 'moment-timezone';
import moment from 'moment';
import {TimexProperty} from '@microsoft/recognizers-text-data-types-timex-expression';

/**
* Return the next viable date of a timex expression based on the current date and user's timezone.
*/
export class GetNextViableDate extends ExpressionEvaluator {
public constructor(){
super(ExpressionType.GetNextViableDate, GetNextViableDate.evaluator, ReturnType.String, FunctionUtils.validateUnaryOrBinaryString);
}

private static evaluator(expr: Expression, state: MemoryInterface, options: Options): {value: any; error: string} {
let parsed: TimexProperty;
let value: string;
let error: string;
let args: any[];
const currentTime = moment(new Date().toISOString());
let validYear = 0;
let validMonth = 0;
let validDay = 0;
let convertedDateTime: moment.Moment;
({args, error} = FunctionUtils.evaluateChildren(expr, state, options));
if (!error) {
({timexProperty: parsed, error: error} = FunctionUtils.parseTimexProperty(args[0]));
}

if (parsed && !error) {
if (parsed.year || !parsed.month || !parsed.dayOfMonth) {
error = `${args[0]} must be a timex string which only contains month and day-of-month, for example: 'XXXX-10-31'.`;
}
}

if (!error) {
if (args.length === 2 && typeof args[1] === 'string') {
const timeZone: string = TimeZoneConverter .windowsToIana(args[1]);
if (!TimeZoneConverter.verifyTimeZoneStr(timeZone)) {
error = `${args[1]} is not a valid timezone`;
}

if (!error) {
convertedDateTime = tz(currentTime.utc(), timeZone);
}
} else {
convertedDateTime = currentTime.utc();
}
}

if (!error) {
const year = convertedDateTime.year();
const month = convertedDateTime.month() + 1;
const dayOfMonth = convertedDateTime.date();

if (parsed.month > month || (parsed.month === month && parsed.dayOfMonth >= dayOfMonth)) {
validYear = year;
} else {
validYear = year + 1;
}

validMonth = parsed.month;
validDay = parsed.dayOfMonth;

if (validMonth === 2 && validDay === 29) {
while (!GetNextViableDate.leapYear(validYear)) {
validYear += 1;
}
}
}

value = TimexProperty.fromDate(new Date(validYear, validMonth - 1, validDay)).timex;
return {value, error};
}

private static leapYear(year: number): boolean
{
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* @module adaptive-expressions
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { ExpressionEvaluator } from '../expressionEvaluator';
import { Expression } from '../expression';
import { ReturnType } from '../returnType';
import { ExpressionType } from '../expressionType';
import { FunctionUtils } from '../functionUtils';
import { MemoryInterface } from '../memory/memoryInterface';
import { Options } from '../options';
import { TimeZoneConverter } from '../timeZoneConverter';
import { tz } from 'moment-timezone';
import moment from 'moment';
import {TimexProperty, Time} from '@microsoft/recognizers-text-data-types-timex-expression';
/**
* Return the next viable time of a timex expression based on the current time and user's timezone.
*/
export class GetNextViableTime extends ExpressionEvaluator {
public constructor(){
super(ExpressionType.GetNextViableTime, GetNextViableTime.evaluator, ReturnType.String, FunctionUtils.validateUnaryOrBinaryString);
}

private static evaluator(expr: Expression, state: MemoryInterface, options: Options): {value: any; error: string} {
let parsed: TimexProperty;
let value: string;
let error: string;
let args: any[];
const currentTime = moment(new Date().toISOString());
let validHour = 0;
let validMinute = 0;
let validSecond = 0;
let convertedDateTime: moment.Moment;
const formatRegex = /TXX:[0-5][0-9]:[0-5][0-9]/g;
({args, error} = FunctionUtils.evaluateChildren(expr, state, options));
if(!error) {
if (!formatRegex.test(args[0] as string)) {
error = `${args[0]} must be a timex string which only contains minutes and seconds, for example: 'TXX:15:28'`
}
}

if (!error) {
if (args.length === 2 && typeof args[1] === 'string') {
const timeZone: string = TimeZoneConverter .windowsToIana(args[1]);
if (!TimeZoneConverter.verifyTimeZoneStr(timeZone)) {
error = `${args[1]} is not a valid timezone`;
}

if (!error) {
convertedDateTime = tz(currentTime.utc(), timeZone);
}
} else {
convertedDateTime = currentTime.utc();
}
}

if (!error) {
({timexProperty: parsed, error: error} = FunctionUtils.parseTimexProperty((args[0] as string).replace('XX', '00')));
}

if (!error) {
const hour = convertedDateTime.hour();
const minute = convertedDateTime.minute();
const second = convertedDateTime.second();

if (parsed.minute > minute || (parsed.minute === minute && parsed.second >= second)) {
validHour = hour;
} else {
validHour = hour + 1;
}

validMinute = parsed.minute;
validSecond = parsed.second;
}

value = TimexProperty.fromTime(new Time(validHour, validMinute, validSecond)).timex;
return {value, error};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* @module adaptive-expressions
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { ExpressionEvaluator } from '../expressionEvaluator';
import { Expression } from '../expression';
import { ReturnType } from '../returnType';
import { ExpressionType } from '../expressionType';
import { FunctionUtils } from '../functionUtils';
import { MemoryInterface } from '../memory/memoryInterface';
import { Options } from '../options';
import { TimeZoneConverter } from '../timeZoneConverter';
import { tz } from 'moment-timezone';
import moment from 'moment';
import {TimexProperty} from '@microsoft/recognizers-text-data-types-timex-expression';

/**
* Return the previous viable date of a timex expression based on the current date and user's timezone.
*/
export class GetPreviousViableDate extends ExpressionEvaluator {
public constructor(){
super(ExpressionType.GetPreviousViableDate, GetPreviousViableDate.evaluator, ReturnType.String, FunctionUtils.validateUnaryOrBinaryString);
}

private static evaluator(expr: Expression, state: MemoryInterface, options: Options): {value: any; error: string} {
let parsed: TimexProperty;
let value: string;
let error: string;
let args: any[];
const currentTime = moment(new Date().toISOString());
let validYear = 0;
let validMonth = 0;
let validDay = 0;
let convertedDateTime: moment.Moment;
({args, error} = FunctionUtils.evaluateChildren(expr, state, options));
if (!error) {
({timexProperty: parsed, error: error} = FunctionUtils.parseTimexProperty(args[0]));
}

if (parsed && !error) {
if (parsed.year || !parsed.month || !parsed.dayOfMonth) {
error = `${args[0]} must be a timex string which only contains month and day-of-month, for example: 'XXXX-10-31'.`;
}
}

if (!error) {
if (args.length === 2 && typeof args[1] === 'string') {
const timeZone: string = TimeZoneConverter .windowsToIana(args[1]);
if (!TimeZoneConverter.verifyTimeZoneStr(timeZone)) {
error = `${args[1]} is not a valid timezone`;
}

if (!error) {
convertedDateTime = tz(currentTime.utc(), timeZone);
}
} else {
convertedDateTime = currentTime.utc();
}
}

if (!error) {
const year = convertedDateTime.year();
const month = convertedDateTime.month() + 1;
const dayOfMonth = convertedDateTime.date();

if (parsed.month <= month || (parsed.month === month && parsed.dayOfMonth < dayOfMonth)) {
validYear = year;
} else {
validYear = year - 1;
}

validMonth = parsed.month;
validDay = parsed.dayOfMonth;

if (validMonth === 2 && validDay === 29) {
while (!GetPreviousViableDate.leapYear(validYear)) {
validYear -= 1;
}
}
}

value = TimexProperty.fromDate(new Date(validYear, validMonth - 1, validDay)).timex;
return {value, error};
}

private static leapYear(year: number): boolean
{
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* @module adaptive-expressions
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { ExpressionEvaluator } from '../expressionEvaluator';
import { Expression } from '../expression';
import { ReturnType } from '../returnType';
import { ExpressionType } from '../expressionType';
import { FunctionUtils } from '../functionUtils';
import { MemoryInterface } from '../memory/memoryInterface';
import { Options } from '../options';
import { TimeZoneConverter } from '../timeZoneConverter';
import { tz } from 'moment-timezone';
import moment from 'moment';
import {TimexProperty, Time} from '@microsoft/recognizers-text-data-types-timex-expression';
/**
* Return the previous viable time of a timex expression based on the current time and user's timezone.
*/
export class GetPreviousViableTime extends ExpressionEvaluator {
public constructor(){
super(ExpressionType.GetPreviousViableTime, GetPreviousViableTime.evaluator, ReturnType.String, FunctionUtils.validateUnaryOrBinaryString);
}

private static evaluator(expr: Expression, state: MemoryInterface, options: Options): {value: any; error: string} {
let parsed: TimexProperty;
let value: string;
let error: string;
let args: any[];
const currentTime = moment(new Date().toISOString());
let validHour = 0;
let validMinute = 0;
let validSecond = 0;
let convertedDateTime: moment.Moment;
const formatRegex = /TXX:[0-5][0-9]:[0-5][0-9]/g;
({args, error} = FunctionUtils.evaluateChildren(expr, state, options));
if(!error) {
if (!formatRegex.test(args[0] as string)) {
error = `${args[0]} must be a timex string which only contains minutes and seconds, for example: 'TXX:15:28'`
}
}

if (!error) {
if (args.length === 2 && typeof args[1] === 'string') {
const timeZone: string = TimeZoneConverter .windowsToIana(args[1]);
if (!TimeZoneConverter.verifyTimeZoneStr(timeZone)) {
error = `${args[1]} is not a valid timezone`;
}

if (!error) {
convertedDateTime = tz(currentTime.utc(), timeZone);
}
} else {
convertedDateTime = currentTime.utc();
}
}

if (!error) {
({timexProperty: parsed, error: error} = FunctionUtils.parseTimexProperty((args[0] as string).replace('XX', '00')));
}

if (!error) {
const hour = convertedDateTime.hour();
const minute = convertedDateTime.minute();
const second = convertedDateTime.second();

if (parsed.minute < minute || (parsed.minute === minute && parsed.second < second)) {
validHour = hour;
} else {
validHour = hour - 1;
}

validMinute = parsed.minute;
validSecond = parsed.second;
}

value = TimexProperty.fromTime(new Time(validHour, validMinute, validSecond)).timex;
return {value, error};
}
}
5 changes: 5 additions & 0 deletions libraries/adaptive-expressions/src/builtinFunctions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export * from './formatEpoch';
export * from './formatNumber';
export * from './formatTicks';
export * from './getFutureTime';
export * from './getNextViableDate';
export * from './getNextViableTime';
export * from './getPastTime';
export * from './getPreviousViableDate';
export * from './getPreviousViableTime';
export * from './getPastTime';
export * from './getProperty';
export * from './getTimeOfDay';
Expand Down
5 changes: 5 additions & 0 deletions libraries/adaptive-expressions/src/expressionFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ export class ExpressionFunctions {
new BuiltinFunctions.FormatNumber(),
new BuiltinFunctions.FormatTicks(),
new BuiltinFunctions.GetFutureTime(),
new BuiltinFunctions.GetNextViableDate(),
new BuiltinFunctions.GetNextViableTime(),
new BuiltinFunctions.GetPastTime(),
new BuiltinFunctions.GetPreviousViableDate(),
new BuiltinFunctions.GetPreviousViableTime(),
new BuiltinFunctions.GetPastTime(),
new BuiltinFunctions.GetProperty(),
new BuiltinFunctions.GetTimeOfDay(),
Expand Down
Loading

0 comments on commit 87c0be3

Please sign in to comment.