Files
maraphon-app/src/Marathon.Domain/ValueObjects/MoscowTime.cs
T
alexei.dolgolyov fed3a09695 refactor: hoist Moscow offset + sport labels into shared helpers (HIGH)
* New Marathon.Domain.ValueObjects.MoscowTime with Offset, Now, and
  EndOfMoscowDay(DateOnly) — replaces ~15 inline TimeSpan.FromHours(3)
  literals across Domain/Application/Infrastructure/UI.
* New Marathon.UI.Services.SportLabels.Resolve(IStringLocalizer, int) —
  replaces 6 near-identical SportLabel switch bodies in EventListShell,
  Events/Detail, Anomalies/AnomalyFeed, Results/ResultsList,
  Results/ResultsLoader, and AnomalyCard. Single source of truth for the
  6/11/22723/43658 sport-code mapping. Pages keep a one-liner wrapper so
  the call sites stay terse.
2026-05-09 15:40:35 +03:00

33 lines
1.3 KiB
C#

namespace Marathon.Domain.ValueObjects;
/// <summary>
/// Single source of truth for the Moscow timezone offset.
/// </summary>
/// <remarks>
/// <para>
/// marathonbet.by serves all timestamps in Moscow time (UTC+3) and the domain
/// invariant on <see cref="Marathon.Domain.Entities.Event.ScheduledAt"/>
/// rejects any other offset. Code that constructs <see cref="DateTimeOffset"/>
/// values for events, results, snapshots, or test fixtures MUST use this
/// constant rather than re-deriving <c>TimeSpan.FromHours(3)</c>.
/// </para>
/// </remarks>
public static class MoscowTime
{
/// <summary>The Moscow time offset (UTC+3).</summary>
public static readonly TimeSpan Offset = TimeSpan.FromHours(3);
/// <summary>Current Moscow time.</summary>
public static DateTimeOffset Now => DateTimeOffset.UtcNow.ToOffset(Offset);
/// <summary>
/// Returns the inclusive end-of-day for the given Moscow date — i.e.,
/// the moment one second before the next day starts. Used by date-range
/// filters where the user picks "to: 2026-05-09" meaning "through the
/// rest of that day."
/// </summary>
public static DateTimeOffset EndOfMoscowDay(DateOnly date) =>
new DateTimeOffset(date.ToDateTime(TimeOnly.MinValue), Offset)
.AddDays(1).AddSeconds(-1);
}