{"id":"57eb83bf-b983-4ff0-b68f-882c10af9de9","shortId":"7JCAFP","kind":"skill","title":"dotnet-testing-datetime-testing-timeprovider","tagline":"使用 TimeProvider 測試時間相依邏輯的專門技能。當需要測試 DateTime、控制時間流逝、處理時區轉換、測試過期邏輯時使用。涵蓋 TimeProvider 抽象化、FakeTimeProvider 時間控制、時間凍結與快轉等。\nMake sure to use this skill whenever the user mentions DateTime testing, TimeProvider, FakeTimeProvider, time-dependent logic, cache expiration, or token expir","description":"# DateTime 與時間相依性測試指南\n\n## 核心原則\n\n### 原則一：時間抽象化 - 以 TimeProvider 取代 DateTime\n\n**傳統問題程式碼**：\n\n```csharp\n// ❌ 無法測試 - 直接使用靜態時間\npublic class OrderService\n{\n    public bool CanPlaceOrder()\n    {\n        var now = DateTime.Now;\n        return now.Hour >= 9 && now.Hour < 17;\n    }\n}\n```\n\n**可測試的重構**：\n\n```csharp\n// ✅ 可測試 - 透過依賴注入接收 TimeProvider\npublic class OrderService\n{\n    private readonly TimeProvider _timeProvider;\n    \n    public OrderService(TimeProvider timeProvider)\n    {\n        _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));\n    }\n    \n    public bool CanPlaceOrder()\n    {\n        var now = _timeProvider.GetLocalNow();\n        return now.Hour >= 9 && now.Hour < 17;\n    }\n}\n```\n\n**依賴注入設定**：\n\n```csharp\n// Program.cs - 生產環境使用系統時間\nservices.AddSingleton(TimeProvider.System);\nservices.AddScoped<OrderService>();\n```\n\n### 原則二：FakeTimeProvider 控制測試時間\n\nFakeTimeProvider 提供完整的時間控制能力：\n\n| 方法                             | 用途          | 使用時機            |\n| -------------------------------- | ------------- | ------------------- |\n| `SetUtcNow(DateTimeOffset)`      | 設定 UTC 時間 | 需要精確 UTC 時間時 |\n| `SetLocalTimeZone(TimeZoneInfo)` | 設定本地時區  | 測試時區相關邏輯    |\n| `Advance(TimeSpan)`              | 時間快轉      | 測試過期、延遲邏輯  |\n| `GetUtcNow()`                    | 取得 UTC 時間 | 讀取當前模擬時間    |\n| `GetLocalNow()`                  | 取得本地時間  | 讀取本地模擬時間    |\n\n**建議擴充方法**：\n\n```csharp\npublic static class FakeTimeProviderExtensions\n{\n    /// <summary>\n    /// 設定 FakeTimeProvider 的本地時間\n    /// </summary>\n    public static void SetLocalNow(this FakeTimeProvider fakeTimeProvider, DateTime localDateTime)\n    {\n        fakeTimeProvider.SetLocalTimeZone(TimeZoneInfo.Local);\n        var utcTime = TimeZoneInfo.ConvertTimeToUtc(localDateTime, TimeZoneInfo.Local);\n        fakeTimeProvider.SetUtcNow(utcTime);\n    }\n}\n```\n\n### 原則三：每個測試使用獨立的時間環境\n\n```csharp\n// ✅ 正確：每個測試獨立建立 FakeTimeProvider\npublic class OrderServiceTests\n{\n    [Fact]\n    public void CanPlaceOrder_在營業時間內_應回傳True()\n    {\n        // Arrange - 獨立實例\n        var fakeTimeProvider = new FakeTimeProvider();\n        fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, 14, 0, 0));\n        var sut = new OrderService(fakeTimeProvider);\n        \n        // Act\n        var result = sut.CanPlaceOrder();\n        \n        // Assert\n        result.Should().BeTrue();\n    }\n}\n\n// ❌ 避免：多個測試共用靜態實例\npublic class BadTestClass\n{\n    private static readonly FakeTimeProvider SharedProvider = new(); // 會互相干擾\n}\n```\n\n---\n\n## 進階時間控制技術\n\n### 時間凍結\n\n當需要驗證多個操作發生在「同一時間點」：\n\n```csharp\n[Fact]\npublic void ProcessBatch_在固定時間點_應產生相同時間戳()\n{\n    var fakeTimeProvider = new FakeTimeProvider();\n    var fixedTime = new DateTime(2024, 12, 25, 10, 30, 0);\n    fakeTimeProvider.SetLocalNow(fixedTime);\n    \n    var processor = new BatchProcessor(fakeTimeProvider);\n    \n    var result1 = processor.ProcessItem(\"Item1\");\n    var result2 = processor.ProcessItem(\"Item2\");\n    \n    // 時間被凍結，兩次操作的時間戳相同\n    result1.Timestamp.Should().Be(result2.Timestamp);\n}\n```\n\n### 時間快轉 (Advance)\n\n測試快取過期、Token 失效等時間敏感邏輯：\n\n```csharp\n[Fact]\npublic void Cache_經過過期時間_應清除項目()\n{\n    var fakeTimeProvider = new FakeTimeProvider();\n    fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, 10, 0, 0));\n    \n    var cache = new TimedCache(fakeTimeProvider, TimeSpan.FromMinutes(5));\n    cache.Set(\"key\", \"value\");\n    \n    // 3 分鐘後 - 尚未過期\n    fakeTimeProvider.Advance(TimeSpan.FromMinutes(3));\n    cache.Get(\"key\").Should().Be(\"value\");\n    \n    // 再 3 分鐘後（共 6 分鐘）- 已過期\n    fakeTimeProvider.Advance(TimeSpan.FromMinutes(3));\n    cache.Get(\"key\").Should().BeNull();\n}\n```\n\n> **重要**：`Advance()` 是非阻塞的，瞬間完成時間跳躍，不會真正等待。\n\n### 時間倒轉\n\n測試歷史資料處理或重播場景：\n\n```csharp\n[Fact]\npublic void HistoricalDataProcessor_回到過去時間_應正確處理()\n{\n    var fakeTimeProvider = new FakeTimeProvider();\n    var historicalTime = new DateTime(2020, 1, 15, 9, 0, 0);\n    fakeTimeProvider.SetLocalNow(historicalTime);\n    \n    var processor = new HistoricalDataProcessor(fakeTimeProvider);\n    var result = processor.ProcessDataForDate(historicalTime.Date);\n    \n    result.ProcessedAt.Should().Be(historicalTime);\n}\n```\n\n---\n\n## 實戰測試模式\n\n### 模式一：參數化邊界測試\n\n```csharp\n[Theory]\n[InlineData(8, false)]   // 上午 8 點 - 營業時間前\n[InlineData(9, true)]    // 上午 9 點 - 剛開始營業\n[InlineData(12, true)]   // 中午 12 點 - 營業時間內\n[InlineData(16, true)]   // 下午 4 點 - 營業時間內\n[InlineData(17, false)]  // 下午 5 點 - 剛結束營業\n[InlineData(18, false)]  // 下午 6 點 - 營業時間後\npublic void CanPlaceOrder_不同時間點_應回傳正確結果(int hour, bool expected)\n{\n    var fakeTimeProvider = new FakeTimeProvider();\n    fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, hour, 0, 0));\n    \n    var sut = new OrderService(fakeTimeProvider);\n    \n    sut.CanPlaceOrder().Should().Be(expected);\n}\n```\n\n### 模式二：交易時間窗口測試\n\n```csharp\n[Theory]\n[InlineData(\"09:30:00\", true)]   // 上午交易時間\n[InlineData(\"12:00:00\", false)]  // 中午休息\n[InlineData(\"14:30:00\", true)]   // 下午交易時間\n[InlineData(\"15:30:00\", false)]  // 交易結束後\npublic void IsInTradingHours_不同時間_應回傳正確結果(string timeStr, bool expected)\n{\n    var fakeTimeProvider = new FakeTimeProvider();\n    var testTime = DateTime.Today.Add(TimeSpan.Parse(timeStr));\n    fakeTimeProvider.SetLocalNow(testTime);\n    \n    var sut = new TradingService(fakeTimeProvider);\n    \n    sut.IsInTradingHours().Should().Be(expected);\n}\n```\n\n### 模式三：排程觸發邏輯測試\n\n```csharp\n[Theory]\n[InlineData(\"2024-03-15 14:30:00\", \"2024-03-15 14:00:00\", true)]   // 已到執行時間\n[InlineData(\"2024-03-15 13:30:00\", \"2024-03-15 14:00:00\", false)]  // 尚未到時間\npublic void ShouldExecuteJob_根據時間判斷_應回傳正確結果(\n    string currentTimeStr, string scheduledTimeStr, bool expected)\n{\n    var fakeTimeProvider = new FakeTimeProvider();\n    fakeTimeProvider.SetLocalNow(DateTime.Parse(currentTimeStr));\n    \n    var schedule = new JobSchedule { NextExecutionTime = DateTime.Parse(scheduledTimeStr) };\n    var sut = new ScheduleService(fakeTimeProvider);\n    \n    sut.ShouldExecuteJob(schedule).Should().Be(expected);\n}\n```\n\n---\n\n## AutoFixture 整合\n\n### FakeTimeProviderCustomization\n\n```csharp\npublic class FakeTimeProviderCustomization : ICustomization\n{\n    public void Customize(IFixture fixture)\n    {\n        fixture.Register(() => new FakeTimeProvider());\n    }\n}\n```\n\n### AutoDataWithCustomization 屬性\n\n```csharp\npublic class AutoDataWithCustomizationAttribute : AutoDataAttribute\n{\n    public AutoDataWithCustomizationAttribute() : base(CreateFixture)\n    {\n    }\n    \n    private static IFixture CreateFixture()\n    {\n        return new Fixture()\n            .Customize(new AutoNSubstituteCustomization())\n            .Customize(new FakeTimeProviderCustomization());\n    }\n}\n```\n\n### 使用 Matching.DirectBaseType\n\n```csharp\n[Theory]\n[AutoDataWithCustomization]\npublic void GetTimeBasedDiscount_週五_應回傳九折優惠(\n    [Frozen(Matching.DirectBaseType)] FakeTimeProvider fakeTimeProvider,\n    OrderService sut)\n{\n    // Matching.DirectBaseType 讓 AutoFixture 知道：\n    // 當需要 TimeProvider（基底類型）時，使用 FakeTimeProvider（衍生類型）\n    \n    var fridayTime = new DateTime(2024, 3, 15, 14, 0, 0); // 週五\n    fakeTimeProvider.SetLocalNow(fridayTime);\n    \n    sut.GetTimeBasedDiscount().Should().Be(\"週五快樂：九折優惠\");\n}\n```\n\n> **關鍵**：必須使用 `[Frozen(Matching.DirectBaseType)]`，否則 AutoFixture 無法正確將 FakeTimeProvider 注入到需要 TimeProvider 的建構式中。\n\n---\n\n## 最佳實踐檢查清單\n\n### 程式碼設計\n\n- [ ] 所有時間相依類別透過建構式接收 `TimeProvider`\n- [ ] 使用 `_timeProvider.GetLocalNow()` 取代 `DateTime.Now`\n- [ ] 使用 `_timeProvider.GetUtcNow()` 取代 `DateTime.UtcNow`\n- [ ] DI 容器註冊 `TimeProvider.System` 作為生產環境實作\n\n### 測試設計\n\n- [ ] 每個測試方法使用獨立的 `FakeTimeProvider` 實例\n- [ ] 使用 `SetLocalNow()` 擴充方法簡化時間設定\n- [ ] 使用 `Advance()` 測試時間敏感邏輯（快取、過期、延遲）\n- [ ] 測試涵蓋邊界條件（開始時間、結束時間、臨界點）\n\n### 進階考量\n\n- [ ] FakeTimeProvider 是執行緒安全的，可用於並行測試\n- [ ] 使用 `IDisposable` 模式正確釋放 FakeTimeProvider\n- [ ] 時區測試使用 `SetLocalTimeZone()` 明確設定時區\n\n---\n\n## 輸出格式\n\n- 產生使用 TimeProvider 抽象的服務類別\n- 產生使用 FakeTimeProvider 的測試類別\n- 包含時間凍結、快轉、時區轉換測試範例\n- 提供 .csproj 套件參考（Microsoft.Bcl.TimeProvider）\n\n## 參考資源\n\n### 原始文章\n\n本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章：\n\n- **Day 16 - 測試日期與時間：Microsoft.Bcl.TimeProvider 取代 DateTime**\n  - 鐵人賽文章：https://ithelp.ithome.com.tw/articles/10375821\n  - 範例程式碼：https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day16\n\n### 官方文件\n\n- [TimeProvider API](https://learn.microsoft.com/zh-tw/dotnet/api/system.timeprovider)\n- [Microsoft.Bcl.TimeProvider NuGet](https://www.nuget.org/packages/Microsoft.Bcl.TimeProvider/)\n- [Microsoft.Extensions.TimeProvider.Testing NuGet](https://www.nuget.org/packages/Microsoft.Extensions.TimeProvider.Testing/)\n\n### 相關技能\n\n- `autofixture-basics` - AutoFixture 自動測試資料生成\n- `nsubstitute-mocking` - 測試替身與模擬\n- `autodata-xunit-integration` - xUnit 與 AutoFixture 的 AutoData 整合","tags":["dotnet","testing","datetime","timeprovider","agent","skills","kevintsengtw","agent-skills","ai-assisted-development","copilot-skills","csharp","dotnet-testing"],"capabilities":["skill","source-kevintsengtw","skill-dotnet-testing-datetime-testing-timeprovider","topic-agent-skills","topic-ai-assisted-development","topic-copilot-skills","topic-csharp","topic-dotnet","topic-dotnet-testing","topic-github-copilot","topic-integration-testing","topic-testing","topic-unit-testing","topic-xunit"],"categories":["dotnet-testing-agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/kevintsengtw/dotnet-testing-agent-skills/dotnet-testing-datetime-testing-timeprovider","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add kevintsengtw/dotnet-testing-agent-skills","source_repo":"https://github.com/kevintsengtw/dotnet-testing-agent-skills","install_from":"skills.sh"}},"qualityScore":"0.461","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 23 github stars · SKILL.md body (8,604 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-24T13:02:26.825Z","embedding":null,"createdAt":"2026-04-18T23:04:29.507Z","updatedAt":"2026-04-24T13:02:26.825Z","lastSeenAt":"2026-04-24T13:02:26.825Z","tsv":"'-03':514,520,529,535 '-15':515,521,530,536 '/articles/10375821':747 '/kevintsengtw/30days_in_testing_samples/tree/main/day16':751 '/packages/microsoft.bcl.timeprovider/)':762 '/packages/microsoft.extensions.timeprovider.testing/)':767 '/zh-tw/dotnet/api/system.timeprovider)':757 '0':200,201,250,294,295,357,358,440,441,652,653 '00':458,463,464,470,476,518,523,524,533,538,539 '09':456 '1':354 '10':248,293 '12':246,393,396,462 '13':531 '14':199,468,516,522,537,651 '15':198,292,355,438,474,650 '16':400,739 '17':70,104,407 '18':414 '2020':353 '2024':196,245,290,436,513,519,528,534,648 '25':247 '3':197,291,306,311,318,326,437,649 '30':249,457,469,475,517,532,735 '4':403 '5':302,410 '6':321,417 '8':379,382 '9':68,102,356,386,389 'act':207 'advanc':132,272,332,697 'api':754 'argumentnullexcept':91 'arrang':187 'assert':211 'autodata':779,786 'autodata-xunit-integr':778 'autodataattribut':599 'autodatawithcustom':593,621 'autodatawithcustomizationattribut':598,601 'autofixtur':577,635,667,770,772,784 'autofixture-bas':769 'autonsubstitutecustom':613 'badtestclass':218 'base':602 'basic':771 'batchprocessor':256 'benul':330 'betru':213 'bool':61,95,427,486,551 'cach':39,280,297 'cache.get':312,327 'cache.set':303 'canplaceord':62,96,184,422 'class':58,77,149,179,217,582,597 'createfixtur':603,607 'csharp':54,72,106,146,174,230,276,338,376,453,510,580,595,619 'csproj':728 'currenttimestr':548,559 'custom':587,611,614 'datetim':4,11,31,44,52,161,195,244,289,352,435,647,743 'datetime.now':65,680 'datetime.parse':558,565 'datetime.today.add':494 'datetime.utcnow':684 'datetimeoffset':121 'day':738 'depend':37 'di':685 'dotnet':2 'dotnet-testing-datetime-testing-timeprovid':1 'expect':428,450,487,507,552,576 'expir':40,43 'fact':181,231,277,339 'faketimeprovid':18,34,113,115,152,159,160,177,190,192,206,222,238,240,257,284,286,300,346,348,365,430,432,446,489,491,503,554,556,571,592,629,630,642,669,691,707,713,722 'faketimeprovider.advance':309,324 'faketimeprovider.setlocalnow':193,251,287,359,433,497,557,655 'faketimeprovider.setlocaltimezone':163 'faketimeprovider.setutcnow':170 'faketimeprovidercustom':579,583,616 'faketimeproviderextens':150 'fals':380,408,415,465,477,540 'fixedtim':242,252 'fixtur':589,610 'fixture.register':590 'fridaytim':645,656 'frozen':627,664 'getlocalnow':142 'gettimebaseddiscount':624 'getutcnow':137 'github.com':750 'github.com/kevintsengtw/30days_in_testing_samples/tree/main/day16':749 'historicaldataprocessor':342,364 'historicaltim':350,360,372 'historicaltime.date':369 'hour':426,439 'icustom':584 'idispos':711 'ifixtur':588,606 'inlinedata':378,385,392,399,406,413,455,461,467,473,512,527 'int':425 'integr':781 'isintradinghour':481 'item1':261 'item2':265 'ithelp.ithome.com.tw':746 'ithelp.ithome.com.tw/articles/10375821':745 'jobschedul':563 'key':304,313,328 'learn.microsoft.com':756 'learn.microsoft.com/zh-tw/dotnet/api/system.timeprovider)':755 'localdatetim':162,168 'logic':38 'make':21 'matching.directbasetype':618,628,633,665 'mention':30 'microsoft.bcl.timeprovider':730,741,758 'microsoft.extensions.timeprovider.testing':763 'mock':776 'nameof':92 'new':90,191,194,204,224,239,243,255,285,288,298,347,351,363,431,434,444,490,501,555,562,569,591,609,612,615,646 'nextexecutiontim':564 'now.hour':67,69,101,103 'nsubstitut':775 'nsubstitute-mock':774 'nuget':759,764 'orderservic':59,78,84,205,445,631 'orderservicetest':180 'privat':79,219,604 'processbatch':234 'processor':254,362 'processor.processdatafordate':368 'processor.processitem':260,264 'program.cs':107 'public':57,60,76,83,94,147,154,178,182,216,232,278,340,420,479,542,581,585,596,600,622 'readon':80,221 'result':209,367 'result.processedat.should':370 'result.should':212 'result1':259 'result1.timestamp.should':268 'result2':263 'result2.timestamp':270 'return':66,100,608 'schedul':561,573 'scheduledtimestr':550,566 'scheduleservic':570 'services.addscoped':111 'services.addsingleton':109 'setlocalnow':157,694 'setlocaltimezon':128,715 'setutcnow':120 'sharedprovid':223 'shouldexecutejob':544 'skill':26 'skill-dotnet-testing-datetime-testing-timeprovider' 'source-kevintsengtw' 'static':148,155,220,605 'string':484,547,549 'sure':22 'sut':203,443,500,568,632 'sut.canplaceorder':210,447 'sut.gettimebaseddiscount':657 'sut.isintradinghours':504 'sut.shouldexecutejob':572 'test':3,5,32 'testtim':493,498 'theori':377,454,511,620 'throw':89 'time':36 'time-depend':35 'timedcach':299 'timeprovid':6,8,16,33,50,75,81,82,85,86,87,88,93,638,671,676,719,753 'timeprovider.getlocalnow':99,678 'timeprovider.getutcnow':682 'timeprovider.system':110,687 'timespan':133 'timespan.fromminutes':301,310,325 'timespan.parse':495 'timestr':485,496 'timezoneinfo':129 'timezoneinfo.converttimetoutc':167 'timezoneinfo.local':164,169 'token':42,274 'topic-agent-skills' 'topic-ai-assisted-development' 'topic-copilot-skills' 'topic-csharp' 'topic-dotnet' 'topic-dotnet-testing' 'topic-github-copilot' 'topic-integration-testing' 'topic-testing' 'topic-unit-testing' 'topic-xunit' 'tradingservic':502 'true':387,394,401,459,471,525 'use':24 'user':29 'utc':123,126,139 'utctim':166,171 'valu':305,316 'var':63,97,165,189,202,208,237,241,253,258,262,283,296,345,349,361,366,429,442,488,492,499,553,560,567,644 'void':156,183,233,279,341,421,480,543,586,623 'whenev':27 'www.nuget.org':761,766 'www.nuget.org/packages/microsoft.bcl.timeprovider/)':760 'www.nuget.org/packages/microsoft.extensions.timeprovider.testing/)':765 'xunit':780,782 '上午':381,388 '上午交易時間':460 '下午':402,409,416 '下午交易時間':472 '不同時間':482 '不同時間點':423 '不會真正等待':335 '中午':395 '中午休息':466 '九折優惠':661 '交易時間窗口測試':452 '交易結束後':478 '以':49 '作為生產環境實作':688 '使用':7,617,641,677,681,693,696,710 '使用時機':119 '依賴注入設定':105 '傳統問題程式碼':53 '兩次操作的時間戳相同':267 '共':320 '再':317 '分鐘':322 '分鐘後':307,319 '剛結束營業':412 '剛開始營業':391 '包含時間凍結':724 '原則一':47 '原則三':172 '原則二':112 '原始文章':732 '參數化邊界測試':375 '參考資源':731 '取代':51,679,683,742 '取得':138 '取得本地時間':143 '可測試':73 '可測試的重構':71 '可用於並行測試':709 '同一時間點':229 '否則':666 '回到過去時間':343 '在固定時間點':235 '在營業時間內':185 '基底類型':639 '多個測試共用靜態實例':215 '天挑戰':736 '失效等時間敏感邏輯':275 '套件參考':729 '官方文件':752 '容器註冊':686 '實例':692 '實戰測試模式':373 '尚未到時間':541 '尚未過期':308 '屬性':594 '已到執行時間':526 '已過期':323 '延遲':701 '延遲邏輯':136 '建議擴充方法':145 '必須使用':663 '快取':699 '快轉':725 '應回傳true':186 '應回傳九折優惠':626 '應回傳正確結果':424,483,546 '應正確處理':344 '應清除項目':282 '應產生相同時間戳':236 '所有時間相依類別透過建構式接收':675 '抽象化':17 '抽象的服務類別':720 '排程觸發邏輯測試':509 '控制時間流逝':12 '控制測試時間':114 '提供':727 '提供完整的時間控制能力':116 '擴充方法簡化時間設定':695 '整合':578,787 '方法':117 '明確設定時區':716 '是執行緒安全的':708 '是非阻塞的':333 '時':640 '時區測試使用':714 '時區轉換測試範例':726 '時間':124,140 '時間倒轉':336 '時間凍結':227 '時間凍結與快轉等':20 '時間快轉':134,271 '時間抽象化':48 '時間控制':19 '時間時':127 '時間被凍結':266 '最佳實踐檢查清單':673 '會互相干擾':225 '本技能內容提煉自':733 '核心原則':46 '根據時間判斷':545 '模式一':374 '模式三':508 '模式二':451 '模式正確釋放':712 '正確':175 '每個測試使用獨立的時間環境':173 '每個測試方法使用獨立的':690 '每個測試獨立建立':176 '注入到需要':670 '涵蓋':15 '測試快取過期':273 '測試日期與時間':740 '測試時區相關邏輯':131 '測試時間敏感邏輯':698 '測試時間相依邏輯的專門技能':9 '測試替身與模擬':777 '測試歷史資料處理或重播場景':337 '測試涵蓋邊界條件':702 '測試設計':689 '測試過期':135 '測試過期邏輯時使用':14 '無法正確將':668 '無法測試':55 '營業時間內':398,405 '營業時間前':384 '營業時間後':419 '獨立實例':188 '生產環境使用系統時間':108 '產生使用':718,721 '用途':118 '當需要':637 '當需要測試':10 '當需要驗證多個操作發生在':228 '的':785 '的建構式中':672 '的本地時間':153 '的測試類別':723 '直接使用靜態時間':56 '相關技能':768 '瞬間完成時間跳躍':334 '知道':636 '程式碼設計':674 '範例程式碼':748 '系列文章':737 '結束時間':704 '經過過期時間':281 '老派軟體工程師的測試修練':734 '臨界點':705 '自動測試資料生成':773 '與':783 '與時間相依性測試指南':45 '處理時區轉換':13 '衍生類型':643 '設定':122,151 '設定本地時區':130 '讀取本地模擬時間':144 '讀取當前模擬時間':141 '讓':634 '輸出格式':717 '透過依賴注入接收':74 '週五':625,654 '週五快樂':660 '進階時間控制技術':226 '進階考量':706 '過期':700 '避免':214 '重要':331 '鐵人賽文章':744 '開始時間':703 '關鍵':662 '需要精確':125 '點':383,390,397,404,411,418","prices":[{"id":"c591ed9a-7a32-4df4-8291-e02f634dcb17","listingId":"57eb83bf-b983-4ff0-b68f-882c10af9de9","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"kevintsengtw","category":"dotnet-testing-agent-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T23:04:29.507Z"}],"sources":[{"listingId":"57eb83bf-b983-4ff0-b68f-882c10af9de9","source":"github","sourceId":"kevintsengtw/dotnet-testing-agent-skills/dotnet-testing-datetime-testing-timeprovider","sourceUrl":"https://github.com/kevintsengtw/dotnet-testing-agent-skills/tree/main/skills/dotnet-testing-datetime-testing-timeprovider","isPrimary":false,"firstSeenAt":"2026-04-18T23:04:29.507Z","lastSeenAt":"2026-04-24T13:02:26.825Z"}],"details":{"listingId":"57eb83bf-b983-4ff0-b68f-882c10af9de9","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"kevintsengtw","slug":"dotnet-testing-datetime-testing-timeprovider","github":{"repo":"kevintsengtw/dotnet-testing-agent-skills","stars":23,"topics":["agent-skills","ai-assisted-development","copilot-skills","csharp","dotnet","dotnet-testing","github-copilot","integration-testing","testing","unit-testing","xunit"],"license":"mit","html_url":"https://github.com/kevintsengtw/dotnet-testing-agent-skills","pushed_at":"2026-03-31T07:28:56Z","description":"AI Agent Skills for .NET Testing - Based on 30-Day Testing Challenge (iThome Ironman 2025 Winner)","skill_md_sha":"9ab706f10cbf9733aa05a1b82baae60e3b7f3066","skill_md_path":"skills/dotnet-testing-datetime-testing-timeprovider/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/kevintsengtw/dotnet-testing-agent-skills/tree/main/skills/dotnet-testing-datetime-testing-timeprovider"},"layout":"multi","source":"github","category":"dotnet-testing-agent-skills","frontmatter":{"name":"dotnet-testing-datetime-testing-timeprovider","description":"使用 TimeProvider 測試時間相依邏輯的專門技能。當需要測試 DateTime、控制時間流逝、處理時區轉換、測試過期邏輯時使用。涵蓋 TimeProvider 抽象化、FakeTimeProvider 時間控制、時間凍結與快轉等。\nMake sure to use this skill whenever the user mentions DateTime testing, TimeProvider, FakeTimeProvider, time-dependent logic, cache expiration, or token expiration testing, even if they don't explicitly ask for time testing guidance.\nKeywords: datetime, time testing, 時間測試, TimeProvider, FakeTimeProvider, DateTime.Now, 時間相依, 快取過期, token 過期, Microsoft.Bcl.TimeProvider, GetUtcNow, SetUtcNow, Advance, time freeze, 時間凍結, 時間快轉"},"skills_sh_url":"https://skills.sh/kevintsengtw/dotnet-testing-agent-skills/dotnet-testing-datetime-testing-timeprovider"},"updatedAt":"2026-04-24T13:02:26.825Z"}}