Skip to main content
BuildingBlocks.Time, sistem saatini soyutlar. Kodun her yerinde DateTime.UtcNow çağırmak yerine IClock (veya statik Time fasadı) kullanılır; böylece zamana bağlı mantık (OTP süresi, TOTP pencere hesabı, token expiry) deterministik şekilde test edilebilir olur.

Neden zaman soyutlanır?

DateTime.UtcNow global ve kontrol edilemez bir bağımlılıktır. Soyutlama olmadan şu testler kararsız (flaky) olur:
  • OTP’nin 3 dakika sonra süresinin dolması
  • TOTP’nin ±1 zaman penceresinde doğrulanması
  • Refresh token’ın geçerlilik kontrolü
IClock/FakeClock ile zaman sabitlenir ve “tam sınırda ne olur?” senaryoları net biçimde sınanır.

Arayüz ve implementasyonlar

TipÜyeAçıklama
IClockDateTime UtcNow { get; }Soyut sistem saati
SystemClock : IClockstatic SystemClock Instance · UtcNow => DateTime.UtcNowProd — singleton
FakeClock : IClockFakeClock(DateTime utc) · UtcNow => _utcTest — sabit zaman
public interface IClock
{
    DateTime UtcNow { get; }
}

Statik Time fasadı

DI gerektirmeyen, async-flow güvenli statik bir giriş noktası da vardır. AsyncLocal tabanlı scoped override ile testte zamanı geçici olarak değiştirir; ayrıca Türkiye yerel saatini sağlar.
ÜyeDöner
Time.UtcNowOverride-aware UTC (yoksa SystemClock)
Time.NowEurope/Istanbul yerel saati
Time.TodayTürkiye tarihi
Time.Override(IClock)IDisposable Scope — dispose’da önceki saate döner

DI kaydı

IClock singleton olarak kaydedilir (prod’da SystemClock).
// Program.cs / Infrastructure DI
services.AddSingleton<IClock>(SystemClock.Instance);
// Kullanım — zamana bağlı mantık
public sealed class OtpChallenge(IClock clock)
{
    private readonly DateTime _expiresAt;
    public OtpChallenge(IClock clock, int expireMinutes)
        => _expiresAt = clock.UtcNow.AddMinutes(expireMinutes);

    public bool IsExpired(IClock clock) => clock.UtcNow >= _expiresAt;
}

Test — sabit zaman

FakeClock ile “şimdi”yi sabitleyip sınır koşullarını deterministik test edin:
[Fact]
public void Otp_Tam_3_Dakika_Sonra_Suresi_Dolar()
{
    var t0 = new DateTime(2026, 6, 3, 10, 0, 0, DateTimeKind.Utc);
    IClock atStart = new FakeClock(t0);

    var challenge = new OtpChallenge(atStart, expireMinutes: 3);

    // 2 dk 59 sn → hâlâ geçerli
    Assert.False(challenge.IsExpired(new FakeClock(t0.AddSeconds(179))));
    // 3 dk 00 sn → süresi doldu
    Assert.True(challenge.IsExpired(new FakeClock(t0.AddMinutes(3))));
}
Statik Time kullanan kod için scoped override:
using (Time.Override(new FakeClock(new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc))))
{
    // bu blok içinde Time.UtcNow sabit; dispose'da gerçek saate döner
    var result = ServiceUsingStaticTime.DoWork();
    Assert.Equal(2026, result.Year);
}
FakeClock salt-okunur sabit zaman tutar (mutable “advance” yoktur); ilerleyen zaman gerektiren senaryolarda farklı FakeClock instance’ları geçirin ya da Time.Override’ı yeni bir saatle tekrar sarın.
Time fasadı yerel saat için Europe/Istanbul’u (yoksa “Turkey Standard Time”) çözer. Kalıcı veriyi daima UTC saklayın; yerelleştirmeyi yalnızca görüntüleme katmanında yapın.

İlgili

OTP

Kod süresi ve throttle hesapları.

Authenticator (TOTP)

RFC6238 zaman penceresi doğrulaması.

JWT

Token expiry ve clock skew.

Domain

Aggregate’lerde zamana bağlı kurallar.