@@ -686,18 +686,20 @@ public void AddAttackOption(Planet planet)
686686 private static readonly int TurnIntervalMinutes = GlobalConst . PlanetWarsMinutesToAttack + GlobalConst . PlanetWarsMinutesToAccept ;
687687
688688 /// <summary>
689- /// Returns the next wall-clock boundary for attack phase start (e.g. :00, :15, :30, :45 for 15-min turns).
690- /// If we're already past the current boundary, snaps to the next one.
689+ /// Returns the current or next wall-clock turn boundary (e.g. :00, :15, :30, :45 for 15-min turns).
690+ /// If exactly on a boundary, returns that time. Otherwise returns the next one.
691691 /// </summary>
692692 private static DateTime GetNextTurnBoundary ( )
693693 {
694694 var now = DateTime . UtcNow ;
695- var minutesSinceHour = now . Minute ;
696- var currentSlot = ( minutesSinceHour / TurnIntervalMinutes ) * TurnIntervalMinutes ;
697- var nextSlot = currentSlot + TurnIntervalMinutes ;
698- var boundary = new DateTime ( now . Year , now . Month , now . Day , now . Hour , 0 , 0 , DateTimeKind . Utc )
699- . AddMinutes ( nextSlot ) ;
700- return boundary ;
695+ // truncate to whole minutes to avoid floating point issues
696+ var nowTrunc = new DateTime ( now . Year , now . Month , now . Day , now . Hour , now . Minute , 0 , DateTimeKind . Utc ) ;
697+ var midnight = nowTrunc . Date ;
698+ var totalMinutes = ( int ) ( nowTrunc - midnight ) . TotalMinutes ;
699+ var remainder = totalMinutes % TurnIntervalMinutes ;
700+ if ( remainder == 0 )
701+ return nowTrunc ; // already on a boundary
702+ return nowTrunc . AddMinutes ( TurnIntervalMinutes - remainder ) ;
701703 }
702704
703705 private void ResetAttackOptions ( )
0 commit comments