diff --git a/AgeCalculator/AgeCalculator.csproj b/AgeCalculator/AgeCalculator.csproj index 4264f8e..531c9d0 100644 --- a/AgeCalculator/AgeCalculator.csproj +++ b/AgeCalculator/AgeCalculator.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 Arman Ghazanchyan AG The Age Calculator is a library (based on the Gregorian calendar) which can be used to calculate the age between two dates and output years, months, days and time components. @@ -9,7 +9,7 @@ https://github.com/arman-g/AgeCalculator Git Age Calculator Calculation Calculate Years Old - 1.4.0 + 1.4.1 For sample usage see the REDME file under the repository. https://github.com/arman-g/AgeCalculator diff --git a/AgeCalculator/AgeCalculator.xml b/AgeCalculator/AgeCalculator.xml index a647719..2d23f3a 100644 --- a/AgeCalculator/AgeCalculator.xml +++ b/AgeCalculator/AgeCalculator.xml @@ -84,7 +84,7 @@ The age's from date. The age's to date. A boolean flag indicating whether February 28th of a non-leap year - is considered the end of 1-year cycle for a leapling. By default it is false. + is considered the end of 1-year cycle for a leapling. By default, it is false. Supports the Gregorian calendar only. An instance of class containing years, months, days and time information. Thrown when is considered greater than . diff --git a/AgeCalculator/Models/Age.cs b/AgeCalculator/Models/Age.cs index bbef54f..3dc9f71 100644 --- a/AgeCalculator/Models/Age.cs +++ b/AgeCalculator/Models/Age.cs @@ -10,203 +10,202 @@ using System; using System.Diagnostics; -namespace AgeCalculator +namespace AgeCalculator; + +/// +/// Represents class to hold years, months, days and time information. +/// +[DebuggerDisplay( + "{" + nameof(Years) + "} " + nameof(Years) + ", " + + "{" + nameof(Months) + "} " + nameof(Months) + ", " + + "{" + nameof(Days) + "} " + nameof(Days) + ", " + + "{" + nameof(Time) + "}")] +public class Age { + private readonly DateTime _fromDateTime; + private readonly DateTime _toDateTime; + private TimeSpan? _difference; + + #region ' Properties ' + /// - /// Represents class to hold years, months, days and time information. + /// Gets the years component of the class. /// - [DebuggerDisplay( - "{" + nameof(Years) + "} " + nameof(Years) + ", " + - "{" + nameof(Months) + "} " + nameof(Months) + ", " + - "{" + nameof(Days) + "} " + nameof(Days) + ", " + - "{" + nameof(Time) + "}")] - public class Age + public int Years { get; init; } + + /// + /// Gets the months component of the class. + /// + public byte Months { get; init; } + + /// + /// Gets the days component of the class. + /// + public byte Days { get; init; } + + /// + /// Gets the time component of the class. + /// + public TimeSpan Time { get; init; } + + /// + /// Gets the value of the current class expressed in whole and fractional days. + /// + /// The total number of days represented by this instance. + public double TotalDays { - private readonly DateTime _fromDateTime; - private readonly DateTime _toDateTime; - private TimeSpan? _difference; - - #region ' Properties ' - - /// - /// Gets the years component of the class. - /// - public int Years { get; init; } - - /// - /// Gets the months component of the class. - /// - public byte Months { get; init; } - - /// - /// Gets the days component of the class. - /// - public byte Days { get; init; } - - /// - /// Gets the time component of the class. - /// - public TimeSpan Time { get; init; } - - /// - /// Gets the value of the current class expressed in whole and fractional days. - /// - /// The total number of days represented by this instance. - public double TotalDays + get { - get - { - _difference ??= _toDateTime.Subtract(_fromDateTime); - return _difference.Value.TotalDays; - } + _difference ??= _toDateTime.Subtract(_fromDateTime); + return _difference.Value.TotalDays; } + } - /// - /// Gets the value of the current class expressed in whole and fractional hours. - /// - /// The total number of hours represented by this instance. - public double TotalHours + /// + /// Gets the value of the current class expressed in whole and fractional hours. + /// + /// The total number of hours represented by this instance. + public double TotalHours + { + get { - get - { - _difference ??= _toDateTime.Subtract(_fromDateTime); - return _difference.Value.TotalHours; - } + _difference ??= _toDateTime.Subtract(_fromDateTime); + return _difference.Value.TotalHours; } + } - /// - /// Gets the value of the current class expressed in whole and fractional minutes. - /// - /// The total number of minutes represented by this instance. - public double TotalMinutes + /// + /// Gets the value of the current class expressed in whole and fractional minutes. + /// + /// The total number of minutes represented by this instance. + public double TotalMinutes + { + get { - get - { - _difference ??= _toDateTime.Subtract(_fromDateTime); - return _difference.Value.TotalMinutes; - } + _difference ??= _toDateTime.Subtract(_fromDateTime); + return _difference.Value.TotalMinutes; } + } - /// - /// Gets the value of the current class expressed in whole and fractional seconds. - /// - /// The total number of seconds represented by this instance. - public double TotalSeconds + /// + /// Gets the value of the current class expressed in whole and fractional seconds. + /// + /// The total number of seconds represented by this instance. + public double TotalSeconds + { + get { - get - { - _difference ??= _toDateTime.Subtract(_fromDateTime); - return _difference.Value.TotalSeconds; - } + _difference ??= _toDateTime.Subtract(_fromDateTime); + return _difference.Value.TotalSeconds; } + } - /// - /// Gets the value of the current class expressed in whole and fractional milliseconds. - /// - /// The total number of milliseconds represented by this instance. - public double TotalMilliseconds + /// + /// Gets the value of the current class expressed in whole and fractional milliseconds. + /// + /// The total number of milliseconds represented by this instance. + public double TotalMilliseconds + { + get { - get - { - _difference ??= _toDateTime.Subtract(_fromDateTime); - return _difference.Value.TotalMilliseconds; - } + _difference ??= _toDateTime.Subtract(_fromDateTime); + return _difference.Value.TotalMilliseconds; } + } - /// - /// Gets the number of ticks that represent the value of the current class. - /// - /// The number of ticks contained in this instance. - public long Ticks + /// + /// Gets the number of ticks that represent the value of the current class. + /// + /// The number of ticks contained in this instance. + public long Ticks + { + get { - get - { - _difference ??= _toDateTime.Subtract(_fromDateTime); - return _difference.Value.Ticks; - } + _difference ??= _toDateTime.Subtract(_fromDateTime); + return _difference.Value.Ticks; } + } + + #endregion - #endregion - - /// - /// Initializes a new instance of the class and calculates the age between the specified dates. - /// - /// The age's from date. - /// The age's to date. - /// A boolean flag indicating whether February 28th of a non-leap year - /// is considered the end of 1-year cycle for a leapling. By default it is false. - /// Supports the Gregorian calendar only. - /// An instance of class containing years, months, days and time information. - /// Thrown when is considered greater than . - public Age( - DateTime fromDate, - DateTime toDate, - bool isFeb28AYearCycleForLeapling = false) + /// + /// Initializes a new instance of the class and calculates the age between the specified dates. + /// + /// The age's from date. + /// The age's to date. + /// A boolean flag indicating whether February 28th of a non-leap year + /// is considered the end of 1-year cycle for a leapling. By default, it is false. + /// Supports the Gregorian calendar only. + /// An instance of class containing years, months, days and time information. + /// Thrown when is considered greater than . + public Age( + DateTime fromDate, + DateTime toDate, + bool isFeb28AYearCycleForLeapling = false) + { + if (fromDate > toDate) throw new ArgumentOutOfRangeException( + nameof(fromDate), + $"The '{nameof(fromDate)}' must be less or equal to '{nameof(toDate)}'."); + + _fromDateTime = fromDate; + _toDateTime = toDate; + + const byte totalMonths = 12; + const byte feb28 = 59; + const byte feb29 = 60; + var fromDateDay = _fromDateTime.Day; + var toDateDay = _toDateTime.Day; + var fromDateMonth = _fromDateTime.Month; + var toDateMonth = _toDateTime.Month; + var fromDateYear = _fromDateTime.Year; + var toDateYear = _toDateTime.Year; + var fromDateTimeOfDay = _fromDateTime.TimeOfDay; + var toDateTimeOfDay = _toDateTime.TimeOfDay; + + // Calculate years and months + var remainderDay = fromDateTimeOfDay > toDateTimeOfDay ? 1 : 0; // One less day + var remainderMonth = fromDateDay > toDateDay - remainderDay ? 1 : 0; // One less month + if (fromDateMonth == toDateMonth) { - if (fromDate > toDate) throw new ArgumentOutOfRangeException( - nameof(fromDate), - $"The '{nameof(fromDate)}' must be less or equal to '{nameof(toDate)}'."); - - _fromDateTime = fromDate; - _toDateTime = toDate; - - const byte totalMonths = 12; - const byte feb28 = 59; - const byte feb29 = 60; - var fromDateDay = _fromDateTime.Day; - var toDateDay = _toDateTime.Day; - var fromDateMonth = _fromDateTime.Month; - var toDateMonth = _toDateTime.Month; - var fromDateYear = _fromDateTime.Year; - var toDateYear = _toDateTime.Year; - var fromDateTimeOfDay = _fromDateTime.TimeOfDay; - var toDateTimeOfDay = _toDateTime.TimeOfDay; - - // Calculate years and months - var remainderDay = fromDateTimeOfDay > toDateTimeOfDay ? 1 : 0; // One less day - var remainderMonth = fromDateDay > toDateDay - remainderDay ? 1 : 0; // One less month - if (fromDateMonth == toDateMonth) - { - Years = toDateYear - fromDateYear - remainderMonth; - Months = (byte)((totalMonths - remainderMonth) * remainderMonth); - } - else - { - var months = fromDateMonth > toDateMonth ? totalMonths : 0; - Years = toDateYear - fromDateYear - months / totalMonths; - Months = (byte)(months + toDateMonth - fromDateMonth - remainderMonth); - } - - // Calculate days - var days = (toDateDay - remainderDay - fromDateDay); - Days = (byte)(days < 0 ? DateTime.DaysInMonth(fromDateYear, fromDateMonth) + days : days); - - // Calculate time - Time = TimeSpan.FromDays(remainderDay) + toDateTimeOfDay - fromDateTimeOfDay; - - // Adjust years, months and days if Feb 29 of a leap year is considered as Feb 28 of non-leap year. - if (!isFeb28AYearCycleForLeapling || - !DateTime.IsLeapYear(fromDateYear) || - DateTime.IsLeapYear(toDateYear) || - _fromDateTime.DayOfYear != feb29 || - _toDateTime.DayOfYear != feb28 || - Days != 28) return; - - ++Years; - Months = 0; - Days = 0; + Years = toDateYear - fromDateYear - remainderMonth; + Months = (byte)((totalMonths - remainderMonth) * remainderMonth); } - - /// - /// Calculates the age between two dates. - /// - /// - public static Age Calculate( - DateTime fromDate, - DateTime toDate, - bool isFeb28AYearCycleForLeapling = false) + else { - return new Age(fromDate, toDate, isFeb28AYearCycleForLeapling); + var months = fromDateMonth > toDateMonth ? totalMonths : 0; + Years = toDateYear - fromDateYear - months / totalMonths; + Months = (byte)(months + toDateMonth - fromDateMonth - remainderMonth); } + + // Calculate days + var days = (toDateDay - remainderDay - fromDateDay); + Days = (byte)(days < 0 ? DateTime.DaysInMonth(fromDateYear, fromDateMonth) + days : days); + + // Calculate time + Time = TimeSpan.FromDays(remainderDay) + toDateTimeOfDay - fromDateTimeOfDay; + + // Adjust years, months and days if Feb 29 of a leap year is considered as Feb 28 of non-leap year. + if (!isFeb28AYearCycleForLeapling || + !DateTime.IsLeapYear(fromDateYear) || + DateTime.IsLeapYear(toDateYear) || + _fromDateTime.DayOfYear != feb29 || + _toDateTime.DayOfYear != feb28 || + Days != 28) return; + + ++Years; + Months = 0; + Days = 0; + } + + /// + /// Calculates the age between two dates. + /// + /// + public static Age Calculate( + DateTime fromDate, + DateTime toDate, + bool isFeb28AYearCycleForLeapling = false) + { + return new Age(fromDate, toDate, isFeb28AYearCycleForLeapling); } -} +} \ No newline at end of file diff --git a/README.md b/README.md index 750eeb3..0f940cb 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,6 @@ The **Age Calculator** is a library *(based on the **Gregorian** calendar)* whic In addition to the default calculation algorithm, it can be configured so that **Feb 29th** of a leap year be considered as **Feb 28th** of a non-leap year *(making **1-year** cycle)* with switch of a flag. By default, the flag is off. -## Dependencies -.NET 6+ - ## How To Use (C# Code) ``` csharp /* There are three ways to calculate the age between two dates */ diff --git a/Tests/AgeTests.cs b/Tests/AgeTests.cs index 7a14cfe..ebce89b 100644 --- a/Tests/AgeTests.cs +++ b/Tests/AgeTests.cs @@ -6,15 +6,8 @@ namespace Tests { - public class AgeTests + public class AgeTests(ITestOutputHelper output) { - private readonly ITestOutputHelper _output; - - public AgeTests(ITestOutputHelper output) - { - _output = output; - } - [Fact] public void TotalValues() { @@ -36,7 +29,7 @@ public void TotalValues() public void InvalidDates(DateTime fromDate, DateTime toDate) { const string paramName = "fromDate"; - _output.WriteLine("Should throw an exception."); + output.WriteLine("Should throw an exception."); // Test initialization method Assert.Throws(paramName, () => new Age(fromDate, toDate)); @@ -165,12 +158,12 @@ public void CalculateAge( bool isFeb28AYearCycleForLeapling = false) { var age = Age.Calculate(fromDate, toDate, isFeb28AYearCycleForLeapling); - _output.WriteLine($"{fromDate:MM/dd/yyyy HH:mm:ss}:{GetLOrNYear(fromDate)} - {toDate:MM/dd/yyyy HH:mm:ss}:{GetLOrNYear(toDate)}"); + output.WriteLine($"{fromDate:MM/dd/yyyy HH:mm:ss}:{GetLOrNYear(fromDate)} - {toDate:MM/dd/yyyy HH:mm:ss}:{GetLOrNYear(toDate)}"); if (DateTime.IsLeapYear(fromDate.Year)) { - _output.WriteLine($"{nameof(isFeb28AYearCycleForLeapling)}: {isFeb28AYearCycleForLeapling}"); + output.WriteLine($"{nameof(isFeb28AYearCycleForLeapling)}: {isFeb28AYearCycleForLeapling}"); } - _output.WriteLine($"Age: {age.Years} years, {age.Months} months, {age.Days} days, {age.Time}"); + output.WriteLine($"Age: {age.Years} years, {age.Months} months, {age.Days} days, {age.Time}"); Assert.StrictEqual(expectedYears, age.Years); Assert.StrictEqual(expectedMonths, age.Months); Assert.StrictEqual(expectedDays, age.Days); diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index ade67a3..4a660d7 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,19 +1,19 @@ - net6.0 + net8.0 false - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all