< Summary - ZhEaIsNsAaBn.Libraries coverage (affected: 23 packages)

Information
Line coverage
84%
Covered lines: 62
Uncovered lines: 11
Coverable lines: 73
Total lines: 229
Line coverage: 84.9%
Branch coverage
81%
Covered branches: 26
Total branches: 32
Branch coverage: 81.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor()100%11100%
.ctor(...)100%22100%
.ctor(...)100%11100%
IsHijriDate(...)100%66100%
Now()100%11100%
op_Equality(...)100%44100%
op_Inequality(...)100%11100%
op_GreaterThan(...)100%210%
op_GreaterThanOrEqual(...)100%210%
op_LessThan(...)100%210%
op_LessThanOrEqual(...)100%210%
op_Implicit(...)100%210%
op_Implicit(...)100%11100%
op_Implicit(...)100%210%
op_Implicit(...)100%210%
ToDateOnly()100%210%
ToDateTime()100%11100%
ToGregorianDateOnly()100%210%
ToGregorianDateTime()100%11100%
Equals(...)50%22100%
GetHashCode()100%11100%
ToString()100%210%
ToString(...)100%210%
ToString(...)50%22100%
FromGregorianDate(...)83.33%66100%
FromGregorianString(...)100%11100%
FromHijriString(...)100%11100%
ParseString(...)70%1010100%

File(s)

/home/runner/work/ZhEaIsNsAaBn.Libraries.V1/ZhEaIsNsAaBn.Libraries.V1/src/UtilitiesPackages/ZhEaIsNsAaBn.Utilities/HijriDate/HijriDate.cs

#LineLine coverage
 1#region Usings
 2using System.Diagnostics.CodeAnalysis;
 3using System.Globalization;
 4#endregion
 5
 6namespace ZhEaIsNsAaBn.Utilities;
 7
 8/// <summary>
 9/// A date held in the Hijri (<see cref="HijriCalendar"/>) calendar, convertible to and from
 10/// Gregorian <see cref="DateTime"/>, <see cref="DateOnly"/> and date strings.
 11/// </summary>
 12/// <remarks>Only dates whose year is between 1900 and 2100 are accepted.</remarks>
 13public class HijriDate
 14{
 15    private const int EndGreg   = 2100;
 16    private const int StartGreg = 1900;
 17
 18    private const string DefaultFormat = "dd-MM-yyy";
 19
 1120    private static readonly string[] _allFormats =
 1121        [
 1122            "yyyy/MM/dd",
 1123            "yyyy/M/d",
 1124            "dd/MM/yyyy",
 1125            "d/M/yyyy",
 1126            "dd/M/yyyy",
 1127            "d/MM/yyyy",
 1128            "yyyy-MM-dd",
 1129            "yyyy-M-d",
 1130            "dd-MM-yyyy",
 1131            "d-M-yyyy",
 1132            "dd-M-yyyy",
 1133            "d-MM-yyyy",
 1134            "yyyy MM dd",
 1135            "yyyy M d",
 1136            "dd MM yyyy",
 1137            "d M yyyy",
 1138            "dd M yyyy",
 1139            "d MM yyyy"
 1140        ];
 41
 11242    private readonly CultureInfo       _arCul     = new( "ar-SA" );
 43    private readonly DateTime          _dateTime;
 11244    private readonly CultureInfo       _enCul     = new( "en-US" );
 11245    private readonly GregorianCalendar _gregorian = new();
 11246    private readonly HijriCalendar     _hijri     = new();
 47
 48
 49    /// <summary>Creates a date by parsing a Hijri or Gregorian date string.</summary>
 50    /// <param name="stringDate">The date, e.g. <c>1446/05/20</c> or <c>20-05-1446</c>. Time parts are not accepted.</pa
 51    /// <param name="isHijri"><see langword="true"/> (default) if <paramref name="stringDate"/> is Hijri; <see langword=
 52    /// <exception cref="HijriDateException"><paramref name="stringDate"/> is empty, not in a supported format, or outsi
 3653    public HijriDate( string stringDate, bool isHijri = true ) : this()
 3654        => _dateTime = isHijri ? FromHijriString( stringDate ) : FromGregorianString( stringDate );
 55
 56    /// <summary>Creates a date from a Gregorian <see cref="DateTime"/>. The time of day is dropped.</summary>
 57    /// <param name="gregorianDate">The Gregorian date.</param>
 58    /// <exception cref="HijriDateException">The year of <paramref name="gregorianDate"/> is outside 1900–2100.</excepti
 15259    public HijriDate( DateTime gregorianDate ) : this() => _dateTime = FromGregorianDate( gregorianDate );
 60
 11261    private HijriDate()
 62    {
 11263        _arCul.DateTimeFormat.Calendar = _hijri;
 11264        _enCul.DateTimeFormat.Calendar = _gregorian;
 11265    }
 66
 67
 68    /// <summary>Checks whether a string is a valid Hijri date in a supported format and year range.</summary>
 69    /// <param name="hijriString">The string to check.</param>
 70    /// <returns><see langword="true"/> if it parses as a Hijri date between 1900 and 2100; otherwise <see langword="fal
 71    public static bool IsHijriDate( string? hijriString )
 72    {
 6273        if ( string.IsNullOrEmpty( hijriString ) ) { return false; }
 74
 75
 4076        var arCul = new CultureInfo( "ar-SA" );
 4077        arCul.DateTimeFormat.Calendar = new HijriCalendar();
 78
 79
 4080        return DateTime.TryParseExact( hijriString, _allFormats, arCul.DateTimeFormat, DateTimeStyles.AllowWhiteSpaces, 
 4081            && tempDate.Year is >= StartGreg and <= EndGreg;
 82    }
 83
 84    /// <summary>Gets today's date.</summary>
 85    /// <returns>Today's date, taken from the local clock.</returns>
 486    public static HijriDate Now() => DateTime.Now;
 87
 88
 89    /// <summary>Compares two dates for equality. Returns <see langword="false"/> if either is <see langword="null"/>.</
 90    /// <param name="date1">The first date.</param>
 91    /// <param name="date2">The second date.</param>
 92    /// <returns><see langword="true"/> if both are set and equal.</returns>
 93    public static bool operator ==( HijriDate? date1, HijriDate? date2 )
 13294        => date1 is not null && date2 is not null && date1.ToDateTime() == date2.ToDateTime();
 95
 96    /// <summary>Compares two dates for inequality.</summary>
 97    /// <param name="date1">The first date.</param>
 98    /// <param name="date2">The second date.</param>
 99    /// <returns>The opposite of <c>==</c>.</returns>
 8100    public static bool operator !=( HijriDate? date1, HijriDate? date2 ) => !( date1 == date2 );
 101
 102    /// <summary>Checks whether <paramref name="date1"/> is later than <paramref name="date2"/>.</summary>
 103    /// <param name="date1">The first date.</param>
 104    /// <param name="date2">The second date.</param>
 105    /// <returns><see langword="true"/> if <paramref name="date1"/> is later.</returns>
 0106    public static bool operator >( HijriDate date1, HijriDate date2 ) => date1.ToDateTime() > date2.ToDateTime();
 107
 108    /// <summary>Checks whether <paramref name="date1"/> is the same as or later than <paramref name="date2"/>.</summary
 109    /// <param name="date1">The first date.</param>
 110    /// <param name="date2">The second date.</param>
 111    /// <returns><see langword="true"/> if <paramref name="date1"/> is the same or later.</returns>
 0112    public static bool operator >=( HijriDate date1, HijriDate date2 ) => date1.ToDateTime() >= date2.ToDateTime();
 113
 114    /// <summary>Checks whether <paramref name="date1"/> is earlier than <paramref name="date2"/>.</summary>
 115    /// <param name="date1">The first date.</param>
 116    /// <param name="date2">The second date.</param>
 117    /// <returns><see langword="true"/> if <paramref name="date1"/> is earlier.</returns>
 0118    public static bool operator <( HijriDate date1, HijriDate date2 ) => date1.ToDateTime() < date2.ToDateTime();
 119
 120    /// <summary>Checks whether <paramref name="date1"/> is the same as or earlier than <paramref name="date2"/>.</summa
 121    /// <param name="date1">The first date.</param>
 122    /// <param name="date2">The second date.</param>
 123    /// <returns><see langword="true"/> if <paramref name="date1"/> is the same or earlier.</returns>
 0124    public static bool operator <=( HijriDate date1, HijriDate date2 ) => date1.ToDateTime() <= date2.ToDateTime();
 125
 126    /// <summary>Parses a Hijri date string.</summary>
 127    /// <param name="s">The Hijri date string.</param>
 128    /// <exception cref="HijriDateException"><paramref name="s"/> is not a valid Hijri date.</exception>
 0129    public static implicit operator HijriDate( string s ) => new( s );
 130
 131    /// <summary>Converts a Gregorian <see cref="DateTime"/>. The time of day is dropped.</summary>
 132    /// <param name="date">The Gregorian date.</param>
 133    /// <exception cref="HijriDateException">The year is outside 1900–2100.</exception>
 4134    public static implicit operator HijriDate( DateTime date ) => new( date );
 135
 136    /// <summary>Formats the date as a Hijri string (<c>dd-MM-yyy</c>).</summary>
 137    /// <param name="hijriDate">The date.</param>
 0138    public static implicit operator string( HijriDate hijriDate ) => hijriDate.ToString();
 139
 140    /// <summary>Converts to a <see cref="DateTime"/>; same as <see cref="ToDateTime"/>.</summary>
 141    /// <param name="hijriDate">The date.</param>
 0142    public static implicit operator DateTime( HijriDate hijriDate ) => hijriDate.ToDateTime();
 143
 144
 145    /// <summary>Gets the date as a <see cref="DateOnly"/>, reading the stored parts in the Hijri calendar.</summary>
 146    /// <returns>The date.</returns>
 0147    public DateOnly ToDateOnly() => DateOnly.FromDateTime( ToDateTime() );
 148
 149    /// <summary>Gets the date as a <see cref="DateTime"/>, reading the stored parts in the Hijri calendar.</summary>
 150    /// <returns>The date.</returns>
 151    public DateTime ToDateTime()
 64152        => new( _dateTime.Year, _dateTime.Month, _dateTime.Day, _dateTime.Hour, _dateTime.Minute, _dateTime.Second,
 64153                _arCul.DateTimeFormat.Calendar );
 154
 155    /// <summary>Gets the date as a <see cref="DateOnly"/>, reading the stored parts in the Gregorian calendar.</summary
 156    /// <returns>The date.</returns>
 0157    public DateOnly ToGregorianDateOnly() => DateOnly.FromDateTime( ToGregorianDateTime() );
 158
 159    /// <summary>Gets the date as a <see cref="DateTime"/>, reading the stored parts in the Gregorian calendar.</summary
 160    /// <returns>The date.</returns>
 161    public DateTime ToGregorianDateTime()
 12162        => new( _dateTime.Year, _dateTime.Month, _dateTime.Day, _dateTime.Hour, _dateTime.Minute, _dateTime.Second,
 12163                _enCul.DateTimeFormat.Calendar );
 164
 165    /// <inheritdoc />
 44166    public override bool Equals( object? obj ) => obj is HijriDate other && _dateTime == other._dateTime;
 167
 168    /// <inheritdoc />
 8169    public override int GetHashCode() => _dateTime.GetHashCode();
 170
 171    /// <summary>Formats the date as a Hijri string (<c>dd-MM-yyy</c>).</summary>
 172    /// <returns>The formatted date.</returns>
 0173    public override string ToString() => ToString( DefaultFormat );
 174
 175    /// <summary>Formats the date with the default format (<c>dd-MM-yyy</c>).</summary>
 176    /// <param name="toGregorian"><see langword="true"/> to format in the Gregorian calendar; <see langword="false"/> fo
 177    /// <returns>The formatted date.</returns>
 0178    public string ToString( bool toGregorian ) => ToString( DefaultFormat, toGregorian );
 179
 180    /// <summary>Formats the date with a custom format.</summary>
 181    /// <param name="format">A <see cref="DateTime"/> format string.</param>
 182    /// <param name="toGregorian"><see langword="true"/> to format in the Gregorian calendar; <see langword="false"/> (d
 183    /// <returns>The formatted date.</returns>
 184    /// <exception cref="FormatException"><paramref name="format"/> is not a valid format string.</exception>
 185    public string ToString(
 186            [ StringSyntax( StringSyntaxAttribute.DateTimeFormat ) ] string format,
 187            bool toGregorian = false
 188        )
 44189        => toGregorian
 44190               ? _dateTime.ToString( format, _enCul.DateTimeFormat )
 44191               : _dateTime.ToString( format, _arCul.DateTimeFormat );
 192
 193
 194    private static DateTime FromGregorianDate( DateTime gregorianDate )
 195    {
 76196        if ( gregorianDate.Year is < StartGreg or > EndGreg )
 197        {
 4198            throw new HijriDateException(
 4199                    $"Convert Gregorian date to Hijri date unsupported data : {gregorianDate.ToString( "yyyy-MM-dd", Cul
 4200                );
 201        }
 202
 203
 72204        return gregorianDate.Date;
 205    }
 206
 207    private DateTime FromGregorianString( string gregorianString )
 4208        => ParseString( gregorianString, _enCul, "Convert Gregorian string to Hijri date" );
 209
 210    private DateTime FromHijriString( string hijriString )
 32211        => ParseString( hijriString, _arCul, "Convert Hijri string to Hijri date" );
 212
 213    private static DateTime ParseString( string value, CultureInfo culture, string operation )
 214    {
 40215        if ( string.IsNullOrEmpty( value ) ) { throw new HijriDateException( $"{operation} Error: Date String is Empty" 
 216
 217
 32218        if ( !DateTime.TryParseExact( value, _allFormats, culture.DateTimeFormat, DateTimeStyles.AllowWhiteSpaces, out v
 219        {
 4220            throw new HijriDateException( $"{operation} Error : {value}" );
 221        }
 222
 223
 28224        if ( date.Year is < StartGreg or > EndGreg ) { throw new HijriDateException( $"{operation} unsupported data : {v
 225
 226
 28227        return date;
 228    }
 229}