|  | using System; | 
|  | using System.Collections.Generic; | 
|  | using System.Globalization; | 
|  | using System.Linq; | 
|  | using System.Text; | 
|  | using OfficeOpenXml.FormulaParsing.Exceptions; | 
|  | using OfficeOpenXml.FormulaParsing.ExpressionGraph; | 
|  |  | 
|  | namespace OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime | 
|  | { | 
|  | public class Yearfrac : ExcelFunction | 
|  | { | 
|  | public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, ParsingContext context) | 
|  | { | 
|  | var functionArguments = arguments as FunctionArgument[] ?? arguments.ToArray(); | 
|  | ValidateArguments(functionArguments, 2); | 
|  | var date1Num = ArgToDecimal(functionArguments, 0); | 
|  | var date2Num = ArgToDecimal(functionArguments, 1); | 
|  | if (date1Num > date2Num) //Switch to make date1 the lowest date | 
|  | { | 
|  | var t = date1Num; | 
|  | date1Num = date2Num; | 
|  | date2Num = t; | 
|  | var fa = functionArguments[1]; | 
|  | functionArguments[1] = functionArguments[0]; | 
|  | functionArguments[0] = fa; | 
|  | } | 
|  | var date1 = System.DateTime.FromOADate(date1Num); | 
|  | var date2 = System.DateTime.FromOADate(date2Num); | 
|  |  | 
|  | var basis = 0; | 
|  | if (functionArguments.Count() > 2) | 
|  | { | 
|  | basis = ArgToInt(functionArguments, 2); | 
|  | ThrowExcelErrorValueExceptionIf(() => basis < 0 || basis > 4, eErrorType.Num); | 
|  | } | 
|  | var func = context.Configuration.FunctionRepository.GetFunction("days360"); | 
|  | var calendar = new GregorianCalendar(); | 
|  | switch (basis) | 
|  | { | 
|  | case 0: | 
|  | var d360Result = System.Math.Abs(func.Execute(functionArguments, context).ResultNumeric); | 
|  | // reproducing excels behaviour | 
|  | if (date1.Month == 2 && date2.Day==31) | 
|  | { | 
|  | var daysInFeb = calendar.IsLeapYear(date1.Year) ? 29 : 28; | 
|  | if (date1.Day == daysInFeb) d360Result++; | 
|  | } | 
|  | return CreateResult(d360Result / 360d, DataType.Decimal); | 
|  | case 1: | 
|  | return CreateResult(System.Math.Abs((date2 - date1).TotalDays / CalculateAcutalYear(date1, date2)), DataType.Decimal); | 
|  | case 2: | 
|  | return CreateResult(System.Math.Abs((date2 - date1).TotalDays / 360d), DataType.Decimal); | 
|  | case 3: | 
|  | return CreateResult(System.Math.Abs((date2 - date1).TotalDays / 365d), DataType.Decimal); | 
|  | case 4: | 
|  | var args = functionArguments.ToList(); | 
|  | args.Add(new FunctionArgument(true)); | 
|  | double? result = System.Math.Abs(func.Execute(args, context).ResultNumeric / 360d); | 
|  | return CreateResult(result.Value, DataType.Decimal); | 
|  | default: | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | private double CalculateAcutalYear(System.DateTime dt1, System.DateTime dt2) | 
|  | { | 
|  | var calendar = new GregorianCalendar(); | 
|  | var perYear = 0d; | 
|  | var nYears = dt2.Year - dt1.Year + 1; | 
|  | for (var y = dt1.Year; y <= dt2.Year; ++y) | 
|  | { | 
|  | perYear += calendar.IsLeapYear(y) ? 366 : 365; | 
|  | } | 
|  | if (new System.DateTime(dt1.Year + 1, dt1.Month, dt1.Day) >= dt2) | 
|  | { | 
|  | nYears = 1; | 
|  | perYear = 365; | 
|  | if (calendar.IsLeapYear(dt1.Year) && dt1.Month <= 2) | 
|  | perYear = 366; | 
|  | else if (calendar.IsLeapYear(dt2.Year) && dt2.Month > 2) | 
|  | perYear = 366; | 
|  | else if (dt2.Month == 2 && dt2.Day == 29) | 
|  | perYear = 366; | 
|  | } | 
|  | return perYear/(double) nYears; | 
|  | } | 
|  | } | 
|  | } |