@@ -953,6 +953,16 @@ export class FormulaEngine {
953953 return this . calculateAbs ( args ) ;
954954 case 'ROUND' :
955955 return this . calculateRound ( args ) ;
956+ case 'FLOOR' :
957+ return this . calculateFloor ( args ) ;
958+ case 'CEILING' :
959+ return this . calculateCeiling ( args ) ;
960+ case 'SQRT' :
961+ return this . calculateSqrt ( args ) ;
962+ case 'POWER' :
963+ return this . calculatePower ( args ) ;
964+ case 'MOD' :
965+ return this . calculateMod ( args ) ;
956966 case 'INT' :
957967 return this . calculateInt ( args ) ;
958968 case 'RAND' :
@@ -987,6 +997,18 @@ export class FormulaEngine {
987997 return this . calculateToday ( args ) ;
988998 case 'NOW' :
989999 return this . calculateNow ( args ) ;
1000+ case 'YEAR' :
1001+ return this . calculateYear ( args ) ;
1002+ case 'MONTH' :
1003+ return this . calculateMonth ( args ) ;
1004+ case 'DAY' :
1005+ return this . calculateDay ( args ) ;
1006+ case 'HOUR' :
1007+ return this . calculateHour ( args ) ;
1008+ case 'MINUTE' :
1009+ return this . calculateMinute ( args ) ;
1010+ case 'SECOND' :
1011+ return this . calculateSecond ( args ) ;
9901012
9911013 default :
9921014 return { value : null , error : `Unknown function: ${ funcName } ` } ;
@@ -1048,6 +1070,44 @@ export class FormulaEngine {
10481070 return { value : Math . abs ( num ) , error : undefined } ;
10491071 }
10501072
1073+ private calculateFloor ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1074+ if ( args . length < 1 || args . length > 2 ) {
1075+ return { value : null , error : 'FLOOR requires 1 or 2 arguments' } ;
1076+ }
1077+ const num = Number ( args [ 0 ] ) ;
1078+ if ( isNaN ( num ) ) {
1079+ return { value : null , error : 'FLOOR first argument must be a number' } ;
1080+ }
1081+ const significance = args . length === 2 ? Number ( args [ 1 ] ) : 1 ;
1082+ if ( isNaN ( significance ) ) {
1083+ return { value : null , error : 'FLOOR significance must be a number' } ;
1084+ }
1085+ if ( significance === 0 ) {
1086+ return { value : 0 , error : undefined } ;
1087+ }
1088+ const factor = Math . floor ( num / significance ) ;
1089+ return { value : factor * significance , error : undefined } ;
1090+ }
1091+
1092+ private calculateCeiling ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1093+ if ( args . length < 1 || args . length > 2 ) {
1094+ return { value : null , error : 'CEILING requires 1 or 2 arguments' } ;
1095+ }
1096+ const num = Number ( args [ 0 ] ) ;
1097+ if ( isNaN ( num ) ) {
1098+ return { value : null , error : 'CEILING first argument must be a number' } ;
1099+ }
1100+ const significance = args . length === 2 ? Number ( args [ 1 ] ) : 1 ;
1101+ if ( isNaN ( significance ) ) {
1102+ return { value : null , error : 'CEILING significance must be a number' } ;
1103+ }
1104+ if ( significance === 0 ) {
1105+ return { value : 0 , error : undefined } ;
1106+ }
1107+ const factor = Math . ceil ( num / significance ) ;
1108+ return { value : factor * significance , error : undefined } ;
1109+ }
1110+
10511111 private calculateRound ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
10521112 if ( args . length < 1 || args . length > 2 ) {
10531113 return { value : null , error : 'ROUND requires 1 or 2 arguments' } ;
@@ -1064,6 +1124,44 @@ export class FormulaEngine {
10641124 return { value : Math . round ( num * factor ) / factor , error : undefined } ;
10651125 }
10661126
1127+ private calculateSqrt ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1128+ if ( args . length !== 1 ) {
1129+ return { value : null , error : 'SQRT requires exactly 1 argument' } ;
1130+ }
1131+ const num = Number ( args [ 0 ] ) ;
1132+ if ( isNaN ( num ) || num < 0 ) {
1133+ return { value : null , error : 'SQRT argument must be a non-negative number' } ;
1134+ }
1135+ return { value : Math . sqrt ( num ) , error : undefined } ;
1136+ }
1137+
1138+ private calculatePower ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1139+ if ( args . length !== 2 ) {
1140+ return { value : null , error : 'POWER requires exactly 2 arguments' } ;
1141+ }
1142+ const base = Number ( args [ 0 ] ) ;
1143+ const exponent = Number ( args [ 1 ] ) ;
1144+ if ( isNaN ( base ) || isNaN ( exponent ) ) {
1145+ return { value : null , error : 'POWER arguments must be numbers' } ;
1146+ }
1147+ return { value : Math . pow ( base , exponent ) , error : undefined } ;
1148+ }
1149+
1150+ private calculateMod ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1151+ if ( args . length !== 2 ) {
1152+ return { value : null , error : 'MOD requires exactly 2 arguments' } ;
1153+ }
1154+ const dividend = Number ( args [ 0 ] ) ;
1155+ const divisor = Number ( args [ 1 ] ) ;
1156+ if ( isNaN ( dividend ) || isNaN ( divisor ) ) {
1157+ return { value : null , error : 'MOD arguments must be numbers' } ;
1158+ }
1159+ if ( divisor === 0 ) {
1160+ return { value : null , error : 'MOD divisor must not be zero' } ;
1161+ }
1162+ return { value : dividend % divisor , error : undefined } ;
1163+ }
1164+
10671165 private calculateInt ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
10681166 if ( args . length !== 1 ) {
10691167 return { value : null , error : 'INT requires exactly 1 argument' } ;
@@ -1241,6 +1339,88 @@ export class FormulaEngine {
12411339 return { value : new Date ( ) , error : undefined } ;
12421340 }
12431341
1342+ private toDate ( value : unknown ) : Date | null {
1343+ if ( value instanceof Date ) {
1344+ return value ;
1345+ }
1346+ if ( typeof value === 'number' && ! isNaN ( value ) ) {
1347+ const d = new Date ( value ) ;
1348+ return isNaN ( d . getTime ( ) ) ? null : d ;
1349+ }
1350+ if ( typeof value === 'string' ) {
1351+ const d = new Date ( value ) ;
1352+ return isNaN ( d . getTime ( ) ) ? null : d ;
1353+ }
1354+ return null ;
1355+ }
1356+
1357+ private calculateYear ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1358+ if ( args . length !== 1 ) {
1359+ return { value : null , error : 'YEAR requires exactly 1 argument' } ;
1360+ }
1361+ const date = this . toDate ( args [ 0 ] ) ;
1362+ if ( ! date ) {
1363+ return { value : null , error : 'YEAR argument must be a valid date' } ;
1364+ }
1365+ return { value : date . getFullYear ( ) , error : undefined } ;
1366+ }
1367+
1368+ private calculateMonth ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1369+ if ( args . length !== 1 ) {
1370+ return { value : null , error : 'MONTH requires exactly 1 argument' } ;
1371+ }
1372+ const date = this . toDate ( args [ 0 ] ) ;
1373+ if ( ! date ) {
1374+ return { value : null , error : 'MONTH argument must be a valid date' } ;
1375+ }
1376+ // JavaScript month is 0-based; Excel-style is 1-based
1377+ return { value : date . getMonth ( ) + 1 , error : undefined } ;
1378+ }
1379+
1380+ private calculateDay ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1381+ if ( args . length !== 1 ) {
1382+ return { value : null , error : 'DAY requires exactly 1 argument' } ;
1383+ }
1384+ const date = this . toDate ( args [ 0 ] ) ;
1385+ if ( ! date ) {
1386+ return { value : null , error : 'DAY argument must be a valid date' } ;
1387+ }
1388+ return { value : date . getDate ( ) , error : undefined } ;
1389+ }
1390+
1391+ private calculateHour ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1392+ if ( args . length !== 1 ) {
1393+ return { value : null , error : 'HOUR requires exactly 1 argument' } ;
1394+ }
1395+ const date = this . toDate ( args [ 0 ] ) ;
1396+ if ( ! date ) {
1397+ return { value : null , error : 'HOUR argument must be a valid date' } ;
1398+ }
1399+ return { value : date . getHours ( ) , error : undefined } ;
1400+ }
1401+
1402+ private calculateMinute ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1403+ if ( args . length !== 1 ) {
1404+ return { value : null , error : 'MINUTE requires exactly 1 argument' } ;
1405+ }
1406+ const date = this . toDate ( args [ 0 ] ) ;
1407+ if ( ! date ) {
1408+ return { value : null , error : 'MINUTE argument must be a valid date' } ;
1409+ }
1410+ return { value : date . getMinutes ( ) , error : undefined } ;
1411+ }
1412+
1413+ private calculateSecond ( args : unknown [ ] ) : { value : unknown ; error ?: string } {
1414+ if ( args . length !== 1 ) {
1415+ return { value : null , error : 'SECOND requires exactly 1 argument' } ;
1416+ }
1417+ const date = this . toDate ( args [ 0 ] ) ;
1418+ if ( ! date ) {
1419+ return { value : null , error : 'SECOND argument must be a valid date' } ;
1420+ }
1421+ return { value : date . getSeconds ( ) , error : undefined } ;
1422+ }
1423+
12441424 private flattenArgs ( args : unknown [ ] ) : unknown [ ] {
12451425 const result : unknown [ ] = [ ] ;
12461426 for ( const arg of args ) {
0 commit comments