The problem I needed to solve
A daily reward is more than a claim button: I need to know whether it was claimed today and whether its definition is valid. I placed time behind ITimeService to clarify that boundary.
Implementation in this snapshot
DailyRewardService.CanClaimToday compares the saved date with TodayLocal. TryClaimToday writes the new date and announces state change only after GiveReward succeeds.
The design decision and its cost
Date formatting is culture-independent. Despite a legacy Utc suffix on the saved field, current behavior uses a local calendar day, and product explanations should reflect that.
Current boundaries
Device time is not trusted server time. Clock or timezone changes are a real limitation, and the claim result should be interpreted together with its grant result.
Review scenarios and contract meaning
A local-day example makes the boundary clearer: claiming shortly before midnight and returning after midnight means two calendar days, even if little real time has passed. This follows a date comparison and does not promise a fixed twenty-four-hour cooldown. The screen copy should explain that rule. A legacy field name ending in Utc should not define the product explanation; current behavior matters. Putting time behind an interface lets me reason about this boundary independently of the button and date presentation.
The more sensitive issue is the connection to reward distribution. A coin grant may remain in memory for presentation and report success before the balance increases. The daily service then records the claim date after that result. Closing the process in the gap can therefore leave the day marked as claimed while the coins have not reached the local balance. This is a boundary visible in the current contract, not a claim that it happened to users. The relevant review includes immediate delivery, normal presentation completion and interruption midway, so the product has a clear meaning for success.
The next-availability copy must agree with TodayLocal. A twenty-four-hour countdown over a calendar-day rule promises different behavior. An invalid reward definition also differs from an already-claimed day and needs different feedback. These details make service decisions understandable instead of hiding a data problem behind a disabled button.
What this stage taught me
For me, the daily boundary should be explainable. The button’s appearance comes from CurrentState, but granting comes from the service check; changing appearance must not mean granting coins twice.




