PowerShell - Посчитать время с учётом рабочих часов

Аватара пользователя
Njem

PowerShell - Посчитать время с учётом рабочих часов

Сообщение Njem »

Всем привет!



Стоит задача посчитать сколько было затрачено времени в текущей задаче. В целом всё просто, использовал командлет Timespan, взял время старта, время конца, сложил и получил ответ:





Код:

Код: Выделить всё

$FIRST_RESPONSE_TIME = New-TimeSpan -Start "$START_TIME_TICKET" -End "$END_TIME_TICKET"

Во вложении вывод.



Однако, есть нюанс, который заключается в SLA. Время останавливается на нерабочих часах. То есть должно на 19:00 время остановиться и в 9:00 следующего дня утром снова возобновиться. По итогу вывод должен быть не на 2 часа 17 минут, а просто на 1 час 32 минуты. Следовательно, если время будет начинаться, скажем с 20:00 часов, то считать он должен с 9:00 и до времени, когда будет $END_TIME_TICKET (например конечная дата 12:00 и того 3 часа итог, а не 16 часов, как он посчитается всё время).



И вот как посчитать время с учётом рабочих часов, вообще никаких идей нет, может у кого будут мысли как это сделать.



Заранее спасибо!!!



p.s. а и да, ещё нужно как-то учесть производственный календарь... в общем время должно считать строго в пороге от 9:00 до 19:00 буднего дня, а оставшееся выкидывать
Вложения

[Расширение png было запрещено, вложение больше недоступно.]

Аватара пользователя
YuS_2

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение YuS_2 »

Для будних дней:



Код:

Код: Выделить всё

$k = new-timespan -h 14
if (($x = $END_TIME_TICKET.day - $START_TIME_TICKET.day)){
    $END_TIME_TICKET - $START_TIME_TICKET - $x*$k
} else {
    $END_TIME_TICKET - $START_TIME_TICKET
}

где $x - разница дней, $k - период с 19:00 до 9:00




Цитата Njem:



ещё нужно как-то учесть производственный календарь...
PowerShell - Посчитать время с учётом рабочих часов




Для выходных и праздничных дней надо будет добавить условный блок с коэффициентом, учитывающим выходные... с праздничными - сложнее, придется составлять календарь с праздничными днями, учитывать год, день недели и если праздничный выпадает на будний день, то вычитать 24 часа... в общем, по аналогии составляйте алгоритм и реализуйте его в код... ничего сложного Изображение
Аватара пользователя
YuS_2

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение YuS_2 »

Цитата YuS_2:



где $x - разница дней, $k - период с 19:00 до 9:00
PowerShell - Посчитать время с учётом рабочих часов




хм... то есть получается $k - период, который, он не явно с 19:00 до 9:00, а просто делается всегда отнимание 14 часов, если $END_TIME_TICKET +1 день от $START_TIME_TICKET



а если время будет начинаться с 7:00 утра, он ведь всё равно посчитает его с 7:00, а не с 9:00 как должен((



p.s. причём вот, есть живой пример сейчас: время начинается с 16:37, а заканчивается на следующий день в 19:37. Считает 13 часов, так как отнимает 14 часов, НО, 37 минут же идут вне рабочего временни, значит должно быть 12 часов 23 минуты. И вот как сделать, чтобы он когда достигал 19:00 понимал, что это уже стоп... Изображение
Аватара пользователя
Njem

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение Njem »

Цитата Njem:



причём вот, есть живой пример сейчас: время начинается с 16:37, а заканчивается на следующий день в 19:37. Считает 13 часов, так как отнимает 14 часов, НО, 37 минут же идут вне рабочего временни, значит должно быть 12 часов 23 минуты. И вот как сделать, чтобы он когда достигал 19:00 понимал, что это уже стоп...
PowerShell - Посчитать время с учётом рабочих часов




Научитесь составлять словесный алгоритм, в котором будут учитываться все возможные ситуации, после этого реализуйте его в код...

Пример для одного дня, без перехода на новую дату:



Код:

Код: Выделить всё

$start = new-timespan -h 9
$end = new-timespan -h 19
$k = new-timespan -h 14
$time = new-timespan
if (!($x = $END_TIME_TICKET.day - $START_TIME_TICKET.day)){
	if($END_TIME_TICKET.timeofday.totalhours -ge $end.totalhours -and $START_TIME_TICKET.timeofday.totalhours -lt $start.totalhours){
		$time = $end - $start
	} elseif ($END_TIME_TICKET.timeofday.totalhours -ge $end.totalhours -and $START_TIME_TICKET.timeofday.totalhours -ge $start.totalhours){
		$time = $end - $START_TIME_TICKET.timeofday
	} elseif ($END_TIME_TICKET.timeofday.totalhours -le $end.totalhours -and $START_TIME_TICKET.timeofday.totalhours -lt $start.totalhours){
		$time = $END_TIME_TICKET.timeofday - $start
	} elseif ($END_TIME_TICKET.timeofday.totalhours -le $end.totalhours -and $START_TIME_TICKET.timeofday.totalhours -ge $start.totalhours) {
		$time = $END_TIME_TICKET.timeofday - $START_TIME_TICKET.timeofday
	}
}
$time

- Здесь не учтена ситуация, когда и старт, и стоп, до начала рабочего дня, либо оба после окончания, но в этом случае, $time будет 0, только надо описать это в условиях...

В общем, остальные возможные комбинации, предлагаю обдумывать самостоятельно...
Аватара пользователя
YuS_2

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение YuS_2 »

Njem,

В общем, накидал тут пример реализации логики для будней:



script.ps1

[spoiler]Код:
[/spoiler]

Код: Выделить всё

param (
	[datetime]$stime = $(get-date("21.12.2022 8:00")),
	[datetime]$etime = $(get-date("22.12.2022 8:00")),
	[timespan]$startwork = $(new-timespan -h 9),
	[timespan]$endwork = $(new-timespan -h 19),
	[timespan]$fullwork = $(new-timespan -h 10)
)
function get-timework {
	param (
		[datetime]$stt, # старт
		[datetime]$ett, # стоп
		[timespan]$start, # начало рабочего периода
		[timespan]$end , # конец рабочего периода
		[timespan]$m # полный рабочий период
	)
	$x = $ett.day - $stt.day # количество дней между стартом и стопом
	$w, $h = @(1,2,3,4,5), @(6,0) # будни, выходные
	$sdw,$edw = $stt.dayofweek.value__,$ett.dayofweek.value__ # номер дня недели для старт и стоп
	# укорачиваем код до названия переменных:
	$rs,$re = $stt.timeofday.totalminutes,$ett.timeofday.totalminutes
	$s,$e = $start.totalminutes,$end.totalminutes
	$minutes = $m.totalminutes
	# Реализация логики подсчета для будней:
	if (!$x -and $re -le $rs) {
		write-host Ошибка! Проверьте данные '$stt - старт/ $ett - стоп' -for red
		break
	}
	if (!$x -and $sdw -notin $h -and $edw -notin $h -and $re -gt $rs){
		if($rs -lt $s -and $re -gt $e){
			$time = new-timespan -min ($minutes)
		} elseif (($rs -ge $s -and $rs -le $e) -and $re -gt $e){
			$time = new-timespan -min ($e - $rs)
		} elseif (($rs -gt $e -and $re -gt $e) -or ($rs -lt $s -and $re -lt $s)){
			$time = new-timespan
		} elseif ($rs -lt $s -and ($re -ge $s -and $re -le $e)) {
			$time = new-timespan -min ($re - $s)
		} elseif (($rs -ge $s -and $rs -le $e) -and ($re -ge $s -and $re -le $e)){
			$time = new-timespan -min ($re - $rs)
		}
	} elseif ($x -and $sdw -notin $h -and $edw -notin $h) {
		if ($rs -lt $s -and $re -gt $e){
			$time = new-timespan -min ($minutes * ($x+1))
		} elseif (($rs -ge $s -and $rs -le $e) -and $re -gt $e){
			$time = new-timespan -min ($e - $rs + $minutes*$x)
		} elseif (($rs -gt $e -and $re -gt $e) -or ($rs -lt $s -and $re -lt $s)){
			$time = new-timespan -min ($minutes * $x)
		} elseif ($rs -lt $s -and ($re -ge $s -and $re -le $e)){
			$time = new-timespan -min ($re - $s + $minutes*$x)
		} elseif (($rs -ge $s -and $rs -le $e) -and ($re -ge $s -and $re -le $e)){
			$time = new-timespan -min ($re - $s + $e - $rs + $minutes*($x-1))
		} elseif ($rs -gt $e -and ($re -ge $s -and $re -le $e)) {
			$time = new-timespan -min ($re - $s + $minutes*($x-1))
		} elseif (($rs -ge $s -and $rs -le $e) -and $re -lt $s){
			$time = new-timespan -min ($e - $rs + $minutes*($x-1))
		} elseif ($rs -gt $e -and $re -lt $s) {
			$time = new-timespan -min ($minutes*($x-1))
		} 
	} else {
		write-host Условия не сработали... для будущей разработки. -for cyan
	}
	return $time
}
get-timework -stt $stime -ett $etime -start $startwork -end $endwork -m $fullwork









Пример запуска:



Код:

Код: Выделить всё

.\script.ps1 -stime $(get-date("21.12.2022 19:50")) -etime $(get-date("22.12.2022 17:00"))

Для ориентирования, как реализуется логика, приведу табличку:

2022-12-22 16-1-39.png




Выходные и праздники не учитываются... попробуйте их реализовать, отталкиваясь от логики будней...
Аватара пользователя
YuS_2

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение YuS_2 »

Цитата YuS_2:



В общем, накидал тут пример реализации логики для будней:
PowerShell - Посчитать время с учётом рабочих часов




ВООУУ, ничего себе!!! господи, спасибо Вам большое!!

а я тут сижу второй день пытаясь математически всё это рассчитать) пока что логика тяжело даётся) думаю с этим намного проще будет!)




Цитата YuS_2:



Выходные и праздники не учитываются... попробуйте их реализовать, отталкиваясь от логики будней...
PowerShell - Посчитать время с учётом рабочих часов




да я думаю выходные как-нить попытаюсь сделать, а-ля в какой нить csv запихнуть дни (выходные все), где он туда будет ссылаться с проверкой, мол если день есть, то -24 часа делать сходу... как-нить по такому принципу думал



сейчас всё посмотрю, спасибо ещё раз!!!) работа с числами всегда тяжело даётся для меня Изображение
Аватара пользователя
Njem

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение Njem »

Цитата Njem:



а-ля в какой нить csv запихнуть дни (выходные все), где он туда будет ссылаться с проверкой, мол если день есть, то -24 часа делать сходу... как-нить по такому принципу думал
PowerShell - Посчитать время с учётом рабочих часов




В csv для выходных не надо, там есть начало реализации... надо учитывать номер дня недели и если старт/стоп выпадает на выходные, то формула будет изменяться, а также при переходе через выходные, т.е. для ситуации:



Код:

Код: Выделить всё

($x -gt 0 -and $x -lt 7) -and ($sdw -gt $edw) -and ($sdw -in $w) -and ($edw -in $w)

- выполнение условия будет означать переход через выходные, кроме того и старт, и стоп были осуществлены в будний день... только при реализации, надо учесть все нюансы (один или два выходных, момент старта и стопа и т.д.), в общем поле для размышлений есть...

А вот для праздников, возможно, придется использовать какой-либо список, тот же csv, либо прямо в скрипте его организовать...
Вложения

[Расширение png было запрещено, вложение больше недоступно.]

Аватара пользователя
YuS_2

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение YuS_2 »

Цитата YuS_2:



выполнение условия будет означать переход через выходные, кроме того и старт, и стоп были осуществлены в будний день... только при реализации, надо учесть все нюансы (один или два выходных, момент старта и стопа и т.д.), в общем поле для размышлений есть...
PowerShell - Посчитать время с учётом рабочих часов




о как, понял, хорошо, тогда так и сделаю, спасибо ещё раз за помощь)




Цитата YuS_2:



А вот для праздников, возможно, придется использовать какой-либо список, тот же csv, либо прямо в скрипте его организовать...
PowerShell - Посчитать время с учётом рабочих часов




а тут проще, в инете есть производственный календарь как раз на 2023 год. Его можно как csv, так и json формата скачать)
Аватара пользователя
Njem

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение Njem »

Njem, небольшая поправка

неправильно был указан номер воскресенья... должен был быть 0, т.е. 6 - суббота, 0 - воскресенье

в
PowerShell - Посчитать время с учётом рабочих часов
исправил...
Аватара пользователя
YuS_2

Re: PowerShell - Посчитать время с учётом рабочих часов

Сообщение YuS_2 »

Ещё вариант, в котором поправлены ошибки и добавлен учет выходных:



script.ps1

[spoiler]Код:
[/spoiler]

Код: Выделить всё

# Пример работы со временем. Учет времени только в рабочее время (9:00 - 19:00),
# исключая выходные (суб., вск.).
# .\script.ps1 -stime $(get-date("21.12.2022 19:50")) -etime $(get-date("22.12.2022 17:00"))
param (
	[datetime]$stime = $(get-date("21.12.2022 8:00")),
	[datetime]$etime = $(get-date("22.12.2022 8:00")),
	[timespan]$startwork = $(new-timespan -h 9 -min 0),
	[timespan]$endwork = $(new-timespan -h 19 -min 0)
)
function get-timework {
	param (
		[datetime]$stt, # старт
		[datetime]$ett, # стоп
		[timespan]$start, # начало рабочего периода
		[timespan]$end # конец рабочего периода
	)
	function get-weekend {
		param (
			[datetime]$sd,
			[datetime]$ed,
			[int[]]$weekend = @(6,0)
		)
		$sub = ($ed.date - $sd.date).days
		$arr = @()
		for ($i = 0; $i -le $sub;$i++){
			#формируем массив выходных за период, для дальнейшего подсчета пар
			if ($sd.adddays($i).dayofweek.value__ -in $weekend){
				$arr += $sd.adddays($i).dayofweek.value__
			}
		}
		if ($arr){
			$out = ($arr.where({$_ -eq $weekend[0]},5)|%{$_.count}|sort -d)[0]
		} else {$out = 0}
		return $out
	}
	if ($ett -le $stt) {
		write-host Ошибка! Проверьте данные '$stt - старт/ $ett - стоп' -for red
		break
	}
	$m = new-timespan -h ($end - $start).totalhours
	$x = ($ett.date - $stt.date).days # количество дней между стартом и стопом
	$w, $h = @(1,2,3,4,5), @(6,0) # будни, выходные (суббота и воскресенье)
	# укорачиваем код до названия переменных:
	$sdw,$edw = $stt.dayofweek.value__,$ett.dayofweek.value__ # номер дня недели для старт и стоп
	$rs,$re = $stt.timeofday.totalminutes,$ett.timeofday.totalminutes
	$s,$e = $start.totalminutes,$end.totalminutes
	$minutes = $m.totalminutes
	# Для выходных:
	if ($edw -in $h){
		# Если стоп в выходной день (перенос стопа на пон. 0:00)
		if (!$edw){$ds = 1} else {$ds = 2}
		$ett = $ett.addminutes(-$ett.timeofday.totalminutes)
		$ett = $ett.adddays($ds)
		$edw,$re = $ett.dayofweek.value__,$ett.timeofday.totalminutes
		$x = ($ett.date - $stt.date).days
	}
	if ($sdw -in $h){
		# Если старт в выходной день (перенос старта на пон. 0:00)
		if (!$sdw){$ds = 1} else {$ds = 2}
		$stt = $stt.addminutes(-$stt.timeofday.totalminutes)
		$stt = $stt.adddays($ds)
		$sdw,$rs = $stt.dayofweek.value__,$stt.timeofday.totalminutes
		$x = ($ett.date - $stt.date).days
	}
	$kw = get-weekend $stt $ett # количество пар выходных между стартом и стопом
	if ($stt.dayofweek.value__ -in $h -and $kw){$kw--}
	if ($x -and $sdw -in $w -and $edw -in $w){
		# Если есть полный переход через выходные, 
		# старт/стоп в рабочие дни, вычитаем выходные полностью:
		$x = $x - 2*$kw
	}
	# Реализация логики подсчета для будней:
	if (!$x -and $sdw -in $w -and $edw -in $w -and $re -ge $rs){
		# Если и старт, и стоп в один день
		if($rs -lt $s -and $re -gt $e){
			$time = new-timespan -min ($minutes)
		} elseif (($rs -ge $s -and $rs -le $e) -and $re -gt $e){
			$time = new-timespan -min ($e - $rs)
		} elseif (($rs -gt $e -and $re -gt $e) -or ($rs -lt $s -and $re -lt $s)){
			$time = new-timespan
		} elseif ($rs -lt $s -and ($re -ge $s -and $re -le $e)) {
			$time = new-timespan -min ($re - $s)
		} elseif (($rs -ge $s -and $rs -le $e) -and ($re -ge $s -and $re -le $e)){
			$time = new-timespan -min ($re - $rs)
		}
	} elseif ($x -and $sdw -in $w -and $edw -in $w) {
		# Если старт и стоп в разные дни,
		if ($rs -lt $s -and $re -gt $e){
			$time = new-timespan -min ($minutes * ($x+1))
		} elseif (($rs -ge $s -and $rs -le $e) -and $re -gt $e){
			$time = new-timespan -min ($e - $rs + $minutes*$x)
		} elseif (($rs -gt $e -and $re -gt $e) -or ($rs -lt $s -and $re -lt $s)){
			$time = new-timespan -min ($minutes * $x)
		} elseif ($rs -lt $s -and ($re -ge $s -and $re -le $e)){
			$time = new-timespan -min ($re - $s + $minutes*$x)
		} elseif (($rs -ge $s -and $rs -le $e) -and ($re -ge $s -and $re -le $e)){
			$time = new-timespan -min ($re - $s + $e - $rs + $minutes*($x-1))
		} elseif ($rs -gt $e -and ($re -ge $s -and $re -le $e)) {
			$time = new-timespan -min ($re - $s + $minutes*($x-1))
		} elseif (($rs -ge $s -and $rs -le $e) -and $re -lt $s){
			$time = new-timespan -min ($e - $rs + $minutes*($x-1))
		} elseif ($rs -gt $e -and $re -lt $s) {
			$time = new-timespan -min ($minutes*($x-1))
		} 
	} else {
		write-host Условия не сработали... для будущей разработки. -for cyan
	}
	return $time
}
get-timework -stt $stime -ett $etime -start $startwork -end $endwork
Ответить

Вернуться в «Скриптовые языки администрирования Windows»