Files
docs/archive/SESSION-2025-12-12-PERSIAN-DATE-AND-NETWORK-INFO-IMPROVEMENTS.md
T
masoodafar-web 5965b98728 update
2026-01-03 18:27:49 +03:30

23 KiB

گزارش تغییرات - 2025-12-12

خلاصه اجرایی

این سشن شامل دو بخش اصلی بود:

  1. تبدیل نمایش تاریخ‌ها به شمسی در فرانت‌اند BackOffice
  2. بهبود سرویس اطلاعات شبکه کاربران با اضافه کردن 28+ فیلد جدید

بخش 1: سیستم تبدیل تاریخ شمسی

1.1. ایجاد PersianDateTimeService

فایل: /BackOffice/src/BackOffice/Services/PersianDateTimeService.cs

سرویسی برای تبدیل تاریخ‌های میلادی به شمسی در لایه نمایش:

public interface IPersianDateTimeService
{
    string GetCurrentWeekNumber();                      // "1404-W23"
    string ConvertWeekNumberToPersian(string);          // "2025-W48" → "1404-W23"
    string ConvertToPersianDate(DateTime);              // DateTime → "1404/09/21"
    string ConvertToPersianDateTime(DateTime);          // DateTime → "1404/09/21 - 14:30"
    string GetWeekRangeDisplay(string);                 // "شنبه 1404/09/15 تا جمعه 1404/09/21"
}

قابلیت‌های کلیدی:

  • تبدیل شماره هفته میلادی به شمسی با حفظ هفته شنبه‌محور
  • فرمت‌دهی تاریخ و تاریخ‌وزمان شمسی
  • نمایش بازه هفتگی با نام روزهای فارسی

1.2. ثبت سرویس در DI Container

فایل: /BackOffice/src/BackOffice/ConfigureService.cs

services.AddSingleton<BackOffice.Services.IPersianDateTimeService, 
                      BackOffice.Services.PersianDateTimeService>();

1.3. آپدیت صفحات فرانت‌اند

Dashboard.razor + Dashboard.razor.cs

تغییرات:

  • Inject کردن IPersianDateTimeService
  • اضافه کردن فیلد _currentWeekNumberPersian
  • تبدیل شماره هفته در OnInitializedAsync و OnWeekChanged
  • نمایش تاریخ محاسبه Pool به شمسی

نمونه کد:

[Inject] public IPersianDateTimeService PersianDateTime { get; set; }
private string _currentWeekNumberPersian = string.Empty;

protected override async Task OnInitializedAsync()
{
    _currentWeekNumber = GetCurrentWeekNumber();  // "2025-W48"
    _currentWeekNumberPersian = PersianDateTime.ConvertWeekNumberToPersian(_currentWeekNumber); // "1404-W23"
}
<MudText Typo="Typo.body2">هفته @(_currentWeekNumberPersian)</MudText>

@if (_poolData?.CalculatedAt != null)
{
    var persianDate = PersianDateTime.ConvertToPersianDateTime(calculatedDate);
    @($"در تاریخ {persianDate}")
}

UserPayouts.razor + UserPayouts.razor.cs

تغییرات:

  • Inject کردن IPersianDateTimeService
  • تبدیل شماره هفته در ستون جدول
  • تبدیل تاریخ ایجاد Payout

نمونه کد:

<PropertyColumn Property="x => x.WeekNumber" Title="هفته">
    <CellTemplate>
        @{
            var persianWeek = PersianDateTime.ConvertWeekNumberToPersian(context.Item.WeekNumber);
        }
        <MudText Typo="Typo.body2">@persianWeek</MudText>
    </CellTemplate>
</PropertyColumn>

WorkerControl.razor

تغییرات:

  • Inject کردن IPersianDateTimeService
  • تبدیل تاریخ آخرین اجرا و اجرای بعدی Worker
  • تبدیل شماره هفته و تاریخ در لاگ اجرا
  • نمایش پیام تایید با هفته شمسی

نمونه کد:

<tr>
    <td><strong>آخرین اجرا:</strong></td>
    <td>@PersianDateTime.ConvertToPersianDateTime(_lastRunTime)</td>
</tr>

<MudTd DataLabel="هفته">@PersianDateTime.ConvertWeekNumberToPersian(context.WeekNumber)</MudTd>

1.4. استراتژی معماری

بک‌اند (CMS):

  • ذخیره و محاسبه با تاریخ میلادی
  • شماره هفته فرمت میلادی: "2025-W48"
  • هفته از شنبه شروع می‌شود

فرانت‌اند (BackOffice):

  • دریافت داده‌های میلادی از API
  • تبدیل به شمسی فقط در لایه نمایش (Presentation Layer)
  • هیچ تغییری در API Call ها یا Database

مزایا:

  • جداسازی کامل Business Logic از Presentation
  • امکان تغییر نمایش بدون تأثیر بر دیتابیس
  • سازگاری با APIهای خارجی که میلادی هستند

بخش 2: بهبود سرویس GetUserNetworkPosition

2.1. آپدیت UserNetworkPositionDto (CMS)

فایل: /CMS/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetUserNetworkPosition/UserNetworkPositionDto.cs

فیلدهای اضافه شده (28+ فیلد جدید):

اطلاعات شخصی کاربر

public string? Email { get; set; }
public string? NationalCode { get; set; }
public string ReferralCode { get; set; }
public bool IsMobileVerified { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime JoinedAt { get; set; }

اطلاعات والد (تکمیل شده)

public string? ParentFullName { get; set; }

اطلاعات فرزندان مستقیم (جزئیات کامل)

// فرزند چپ
public long? LeftChildId { get; set; }
public string? LeftChildFullName { get; set; }
public string? LeftChildMobile { get; set; }
public DateTime? LeftChildJoinedAt { get; set; }

// فرزند راست
public long? RightChildId { get; set; }
public string? RightChildFullName { get; set; }
public string? RightChildMobile { get; set; }
public DateTime? RightChildJoinedAt { get; set; }

آمار کامل شبکه

public int TotalLeftLegMembers { get; set; }      // کل اعضای شاخه چپ (همه سطوح)
public int TotalRightLegMembers { get; set; }     // کل اعضای شاخه راست (همه سطوح)
public int TotalNetworkSize { get; set; }         // کل اعضای شبکه
public int MaxNetworkDepth { get; set; }          // حداکثر عمق شبکه

اطلاعات پکیج و دایا

public bool HasReceivedDayaCredit { get; set; }
public DateTime? DayaCreditReceivedAt { get; set; }
public PackagePurchaseMethod PackagePurchaseMethod { get; set; }
public bool HasPurchasedGoldenPackage { get; set; }

آمار مالی (کمیسیون)

public decimal TotalEarnedCommission { get; set; }  // کل کمیسیون کسب شده
public decimal TotalPaidCommission { get; set; }    // کمیسیون پرداخت شده
public decimal PendingCommission { get; set; }      // کمیسیون در انتظار
public int TotalBalancesEarned { get; set; }        // تعداد بالانس‌های کسب شده

آمار فعالیت

public int ActiveMembersInNetwork { get; set; }     // اعضای فعال (پکیج خریده)
public int InactiveMembersInNetwork { get; set; }   // اعضای غیرفعال

2.2. آپدیت GetUserNetworkPositionQueryHandler

فایل: /CMS/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetUserNetworkPosition/GetUserNetworkPositionQueryHandler.cs

متدهای کمکی جدید:

/// <summary>
/// محاسبه تعداد اعضای یک شاخه (چپ یا راست) به صورت بازگشتی
/// </summary>
private async Task<int> GetLegMemberCountAsync(long userId, NetworkLeg leg, CancellationToken cancellationToken)

/// <summary>
/// محاسبه حداکثر عمق شبکه
/// </summary>
private async Task<int> GetMaxNetworkDepthAsync(long userId, CancellationToken cancellationToken)

/// <summary>
/// دریافت تمام ID های زیرمجموعه یک کاربر
/// </summary>
private async Task<List<long>> GetAllDescendantIdsAsync(long userId, CancellationToken cancellationToken)

کوئری‌های جدید:

  • محاسبه آمار کمیسیون از جدول UserCommissionPayouts
  • شمارش اعضای فعال/غیرفعال بر اساس PackagePurchaseMethod
  • واکشی اطلاعات کامل فرزندان با موبایل و تاریخ عضویت

2.3. آپدیت Protobuf Messages

فایل‌ها:

  • /CMS/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto
  • /BackOffice.BFF/src/Protobufs/BackOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto

تغییرات: افزایش فیلدها از 14 به 42 فیلد

message GetUserNetworkResponse
{
    // اطلاعات اصلی کاربر
    int64 id = 1;
    int64 user_id = 2;
    string user_name = 3;
    string mobile = 4;
    string email = 5;
    string national_code = 6;
    string referral_code = 7;
    bool is_mobile_verified = 8;
    google.protobuf.Timestamp birth_date = 9;
    google.protobuf.Timestamp joined_at = 10;
    
    // اطلاعات والد
    google.protobuf.Int64Value parent_id = 11;
    string parent_name = 12;
    string parent_mobile = 13;
    
    // موقعیت در شبکه
    int32 network_leg = 14;
    int32 network_level = 15;
    bool is_in_network = 16;
    
    // اطلاعات فرزند چپ
    google.protobuf.Int64Value left_child_id = 17;
    string left_child_name = 18;
    string left_child_mobile = 19;
    google.protobuf.Timestamp left_child_joined_at = 20;
    
    // اطلاعات فرزند راست
    google.protobuf.Int64Value right_child_id = 21;
    string right_child_name = 22;
    string right_child_mobile = 23;
    google.protobuf.Timestamp right_child_joined_at = 24;
    
    // آمار فرزندان مستقیم
    int32 total_children = 25;
    int32 left_child_count = 26;
    int32 right_child_count = 27;
    
    // آمار کل شبکه
    int32 total_left_leg_members = 28;
    int32 total_right_leg_members = 29;
    int32 total_network_size = 30;
    int32 max_network_depth = 31;
    
    // اطلاعات پکیج و دایا
    bool has_received_daya_credit = 32;
    google.protobuf.Timestamp daya_credit_received_at = 33;
    int32 package_purchase_method = 34;
    bool has_purchased_golden_package = 35;
    
    // آمار مالی
    double total_earned_commission = 36;
    double total_paid_commission = 37;
    double pending_commission = 38;
    int32 total_balances_earned = 39;
    
    // آمار فعالیت
    int32 active_members_in_network = 40;
    int32 inactive_members_in_network = 41;
    
    google.protobuf.Timestamp created = 42;
}

2.4. آپدیت CMS Mapping Profile

فایل: /CMS/src/CMSMicroservice.WebApi/Common/Mappings/NetworkMembershipProfile.cs

تغییرات: 40+ خط mapping برای تمام فیلدهای جدید

config.NewConfig<UserNetworkPositionDto, GetUserNetworkResponse>()
    .Map(dest => dest.Mobile, src => src.Mobile ?? "")
    .Map(dest => dest.Email, src => src.Email ?? "")
    .Map(dest => dest.NationalCode, src => src.NationalCode ?? "")
    .Map(dest => dest.ReferralCode, src => src.ReferralCode)
    .Map(dest => dest.IsMobileVerified, src => src.IsMobileVerified)
    // ... 35+ mappings دیگر
    .Map(dest => dest.TotalEarnedCommission, src => (double)src.TotalEarnedCommission)
    .Map(dest => dest.ActiveMembersInNetwork, src => src.ActiveMembersInNetwork);

2.5. آپدیت BackOffice BFF

GetUserNetworkInfoResponseDto

فایل: /BackOffice.BFF/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetUserNetworkInfo/GetUserNetworkInfoResponseDto.cs

تغییرات: همان 42 فیلد CMS برای consistency

NetworkMembershipProfile (BFF)

فایل: /BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/NetworkMembershipProfile.cs

تغییرات: Mapping کامل از DTO به Protobuf Response با تبدیل DateTime به Timestamp

config.NewConfig<GetUserNetworkInfoResponseDto, GetUserNetworkResponse>()
    .MapWith(src => new GetUserNetworkResponse
    {
        // ... 42 field mapping با تبدیل صحیح DateTime ها
        BirthDate = src.BirthDate.HasValue 
            ? Timestamp.FromDateTime(DateTime.SpecifyKind(src.BirthDate.Value, DateTimeKind.Utc)) 
            : null,
        // ...
    });

2.6. آپدیت صفحه UserNetworkInfo.razor

فایل: /BackOffice/src/BackOffice/Pages/Network/UserNetworkInfo.razor

بازنویسی کامل UI با 6 کارت اصلی:

1. کارت اطلاعات کاربر

  • شناسه، نام، موبایل (با badge تایید)
  • ایمیل، کد ملی
  • کد ارجاع
  • موقعیت در شبکه
  • تاریخ عضویت (شمسی)

2. کارت ساختار شبکه

  • اطلاعات والد (نام، موبایل، لینک)
  • فرزند چپ (نام، موبایل، تاریخ عضویت، لینک)
  • فرزند راست (نام، موبایل، تاریخ عضویت، لینک)

3. کارت آمار کامل شبکه (6 آیتم با آیکون)

<MudGrid>
    <MudItem xs="12" sm="6" md="3">
        <!-- کل اعضای شبکه -->
        <MudIcon Icon="@Icons.Material.Filled.AccountTree" />
        <MudText Typo="Typo.h4">@_userInfo.TotalNetworkSize</MudText>
    </MudItem>
    <!-- اعضای شاخه چپ -->
    <!-- اعضای شاخه راست -->
    <!-- حداکثر عمق شبکه -->
    <!-- اعضای فعال -->
    <!-- اعضای غیرفعال -->
</MudGrid>

4. کارت آمار مالی و کمیسیون

  • کل کمیسیون کسب شده (با فرمت هزارگان)
  • کمیسیون پرداخت شده
  • کمیسیون در انتظار
  • تعداد بالانس کسب شده

5. کارت وضعیت پکیج و دایا

  • وضعیت پکیج طلایی (با روش خرید)
  • وضعیت اعتبار دایا (با تاریخ دریافت شمسی)

6. کارت عملیات

  • دکمه نمایش درخت کامل
  • دکمه Payout های کاربر (جدید)
  • دکمه بروزرسانی

ویژگی‌های UI:

  • استفاده از MudBlazor Components
  • آیکون‌های Material Design
  • رنگ‌بندی semantic (Success, Warning, Info, Error)
  • فرمت هزارگان برای مبالغ ریالی
  • تاریخ‌های شمسی با PersianDateTimeService

بخش 3: اصلاح الگوریتم محاسبه شماره هفته

3.1. مشکل اولیه

علت: استفاده از CalendarWeekRule.FirstDay در C# که محاسبه اشتباه می‌کرد

نتیجه:

  • C# (GetAvailableWeeksQueryHandler): هفته 50
  • SQL (populate-weekly-commission-pools.sql): هفته 49

3.2. محاسبه صحیح (Saturday-based)

برای تاریخ 2025-12-12 (پنجشنبه):

  1. اولین روز سال: 2025-01-01 = چهارشنبه
  2. اولین شنبه سال: 2025-01-04
  3. شنبه این هفته: 2025-12-07
  4. فاصله: 337 روز
  5. شماره هفته: 337 ÷ 7 = 48.14 → هفته 49

3.3. آپدیت GetAvailableWeeksQueryHandler

فایل: /CMS/src/CMSMicroservice.Application/CommissionCQ/Queries/GetAvailableWeeks/GetAvailableWeeksQueryHandler.cs

قبل:

private static string GetWeekNumber(DateTime date)
{
    var calendar = CultureInfo.InvariantCulture.Calendar;
    var weekOfYear = calendar.GetWeekOfYear(
        date, 
        CalendarWeekRule.FirstDay,  // ❌ اشتباه
        DayOfWeek.Saturday);
    
    return $"{date.Year}-W{weekOfYear:D2}";
}

بعد:

private static string GetWeekNumber(DateTime date)
{
    var year = date.Year;
    
    // پیدا کردن اولین شنبه سال
    var jan1 = new DateTime(year, 1, 1);
    var jan1DayOfWeek = (int)jan1.DayOfWeek;
    
    // محاسبه offset تا اولین شنبه
    var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
    var firstSaturday = jan1.AddDays(daysToFirstSaturday);
    
    // پیدا کردن شنبه شروع هفته جاری
    var currentDayOfWeek = (int)date.DayOfWeek;
    var daysToCurrentSaturday = currentDayOfWeek == 6 ? 0 : (currentDayOfWeek + 1) % 7;
    var weekStartSaturday = date.Date.AddDays(-daysToCurrentSaturday);
    
    // محاسبه شماره هفته
    int weekNum;
    if (weekStartSaturday < firstSaturday)
    {
        weekNum = 1;
    }
    else
    {
        var daysSinceFirstSaturday = (weekStartSaturday - firstSaturday).Days;
        weekNum = (daysSinceFirstSaturday / 7) + 1;
    }
    
    return $"{year}-W{weekNum:D2}";
}

3.4. آپدیت SQL Script

فایل: /dbbkup/populate-weekly-commission-pools.sql

تغییرات مشابه در تابع GetWeekNumber:

CREATE FUNCTION dbo.GetWeekNumber (@Date DATETIME)
RETURNS NVARCHAR(10)
AS
BEGIN
    DECLARE @Year INT = YEAR(@Date);
    
    -- پیدا کردن اولین شنبه سال
    DECLARE @Jan1 DATE = CAST(CAST(@Year AS VARCHAR(4)) + '-01-01' AS DATE);
    DECLARE @Jan1DayOfWeek INT = DATEPART(WEEKDAY, @Jan1);
    
    -- محاسبه offset
    DECLARE @DaysToFirstSaturday INT;
    IF @Jan1DayOfWeek = 7
        SET @DaysToFirstSaturday = 0;
    ELSE
        SET @DaysToFirstSaturday = 7 - @Jan1DayOfWeek;
    
    DECLARE @FirstSaturday DATE = DATEADD(DAY, @DaysToFirstSaturday, @Jan1);
    
    -- پیدا کردن شنبه شروع هفته جاری
    DECLARE @CurrentDayOfWeek INT = DATEPART(WEEKDAY, @Date);
    DECLARE @DaysToCurrentSaturday INT;
    
    IF @CurrentDayOfWeek = 7
        SET @DaysToCurrentSaturday = 0;
    ELSE
        SET @DaysToCurrentSaturday = @CurrentDayOfWeek - 1;
    
    DECLARE @WeekStartSaturday DATE = DATEADD(DAY, -@DaysToCurrentSaturday, @Date);
    
    -- محاسبه شماره هفته
    DECLARE @WeekNum INT;
    IF @WeekStartSaturday < @FirstSaturday
        SET @WeekNum = 1;
    ELSE
    BEGIN
        DECLARE @DaysSinceFirstSaturday INT = DATEDIFF(DAY, @FirstSaturday, @WeekStartSaturday);
        SET @WeekNum = (@DaysSinceFirstSaturday / 7) + 1;
    END
    
    RETURN CAST(@Year AS NVARCHAR(4)) + '-W' + RIGHT('0' + CAST(@WeekNum AS NVARCHAR(2)), 2);
END

3.5. سایر فایل‌های آپدیت شده

CalculateWeeklyBalancesCommandHandler.cs:

  • متد GetWeekDateRange() با الگوریتم دقیق‌تر

GetAvailableWeeksQueryHandler.cs:

  • متد GetWeekRange() برای محاسبه بازه شنبه تا جمعه

همه یکپارچه شدند: C# ≡ SQL ≡ Frontend Display


خلاصه فایل‌های تغییر یافته

فایل‌های جدید

  1. /BackOffice/src/BackOffice/Services/PersianDateTimeService.cs جدید

فایل‌های CMS

  1. /CMS/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetUserNetworkPosition/UserNetworkPositionDto.cs
  2. /CMS/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetUserNetworkPosition/GetUserNetworkPositionQueryHandler.cs
  3. /CMS/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto
  4. /CMS/src/CMSMicroservice.WebApi/Common/Mappings/NetworkMembershipProfile.cs
  5. /CMS/src/CMSMicroservice.Application/CommissionCQ/Queries/GetAvailableWeeks/GetAvailableWeeksQueryHandler.cs
  6. /CMS/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs

فایل‌های BackOffice.BFF

  1. /BackOffice.BFF/src/Protobufs/BackOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto
  2. /BackOffice.BFF/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetUserNetworkInfo/GetUserNetworkInfoResponseDto.cs
  3. /BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/NetworkMembershipProfile.cs

فایل‌های BackOffice (Frontend)

  1. /BackOffice/src/BackOffice/ConfigureService.cs
  2. /BackOffice/src/BackOffice/Pages/Commission/Dashboard.razor
  3. /BackOffice/src/BackOffice/Pages/Commission/Dashboard.razor.cs
  4. /BackOffice/src/BackOffice/Pages/Commission/UserPayouts.razor
  5. /BackOffice/src/BackOffice/Pages/Commission/UserPayouts.razor.cs
  6. /BackOffice/src/BackOffice/Pages/SystemManagement/WorkerControl.razor
  7. /BackOffice/src/BackOffice/Pages/Network/UserNetworkInfo.razor

فایل‌های SQL

  1. /dbbkup/populate-weekly-commission-pools.sql

نتایج و دستاوردها

سیستم تاریخ شمسی

  • 3 صفحه اصلی به شمسی تبدیل شد
  • صفر تغییر در Backend یا Database
  • معماری پاک با جداسازی Presentation از Business Logic
  • Performance: سرویس Singleton بدون overhead

بهبود سرویس شبکه

  • 28+ فیلد جدید اضافه شد
  • 3 متد بازگشتی برای محاسبه آمار شبکه
  • یکپارچگی کامل از CMS تا UI
  • UI کاملا بازنویسی شد با 6 کارت اطلاعاتی

اصلاح الگوریتم هفته

  • یکپارچگی کامل بین C#, SQL, Frontend
  • محاسبه دقیق Saturday-based
  • صفر اختلاف بین سیستم‌ها

📊 آمار کلی

  • 17 فایل ویرایش شد
  • 1 فایل جدید ایجاد شد
  • 42 فیلد Protobuf به جای 14 فیلد
  • 3 صفحه Frontend به شمسی تبدیل شد
  • 2 الگوریتم (C# + SQL) یکپارچه شد

تست و Validation

Build Status

  • CMS: Build Successful (0 Errors, 465 Warnings - معمولی)
  • BackOffice.BFF: Build Successful (0 Errors, 199 Warnings - معمولی)
  • BackOffice: Build Successful (0 Errors, 239 Warnings - MudBlazor)

محاسبات تست شده

  • تاریخ 2025-12-12 → هفته 49 (یکسان در همه سیستم‌ها)
  • تبدیل شمسی "1404/09/21" ← 2025-12-12
  • محاسبه بازه هفته: شنبه 2025-12-07 تا جمعه 2025-12-13

توصیه‌های آینده

کارهای تکمیلی پیشنهادی

  1. Component Reusability: ایجاد Blazor Components مشترک برای نمایش تاریخ شمسی

    <PersianDateDisplay DateTime="@dateTime" ShowTime="true" />
    <PersianWeekDisplay WeekNumber="@weekNumber" ShowRange="true" />
    
  2. Caching: اضافه کردن Cache برای محاسبات تبدیل هفته (اگر Performance مشکل شد)

  3. Testing: نوشتن Unit Test برای GetWeekNumber در C# و SQL

  4. Documentation: اضافه کردن XML Comments بیشتر برای API Documentation

  5. صفحات باقیمانده: اگر صفحات دیگری تاریخ نمایش می‌دهند، آن‌ها را هم تبدیل کنید


نکات فنی مهم

Saturday-based Week Calculation

هفته از شنبه شروع می‌شود:
- شنبه: روز اول هفته
- جمعه: روز آخر هفته
- Week 1: اولین شنبه سال

DateTime to Timestamp Conversion

// در Protobuf mapping همیشه UTC specify کنید
Timestamp.FromDateTime(DateTime.SpecifyKind(dateTime, DateTimeKind.Utc))

Persian Calendar in C#

private readonly PersianCalendar _persianCalendar = new();
var persianYear = _persianCalendar.GetYear(dateTime);
var persianMonth = _persianCalendar.GetMonth(dateTime);
var persianDay = _persianCalendar.GetDayOfMonth(dateTime);

تاریخ: 2025-12-12
مدت زمان: 1 Session
وضعیت: Completed & Tested
تیم: Masoud + GitHub Copilot