Доброго дня, любители Powershell.
Я люблю его, и сегодня заметил одну странность, которая мотивировала к написанию данного поста. Думаю, вам тоже будет интересно. Дело о лишнем тике. Если интересно, добро пожаловать под кат:
В чем суть странности?
В одной обработке для удобства выбора нужно было число дней до конца месяца.
Вычислялось это одной строкой такого вида:
((Get-date -Day 1).AddMonths(1)-(get-date)).days-1
Что характерно ее вычисление может давать разные результаты:
Сначала я подумал, что с кодом что-то не то, или версией Powershell.
Проверил на нескольких машинках и понял, что ситуация воспроизводима.
Поэтому сел дебажить и писать функцию.
У меня получилось такое, в коде сразу идут комментарии:
Function Get-DaysToEndOfMonths([int]$Month=1)
{If($Month -lt 1){[int]$Month = 1}
$CurrentDate = get-date #Получаем текущую дату
$CurrentDay = $CurrentDate.day #И какой это день
$FirstDayCurrentMonths = (Get-date -Day 1) #Первый день текущего месяца
#$FirstDayNextMonths = $FirstDayCurrentMonths.AddMonths($Month) #Первый день искомого месяца
#Ага, так можно сразу отнять один день, чтобы получить последний день до искомого месяца
$LastDay = $FirstDayCurrentMonths.AddMonths($Month).AddDays(-1) #Последний день искомого месяца
$DaysToEndOfMonths = $($LastDay - $CurrentDate).Days #Сколько дней до искомого месяца
#Или через New-TimeSpan
#$NewTimeSpan = New-TimeSpan -Start $CurrentDate -End $LastDay
#$DaysToEndOfMonths = $NewTimeSpan.Days #Сколько дней
Write-debug "$DaysToEndOfMonths days to the end of the next $Month month"
}
Get-DaysToEndOfMonths(1)
Ну и после того как написал, сел еще подумать и пришел к такой строке:
[int]((((Get-Date -day 01).AddMonths(1)).AddDays(-1)).Day-(Get-date).Day)
Она уже не давала ошибку, но может, так как:
Оказывается, что мы не учитываем часминутысекунды, а вернее тактовые тики процессора.
Заметим, что, Get-date выдаёт значение в миллисекундах.
Но если при выполнении вычислений первый и второй вызов Get-date пришёлся на один тик, то будут такие значения:
(Get-date -day 1).AddMonth(1) = 1.12.2019 15:33:00:500
Get-date = 1.11.2019 15:33:00:500
Вычтем и получим 30 00:00:00:000
Но если вызов второго Get-date выпадает на следующий тик, то его значение будет
=>
1.11.2019 15:33:00:501
И тогда мы получим значение в
29 23:59:59:999
Теперь, когда проблема найдена, мы можем сделать так:
#Число дней до конца месяца
((((Get-Date -day 01).AddMonths(1)).AddDays(-1))-(Get-date -Hour 0 -Minute 0 -Second 0 -Millisecond 0)).Days
И у нас всегда будет одинаковое значение.
Будьте внимательны и хороших выходных!
Автор: evr1ka