Last active
February 28, 2022 18:18
-
-
Save ljnmedium/626579cd9c4b38686b7f189466c4c350 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface TempExpDocument { | |
id: string; | |
text: string; | |
predString?: string; // Prediction string form the model | |
preds?: Array<NormalizedTempExp>; // TempExps in tuple format | |
absPreds?: Array<any>; // TempExps in absolute format for the referenced date | |
chartData?: any; | |
} | |
export class ValUnitTuple { | |
value: number; | |
unit: string; | |
constructor(unit: string, value: number) { | |
this.unit = unit; | |
this.value = value; | |
} | |
} | |
export class NormalizedTempExp { | |
type: 'ABS'|'REL'|'DUR'|'FREQ'|'NONE'; | |
mode?: 'DIR'|'IND'|undefined; | |
value: Array<ValUnitTuple>; | |
anchor?: 'FROM'|'TO'|'BEFORE'|'AFTER'|undefined; | |
tense?: 'PAST'|'FUTURE'|'PREV'|'NEXT'|'CURRENT'|undefined; | |
partial?: 'START'|'MID'|'END'|undefined; | |
approximation?: 'LESS'|'MORE'|'APPROX'|undefined; | |
ordinal?: 'FIRST'|'LAST'|undefined; | |
constructor() { | |
this.type = 'NONE'; | |
this.value = []; | |
} | |
public setAttribute(key: string, value: any) { | |
if (key == 'type') { | |
this.type = value; | |
} else if (key == 'mode') { | |
this.mode = value; | |
} else if (key == 'value') { | |
this.value = value; | |
} else if (key == 'anchor') { | |
this.anchor = value; | |
} else if (key == 'tense') { | |
this.tense = value; | |
} else if (key == 'partial') { | |
this.partial = value; | |
} else if (key == 'approximation') { | |
this.approximation = value; | |
} else if (key == 'ordinal') { | |
this.ordinal = value; | |
} | |
} | |
} | |
export const KEYWORD_TO_ATTR: {[key: string]: string} = { | |
ABS: 'type', | |
REL: 'type', | |
DUR: 'type', | |
FREQ: 'type', | |
NONE: 'type', | |
DIR: 'mode', | |
IND: 'mode', | |
FROM: 'anchor', | |
TO: 'anchor', | |
BEFORE: 'anchor', | |
AFTER: 'anchor', | |
PREV: 'tense', | |
NEXT: 'tense', | |
PAST: 'tense', | |
FUTURE: 'tense', | |
CURRENT: 'tense', | |
START: 'partial', | |
MID: 'partial', | |
END: 'partial', | |
LESS: 'approximation', | |
MORE: 'approximation', | |
APPROX: 'approximation', | |
FIRST: 'ordinal', | |
LAST: 'ordinal', | |
}; | |
const DUMMY_SEPARATOR = '-'; | |
const TIMESTRING_REGEXP = /^(S|DC|Y|Q|M|W|D)(\d)+$/g; | |
export function getNormalizedTempExp(myStr: string) { | |
/* | |
* Convert a string into normalized forms | |
* myStr: E.g. REL IND - FROM PREV M1; REL IND - FROM PREV M1; | |
*/ | |
// Get the single terms | |
const myTerms = myStr.split('; '); | |
const myResults = []; | |
for (const myTerm of myTerms) { | |
// For each term, break down the string into single attributes | |
const attrs = myTerm.split(' ').filter((x: string) => (x.length > 0 && x != DUMMY_SEPARATOR)); | |
if (attrs.length > 0) { | |
const myResult = new NormalizedTempExp(); | |
const valList = []; | |
for (const attr of attrs) { | |
if (KEYWORD_TO_ATTR[attr]) { | |
// Fixed terms like "DIR", "IND", "NONE"... can be assigned to their attributes | |
myResult.setAttribute(KEYWORD_TO_ATTR[attr], attr); | |
} else if (attr.match(TIMESTRING_REGEXP)) { | |
// Dynamic terms like "M12", "Y1980" ... can be assigned to the attribute "value" | |
const unit = (attr.slice(0, 2) == 'DC') ? 'DC' : attr.slice(0, 1); | |
const numVal = Number.parseInt(attr.slice(unit.length), 10); | |
valList.push(new ValUnitTuple(unit, numVal)); | |
} | |
} | |
myResult.setAttribute('value', valList); | |
myResults.push(myResult); | |
} | |
} | |
return myResults; | |
} | |
export function getRealDate(tempExp: NormalizedTempExp, refDate: any) { | |
let startDate = undefined; | |
let endDate = undefined; | |
let usedUnits: any = []; | |
let result: any = {start: startDate, end: endDate, anchor: tempExp.anchor, approximation: tempExp.approximation, partial: tempExp.partial}; | |
switch (tempExp.type) { | |
case 'ABS': | |
startDate = {year: 0, month: 0, day: 0}; | |
endDate = {year: 0, month: 0, day: 0}; | |
result = {start: startDate, end: endDate, anchor: tempExp.anchor, approximation: tempExp.approximation, partial: tempExp.partial}; | |
usedUnits = []; | |
for (const myTuple of tempExp.value) { | |
const unit = myTuple.unit; | |
const numVal = myTuple.value; | |
if (unit === 'S') { | |
startDate.year = (numVal > 0) ? (numVal * 100 - 99) : (numVal * 100); | |
endDate.year = (numVal > 0) ? (numVal * 100) : (numVal * 100 + 99); | |
usedUnits.push('Y'); | |
} else if (unit === 'DC') { | |
startDate.year = numVal; | |
endDate.year = numVal + 9; | |
usedUnits.push('Y'); | |
} else if (unit === 'Y') { | |
startDate.year = numVal; | |
endDate.year = numVal; | |
usedUnits.push('Y'); | |
} else if (unit === 'Q') { | |
startDate.month = numVal * 3 - 2; | |
endDate.month = numVal * 3; | |
usedUnits.push('M'); | |
} else if (unit === 'M') { | |
startDate.month = numVal; | |
endDate.month = numVal; | |
usedUnits.push('M'); | |
} else if (unit === 'W') { | |
startDate.day = numVal * 7 - 6; | |
endDate.day = numVal * 7; | |
usedUnits.push('D'); | |
} else if (unit === 'D') { | |
startDate.day = numVal; | |
endDate.day = numVal; | |
usedUnits.push('D'); | |
} | |
} | |
if (usedUnits.indexOf('D') >= 0 && usedUnits.indexOf('M') < 0) { | |
startDate.year = refDate.year; | |
startDate.month = refDate.month; | |
endDate.year = refDate.year; | |
endDate.month = refDate.month; | |
} else if (usedUnits.indexOf('M') >= 0 && usedUnits.indexOf('Y') < 0) { | |
startDate.year = refDate.year; | |
endDate.year = refDate.year; | |
if (usedUnits.indexOf('D') < 0) { | |
startDate.day = 1; | |
endDate.day = getEndOfMonth(endDate.month, endDate.year); | |
} | |
} | |
if (usedUnits.indexOf('Y') >= 0) { | |
if (usedUnits.indexOf('M') < 0) { | |
startDate.month = 1; | |
endDate.month = 12; | |
} | |
if (usedUnits.indexOf('D') < 0) { | |
startDate.day = 1; | |
endDate.day = getEndOfMonth(endDate.month, endDate.year); | |
} | |
} | |
switch (tempExp.tense) { | |
case 'PAST': | |
if (usedUnits.indexOf('D') >= 0 && usedUnits.indexOf('M') < 0) { | |
if (startDate.day >= refDate.day) { | |
startDate.month -= 1; | |
endDate.month -= 1; | |
} | |
} else if (usedUnits.indexOf('M') >= 0 && usedUnits.indexOf('Y') < 0) { | |
if (startDate.month >= refDate.month ) { | |
startDate.year -= 1; | |
endDate.year -= 1; | |
} | |
} | |
break; | |
case 'FUTURE': | |
if (usedUnits.indexOf('D') >= 0 && usedUnits.indexOf('M') < 0) { | |
if (startDate.day <= refDate.day) { | |
startDate.month += 1; | |
endDate.month += 1; | |
} | |
} else if (usedUnits.indexOf('M') >= 0 && usedUnits.indexOf('Y') < 0) { | |
if (startDate.month <= refDate.month) { | |
startDate.year += 1; | |
endDate.year += 1; | |
} | |
} | |
break; | |
} | |
result.start = adjustInvalidDate(result.start); | |
result.end = adjustInvalidDate(result.end); | |
return result; | |
case 'REL': | |
startDate = {year: refDate.year, month: refDate.month, day: refDate.day}; | |
endDate = {year: refDate.year, month: refDate.month, day: refDate.day}; | |
result = {start: startDate, end: endDate, anchor: tempExp.anchor, approximation: tempExp.approximation, partial: tempExp.partial}; | |
usedUnits = []; | |
switch (tempExp.tense) { | |
case 'CURRENT': | |
for (const myTuple of tempExp.value) { | |
const unit = myTuple.unit; | |
if (unit === 'S') { | |
startDate.year = Math.ceil(startDate.year / 100) * 100 - 99; | |
endDate.year = Math.ceil(endDate.year / 100) * 100; | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'DC') { | |
startDate.year = Math.floor(startDate.year / 10) * 10; | |
endDate.year = Math.ceil(endDate.year / 10) * 10 + 9; | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'Y') { | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'Q') { | |
startDate.month = Math.ceil(startDate.month / 3) * 3 - 2; | |
endDate.month = Math.ceil(endDate.month / 3) * 3; | |
startDate.day = 1; | |
endDate.day = getEndOfMonth(endDate.month, endDate.year); | |
usedUnits.push('M'); | |
} else if (unit === 'M') { | |
startDate.day = 1; | |
endDate.day = getEndOfMonth(endDate.month, endDate.year); | |
usedUnits.push('M'); | |
} else if (unit === 'W') { | |
startDate.day = Math.ceil(startDate.day / 7) * 7 - 6; | |
endDate.day = Math.ceil(endDate.day / 7) * 7; | |
usedUnits.push('D'); | |
} else if (unit === 'D') { | |
usedUnits.push('D'); | |
} | |
} | |
break; | |
case 'PAST': | |
for (const myTuple of tempExp.value) { | |
const unit = myTuple.unit; | |
const numVal = myTuple.value; | |
if (unit === 'S') { | |
startDate.year = Math.ceil(startDate.year / 100 - numVal) * 100 - 99; | |
endDate.year = Math.ceil(endDate.year / 100 - numVal) * 100; | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'DC') { | |
startDate.year = Math.floor(startDate.year / 10 - numVal) * 10; | |
endDate.year = Math.ceil(endDate.year / 10 - numVal) * 10 + 9; | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'Y') { | |
startDate.year = startDate.year - numVal; | |
endDate.year = endDate.year - numVal; | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'Q') { | |
startDate.month = Math.ceil(startDate.month / 3 - numVal) * 3 - 2; | |
endDate.month = Math.ceil(endDate.month / 3 - numVal) * 3; | |
startDate.day = 1; | |
endDate.day = getEndOfMonth(endDate.month, endDate.year); | |
usedUnits.push('M'); | |
} else if (unit === 'M') { | |
startDate.month = numVal; | |
endDate.month = numVal; | |
startDate.day = 1; | |
endDate.day = getEndOfMonth(endDate.month, endDate.year); ; | |
usedUnits.push('M'); | |
} else if (unit === 'W') { | |
startDate.day = Math.ceil(startDate.day / 7 - numVal) * 7 - 6; | |
endDate.day = Math.ceil(endDate.day / 7 - numVal) * 7; | |
usedUnits.push('D'); | |
} else if (unit === 'D') { | |
startDate.day = startDate.day - numVal; | |
endDate.day = endDate.day - numVal; | |
usedUnits.push('D'); | |
} | |
} | |
break; | |
case 'FUTURE': | |
for (const myTuple of tempExp.value) { | |
const unit = myTuple.unit; | |
const numVal = myTuple.value; | |
if (unit === 'S') { | |
startDate.year = Math.ceil(startDate.year / 100 + numVal) * 100 - 99; | |
endDate.year = Math.ceil(endDate.year / 100 + numVal) * 100; | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'DC') { | |
startDate.year = Math.floor(startDate.year / 10 + numVal) * 10; | |
endDate.year = Math.ceil(endDate.year / 10 + numVal) * 10 + 9; | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'Y') { | |
startDate.year = startDate.year + numVal; | |
endDate.year = endDate.year + numVal; | |
startDate.month = 1; | |
endDate.month = 12; | |
startDate.day = 1; | |
endDate.day = 31; | |
usedUnits.push('Y'); | |
} else if (unit === 'Q') { | |
startDate.month = Math.ceil(startDate.month / 3 + numVal) * 3 - 2; | |
endDate.month = Math.ceil(endDate.month / 3 + numVal) * 3; | |
startDate.day = 1; | |
endDate.day = getEndOfMonth(endDate.month, endDate.year); | |
usedUnits.push('M'); | |
} else if (unit === 'M') { | |
startDate.month = startDate.month + numVal; | |
endDate.month = endDate.month + numVal; | |
startDate.day = 1; | |
endDate.day = getEndOfMonth(endDate.month, endDate.year); | |
usedUnits.push('M'); | |
} else if (unit === 'W') { | |
startDate.day = Math.ceil(startDate.day / 7 + numVal) * 7 - 6; | |
endDate.day = Math.ceil(endDate.day / 7 + numVal) * 7; | |
usedUnits.push('D'); | |
} else if (unit === 'D') { | |
startDate.day = startDate.day + numVal; | |
endDate.day = endDate.day + numVal; | |
usedUnits.push('D'); | |
} | |
} | |
break; | |
case 'PREV': | |
for (const myTuple of tempExp.value) { | |
const unit = myTuple.unit; | |
const numVal = myTuple.value; | |
if (unit === 'S') { | |
startDate.year -= numVal * 100; | |
endDate.year -= numVal * 100; | |
usedUnits.push('Y'); | |
} else if (unit === 'DC') { | |
startDate.year -= numVal * 10; | |
endDate.year -= numVal * 10; | |
usedUnits.push('Y'); | |
} else if (unit === 'Y') { | |
startDate.year -= numVal; | |
endDate.year -= numVal; | |
usedUnits.push('Y'); | |
} else if (unit === 'Q') { | |
startDate.month -= numVal * 3; | |
endDate.month -= numVal * 3; | |
usedUnits.push('M'); | |
} else if (unit === 'M') { | |
startDate.month -= numVal; | |
endDate.month -= numVal; | |
usedUnits.push('M'); | |
} else if (unit === 'W') { | |
startDate.day -= numVal * 7; | |
endDate.day -= numVal * 7; | |
usedUnits.push('D'); | |
} else if (unit === 'D') { | |
startDate.day -= numVal; | |
endDate.day -= numVal; | |
usedUnits.push('D'); | |
} | |
} | |
break; | |
case 'NEXT': | |
for (const myTuple of tempExp.value) { | |
const unit = myTuple.unit; | |
const numVal = myTuple.value; | |
if (unit === 'S') { | |
startDate.year += numVal * 100; | |
endDate.year += numVal * 100; | |
usedUnits.push('Y'); | |
} else if (unit === 'DC') { | |
startDate.year += numVal * 10; | |
endDate.year += numVal * 10; | |
usedUnits.push('Y'); | |
} else if (unit === 'Y') { | |
startDate.year += numVal; | |
endDate.year += numVal; | |
usedUnits.push('Y'); | |
} else if (unit === 'Q') { | |
startDate.month += numVal * 3; | |
endDate.month += numVal * 3; | |
usedUnits.push('M'); | |
} else if (unit === 'M') { | |
startDate.month += numVal; | |
endDate.month += numVal; | |
usedUnits.push('M'); | |
} else if (unit === 'W') { | |
startDate.day += numVal * 7; | |
endDate.day += numVal * 7; | |
usedUnits.push('D'); | |
} else if (unit === 'D') { | |
startDate.day += numVal; | |
endDate.day += numVal; | |
usedUnits.push('D'); | |
} | |
} | |
break; | |
} | |
result.start = adjustInvalidDate(result.start); | |
result.end = adjustInvalidDate(result.end); | |
return result; | |
default: | |
return result; | |
} | |
} | |
export function getEndOfMonth(month: number, year: number) { | |
while (month > 12 || month < 1) { | |
if (month > 12) { | |
month -= 12; | |
year += 1; | |
} else if (month < 1) { | |
month += 12; | |
year -= 1; | |
} | |
} | |
if (month == 2 && (year % 400 == 0 || (year % 4 == 0 && (year % 100 != 0)))) { | |
return 29; | |
} | |
if (month == 2) { | |
return 28; | |
} | |
if ([4, 6, 9, 11].indexOf(month) >= 0) { | |
return 30; | |
} | |
return 31; | |
} | |
export function adjustInvalidDate(myDate: any) { | |
let year = myDate.year; | |
let month = myDate.month; | |
let day = myDate.day; | |
while (month > 12 || month < 1 || day > getEndOfMonth(month, year) || day < 1) { | |
if (day > getEndOfMonth(month, year)) { | |
day -= getEndOfMonth(month, year); | |
month += 1; | |
} else if (day < 1) { | |
day += getEndOfMonth(month - 1, year); | |
month -= 1; | |
} | |
if (month > 12) { | |
month -= 12; | |
year += 1; | |
} else if (month < 1) { | |
month += 12; | |
year -= 1; | |
} | |
} | |
return {year, month, day}; | |
} | |
export function mergeTemporalExpressions(tempExps: Array<any>) { | |
for (let i = tempExps.length - 2; i >= 0; i--) { | |
if (tempExps[i].anchor === 'FROM' && tempExps[i+1].anchor === 'TO') { | |
const merged = { | |
start: tempExps[i].start, | |
end: tempExps[i + 1].end, | |
approximation: tempExps[i].approximation || tempExps[i + 1].approximation, | |
partial: tempExps[i].partials || tempExps[i + 1].partial, | |
anchor: undefined, | |
}; | |
tempExps = tempExps.slice(0, i).concat([merged]).concat(tempExps.slice(i+2)); | |
} | |
} | |
// Sort the temporal expressions | |
try { | |
tempExps = tempExps.sort((a: any, b: any) => (a.start.year - b.start.year) * 1000 + (a.start.month - b.start.month) * 32 + (a.start.day - b.start.day)); | |
} catch (err) { | |
// Do nothing | |
console.log(err); | |
} | |
if (tempExps.length > 0) { | |
const tmp = tempExps[tempExps.length - 1]; | |
const now = new Date(); | |
if (tempExps[tempExps.length - 1].anchor === 'FROM' && | |
new Date(`${pad(tmp.start.year, 4)}-${pad(tmp.start.month, 2)}-${pad(tmp.start.day, 2)}`).getTime() < now.getTime()) { | |
tmp.end = { | |
year: now.getFullYear(), | |
month: now.getMonth() + 1, | |
day: now.getDate(), | |
}; | |
} | |
} | |
return tempExps; | |
} | |
export function pad(myStr: any, maxLength: number) { | |
return myStr.toString().padStart(maxLength, '0'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment