diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs
index a57d635f3eb..a1524e5ed4f 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs
@@ -55,6 +55,15 @@ public void Dispose()
[Alias("ms")]
public int Milliseconds { get; set; }
+ ///
+ /// Allows sleep time to be specified as a TimeSpan.
+ ///
+ [Parameter(Position = 0, Mandatory = true, ParameterSetName = "FromTimeSpan", ValueFromPipeline = true,
+ ValueFromPipelineByPropertyName = true)]
+ [ValidateRange(ValidateRangeKind.NonNegative)]
+ [Alias("ts")]
+ public TimeSpan Duration { get; set; }
+
#endregion
#region methods
@@ -104,6 +113,26 @@ protected override void ProcessRecord()
case "Milliseconds":
sleepTime = Milliseconds;
break;
+
+ case "FromTimeSpan":
+ if (Duration.TotalMilliseconds > int.MaxValue)
+ {
+ PSArgumentException argumentException = PSTraceSource.NewArgumentException(
+ nameof(Duration),
+ StartSleepStrings.MaximumDurationExceeded,
+ TimeSpan.FromMilliseconds(int.MaxValue),
+ Duration);
+
+ ThrowTerminatingError(
+ new ErrorRecord(
+ argumentException,
+ "MaximumDurationExceeded",
+ ErrorCategory.InvalidArgument,
+ targetObject: null));
+ }
+
+ sleepTime = (int)Math.Floor(Duration.TotalMilliseconds);
+ break;
default:
Dbg.Diagnostics.Assert(false, "Only one of the specified parameter sets should be called.");
diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/StartSleepStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/StartSleepStrings.resx
new file mode 100644
index 00000000000..32804b9e21b
--- /dev/null
+++ b/src/Microsoft.PowerShell.Commands.Utility/resources/StartSleepStrings.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ The '-Duration' parameter value must not exceed '{0}', provided value was '{1}'.
+
+
diff --git a/src/System.Management.Automation/engine/Attributes.cs b/src/System.Management.Automation/engine/Attributes.cs
index a0411c78df7..9965824a927 100644
--- a/src/System.Management.Automation/engine/Attributes.cs
+++ b/src/System.Management.Automation/engine/Attributes.cs
@@ -1088,6 +1088,12 @@ public ValidateRangeAttribute(ValidateRangeKind kind) : base()
private static void ValidateRange(object element, ValidateRangeKind rangeKind)
{
+ if (element is TimeSpan ts)
+ {
+ ValidateTimeSpanRange(ts, rangeKind);
+ return;
+ }
+
Type commonType = GetCommonType(typeof(int), element.GetType());
if (commonType == null)
{
@@ -1212,6 +1218,59 @@ private void ValidateRange(object element)
}
}
+ private static void ValidateTimeSpanRange(TimeSpan element, ValidateRangeKind rangeKind)
+ {
+ TimeSpan zero = TimeSpan.Zero;
+
+ switch (rangeKind)
+ {
+ case ValidateRangeKind.Positive:
+ if (zero.CompareTo(element) >= 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangePositiveFailure",
+ null,
+ Metadata.ValidateRangePositiveFailure,
+ element.ToString());
+ }
+
+ break;
+ case ValidateRangeKind.NonNegative:
+ if (zero.CompareTo(element) > 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangeNonNegativeFailure",
+ null,
+ Metadata.ValidateRangeNonNegativeFailure,
+ element.ToString());
+ }
+
+ break;
+ case ValidateRangeKind.Negative:
+ if (zero.CompareTo(element) <= 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangeNegativeFailure",
+ null,
+ Metadata.ValidateRangeNegativeFailure,
+ element.ToString());
+ }
+
+ break;
+ case ValidateRangeKind.NonPositive:
+ if (zero.CompareTo(element) < 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangeNonPositiveFailure",
+ null,
+ Metadata.ValidateRangeNonPositiveFailure,
+ element.ToString());
+ }
+
+ break;
+ }
+ }
+
private static Type GetCommonType(Type minType, Type maxType)
{
Type resultType = null;
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Start-Sleep.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Start-Sleep.Tests.ps1
index 59ac3e9903d..e4899ce5439 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Start-Sleep.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Start-Sleep.Tests.ps1
@@ -23,6 +23,15 @@ Describe "Start-Sleep DRT Unit Tests" -Tags "CI" {
$watch.ElapsedMilliseconds | Should -BeLessThan $maxTime
}
+ It "Should work properly when sleeping with a [TimeSpan]" {
+ $duration = [timespan]::FromMilliseconds(1500)
+ $watch = [System.Diagnostics.Stopwatch]::StartNew()
+ Start-Sleep -Duration $duration
+ $watch.Stop()
+ $watch.ElapsedMilliseconds | Should -BeGreaterThan $minTime
+ $watch.ElapsedMilliseconds | Should -BeLessThan $maxTime
+ }
+
It "Should work properly when sleeping with ms alias" {
$watch = [System.Diagnostics.Stopwatch]::StartNew()
Start-Sleep -ms 1500
@@ -38,6 +47,21 @@ Describe "Start-Sleep DRT Unit Tests" -Tags "CI" {
$watch.ElapsedMilliseconds | Should -BeGreaterThan $minTime
$watch.ElapsedMilliseconds | Should -BeLessThan $maxTime
}
+
+ It "Should work properly when sleeping without parameters from [timespan]" {
+ $duration = [timespan]::FromMilliseconds(1500)
+ $watch = [System.Diagnostics.Stopwatch]::StartNew()
+ Start-Sleep $duration
+ $watch.Stop()
+ $watch.ElapsedMilliseconds | Should -BeGreaterThan $minTime
+ $watch.ElapsedMilliseconds | Should -BeLessThan $maxTime
+ }
+
+ It "Should validate [timespan] parameter values" {
+ { Start-Sleep -Duration '0:00:01' } | Should -Not -Throw
+ { Start-Sleep -Duration '-0:00:01' } | Should -Throw -ErrorId 'ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartSleepCommand'
+ { Start-Sleep -Duration '30.0:00:00' } | Should -Throw -ErrorId 'MaximumDurationExceeded,Microsoft.PowerShell.Commands.StartSleepCommand'
+ }
}
Describe "Start-Sleep" -Tags "CI" {