Measure-Time.psm1

function Measure-Time {
  param (
    [Parameter(Mandatory)]
    [ScriptBlock] $Command
  )

  function Format-Duration {
    param (
      [Parameter(Mandatory)]
      [TimeSpan] $TimeSpan
    )

    $timeSpanStr = @()

    $timeComponents = @(
      @{"unit" = "h"; "value" = $TimeSpan.Hours },
      @{"unit" = "m"; "value" = $TimeSpan.Minutes },
      @{"unit" = "s"; "value" = $TimeSpan.Seconds },
      @{"unit" = "ms"; "value" = $TimeSpan.Milliseconds }
    )

    foreach ($timeComponent in $timeComponents) {
      if ($timeComponent.value -gt 0) {
        $timeSpanStr += "{0:0#}{1}" -f $timeComponent.value, $timeComponent.unit
      }
    }

    if ($timeSpanStr.Count -gt 0) {
      $timeSpanStr -join " "
    }
    else {
      "000ms"
    }
  }

  $JobApi = [JobApiLib.JobApi]
  $JobResourceUsage = [JobApiLib.JobApi+JobResourceUsage]

  try {
    $commandString = $Command.ToString().Trim()

    $jobHandle = $JobApi::CreateJobObject([IntPtr]::Zero, $null)

    $processInfo = [System.Diagnostics.ProcessStartInfo]::new("powershell", "-NoLogo -NoProfile -Command & { $commandString }")
    $processInfo.UseShellExecute = $false
    $processInfo.WorkingDirectory = (Get-Location).Path

    $subProcess = [System.Diagnostics.Process]::Start($processInfo)
    [void]$JobApi::AssignProcessToJobObject($jobHandle, $subProcess.Handle)

    $stopWatch = [System.Diagnostics.Stopwatch]::StartNew()
    $subProcess.WaitForExit()
  }
  finally {
    $stopWatch.Stop()

    $bufferSize = $JobApi::GetStructSize()
    $bufferPointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($bufferSize)

    if ($jobHandle -ne [IntPtr]::Zero -and $JobApi::QueryInformationJobObject($jobHandle, 1, $bufferPointer, $bufferSize, [ref]0)) {
      $jobUsageData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($bufferPointer, [type]$JobResourceUsage)

      $userTime = [TimeSpan]::FromTicks($jobUsageData.TotalUserTime)
      $kernelTime = [TimeSpan]::FromTicks($jobUsageData.TotalPrivilegedTime)
      $processorTime = $userTime + $kernelTime
      $wallTime = $stopWatch.Elapsed

      $cpuLoad = [math]::Round($processorTime.TotalSeconds / $wallTime.TotalSeconds * 100)
      $usage = "$cpuLoad%"

      $userTime = Format-Duration $userTime
      $kernelTime = Format-Duration $kernelTime
      $processorTime = Format-Duration $processorTime
      $wallTime = Format-Duration $wallTime

      # Write-Host "{ $commandString } $userTime user $kernelTime system $processorTime cpu $usage cpu% $wallTime total"
      Write-Host "{ $commandString } $userTime user $kernelTime system $usage cpu $wallTime total"
    }

    if ($bufferPointer -ne [IntPtr]::Zero) { [System.Runtime.InteropServices.Marshal]::FreeHGlobal($bufferPointer) }
    if ($jobHandle -ne [IntPtr]::Zero) { [void]$JobApi::CloseHandle($jobHandle) }
  }
}

# alias to Measure-Time
Set-Alias -Name time -Value Measure-Time -Option AllScope

Export-ModuleMember -Function Measure-Time
Export-ModuleMember -Alias time