PowerShell — Дата и Время

DateTime: The truth is out there…

Так, у меня есть 30 дней для оплаты штрафа. А какое сегодня число? Ой, а месяц-то какой? Кажется февраль… А год не високосный ли?

Да, наплевать! Есть же (Get-Date).AddDays(30) !!

Далее небольшая история и много примеров работы с классом [DateTime], которые вам обязательно пригодятся, раз вы сюда попали 🙂

Когда я только начинал изучать PowerShell, я увидел много интересных и непонятных вещей, но именно класс [DateTime] был самым непонятным, поэтому я долго пытался обходить его стороной. Наверное, это связано с тем, что для написания PowerShell скриптов какое-то время совсем необязательно знать что такое методы и функции, но [DateTime] просто заставляет в это погрузиться и оказывается, все не так сложно…

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

Я думаю, что любую сущность надо прочувствовать. Я написал уже несколько десятков скриптов для ActiveDirectory, прежде чем смог сказать, что я знаю, что такое ActiveDirectory 🙂

В книгах и статьях можно найти разные объяснения про объекты, их свойства и методы. Что-то вроде следующего:

Есть дом — это объект, у него есть длина, ширина, высота — это свойства. У него есть и метод — звонок, нажми на звонок, и из дома вылетит…

Как-то не складно…

Так вот чтобы почувствовать что PowerShell — это ООП, почувствовать его мощь и попробовать понять что это такое и зачем это нужно в скриптовом языке, давайте обратимся за помощью к классу [DateTime]

В PowerShell есть много типов данных, проверить тип данных какого-то конкретного экземпляра можно с помощью метода GetType. Создадим переменную $Date и присвоим ей значение — текущую дату:

$Date = Get-Date

Убедимся что переменная $Date теперь представляет собой экземпляр класса [DateTime]

PS> $Date.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     DateTime                                 System.ValueType


PS>

Свойства:

Что бы просмотреть свойства объекта, можно передать его по конвейеру в командлет select

PS> $Date | select *


DisplayHint : DateTime
DateTime    : 17 июля 2017 г. 17:06:47
Date        : 17.07.2017 0:00:00
Day         : 17
DayOfWeek   : Monday
DayOfYear   : 198
Hour        : 17
Kind        : Local
Millisecond : 78
Minute      : 6
Month       : 7
Second      : 47
Ticks       : 636359080070781099
TimeOfDay   : 17:06:47.0781099
Year        : 2017



PS>

Теперь мы поняли, что то, что мы видим на выходе командлета Get-Date, это просто удобное представление времени, но на самом деле это не строка, а объект, у которого есть свойства и к этим свойствам можно обратиться через точку. Например:

PS> $Date.Date

17 июля 2017 г. 0:00:00


PS> $Date.DayOfWeek
Monday
PS>
PS>

Методы:

$Date | Get-Member

С помощью Get-Member (чаще я использую алиас gm), мы видим методы, применяемые к классам. Метод — это функция, которая может принимать на вход аргументы. Если сравнить это со свойствами, которые показаны выше, то можно заметить что свойство DayOfWeek не может принимать никакие аргументы, они здесь не нужны, т.к. это свойство просто выводит день недели. Методы отличаются от свойств тем, что они выполняют какие-то действия.

В данном случае у нас класс [DateTime], посмотрим его методы через Get-Member (результат довольно большой, показывать не буду, выполняйте сами). Сейчас нам нужны только методы, поэтому уточним запрос:

$Date | Get-Member -MemberType Method

Теперь мы видим только методы. Выберем какой-нибудь из них, например AddDays.
Из названия метода AddDays понятно, что можно «добавить день».
Аргументы для методов передаются в скобках и называются страшным словом «перегрузка», но поскольку методы не имеют именованных параметров, важно соблюдать порядок, в котором аргументы передаются.

Итак мы решили воспользоваться методом AddDays, а когда смотрели Get-Member, то в Definition видели «double value» (число с плавающей запятой, см. типы данных). Теперь мы знаем что через точку можем обратиться к свойствам и методам, но методам можно передавать аргументы в скобках. Значит так и напишем: Экземпляр класса (объект), точка, метод, аргумент в скобках. Аргументом у нас будет число «2»,

Попробуем:

PS> $Date.AddDays(2)

19 июля 2017 г. 17:06:47


PS>

Теперь нецелое отрицательное

PS> $Date.AddDays(-1.5)

16 июля 2017 г. 5:06:47


PS>

Отлично, мы отняли полтора дня!

Это можно использовать и в командлетах:

PS# Get-EventLog -LogName Security -After (Get-Date).AddDays(-1) -Newest 3

   Index Time          EntryType   Source                 InstanceID Message
   ----- ----          ---------   ------                 ---------- -------
     541 июл 17 18:05  SuccessA... Microsoft-Windows...         4801 Разблокирована рабочая станция....
     540 июл 17 17:28  SuccessA... Microsoft-Windows...         4800 Заблокирована рабочая станция....
     539 июл 17 16:12  SuccessA... Microsoft-Windows...         4801 Разблокирована рабочая станция....


PS#
PS# ls $env:USERPROFILE\Desktop | ? {$_.LastWriteTime -ge (Get-Date).AddDays(-1)}


    Каталог: C:\Users\User\Desktop


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       17.07.2017      1:20                script2.1
-a----       16.07.2017     23:24           3234 script1.ps1
-a----       16.07.2017     23:16           1660 readme.txt


PS#

Во многих командлетах на вход принимается именно дата, причем дата может быть указана и в виде текста (правда лучше так не делать).
Убедимся, напишем простую функцию «When», принимающую на вход один аргумент (именованный параметр «Date»):

PS> Function When {
>> param(
>> [Parameter(Mandatory=$true)][DateTime]$Date
>> )
>>
>> $Date
>> }
PS>
PS>
PS>
PS>
PS> When -Date (Get-Date).AddDays(-1)

16 июля 2017 г. 18:56:34


PS>
PS>
PS> When -Date '06.16.2017 18:56:34'

16 июня 2017 г. 18:56:34


PS>

Несколько примеров создания объекта [DateTime]

В этом примере в целом все нормально, но если не указать какой-либо из параметров, то аргумент для параметра будет взят из текущей даты

Get-Date -Year 2017 -Month 12 -Day 06 -Hour 18 -Minute 55 -Second 0

Или тоже самое, но через сплаттинг

$param = @{
'Year'=2017;
'Month'=12;
'Day'=6;
'Hour'=18;
'Minute'=55;
'Second'=0
}
Get-Date @param

Через стандартное создание объекта с перегрузкой, выше я говорил, что в перегрузке методов важен порядок, поэтому если что-то упустить, то создание объекта завершится ошибкой, тем не менее, это хороший способ

New-Object DateTime (2017, 12, 06, 18, 55, 00)

Или так

[DateTime]::new(2017, 12, 06, 18, 55, 00)

Преобразование типа из string в datetime, (плохой метод)

[DateTime]'2017.12.06 18:55:00'

Почему? Да потому что легко допустить ошибку, стоит нам написать дату так, как мы видим ее в пуске  Windows, если язык системы русский, то мы получим неправильное распознавание. Простой пример:

PS> [datetime]’12.07.2017 14:55:00′

7 декабря 2017 г. 14:55:00

PS>

Мы получили 7-е декабря! Это не по-русски 🙂 Поэтому предлагаю самый правильный вариант получения даты из string, он больше всего подходит для парсинга логов, потому что он позволяет не просто отдать строку на неконтролируемое преобразование, а задать шаблон по которому преобразование производить

[DateTime]::ParseExact('2017.12.06 18:55:00','yyyy.MM.dd HH:mm:ss',$null)

В русских форматах нам достаточно двух аргументов, третий тоже необходимо указать, но тут он равен $null (если работать в одной локальной системе), но если у нас разные системы на разных языках, мы можем заставить PowerShell выводить или принимать дату на любом языке. Нам придется чуть-чуть углубиться в еще один класс [System.Globalization.CultureInfo] или сокращенно [CultureInfo]
Третий аргумент отвечает за языковые особенности формата дат, а его тип «CultureInfo»

Текущая культура

PS> [CultureInfo]::CurrentCulture

LCID             Name             DisplayName
----             ----             -----------
1049             ru-RU            Русский (Россия)


PS>

Американская культура

PS> [CultureInfo]::GetCultureInfo('en-US')

LCID             Name             DisplayName
----             ----             -----------
1033             en-US            Английский (США)                                                                                                                                         


PS>

Выведем дату в американском формате

PS> (Get-Date).ToString([CultureInfo]::GetCultureInfo('en-US'))
7/18/2017 3:27:35 PM
PS>

А теперь преобразуем строку с полученной выше датой обратно в объект

PS> $str = (Get-Date).ToString([cultureinfo]::GetCultureInfo('en-US'))
PS>
PS> $str
7/18/2017 3:30:40 PM
PS>
PS>
PS> [datetime]::ParseExact($str, 'M/d/yyyy h:m:s tt', [cultureinfo]::GetCultureInfo('en-US'))

18 июля 2017 г. 15:30:40


PS>

Сравнение:

В скриптах нам часто требуется сравнивать две даты между собой или сортировать по дате. PowerShell позволяет сравнивать даты как будто это числа (хотя так оно, конечно, и есть).

Обращаю внимание на то, что заключаю командлет в скобки для того, чтобы как в математике, сначала выполнять операцию в скобках, ведь очевидно, что нельзя один командлет вычесть из другого, но их результаты — это уже конкретные значения, над которыми можно проводить операции вычитания, сложения и т.д..

Убедимся:

PS> (Get-Date) -lt (Get-Date).AddSeconds(1)
True
PS>
PS>
PS> (Get-Date) -gt (Get-Date).AddSeconds(1)
False
PS>
PS>

Сортировка:

PS> 1..3 | % {[DateTime]::new(2017, 12, 06, 18, 55, $_)} | Sort-Object -Descending

6 декабря 2017 г. 18:55:03
6 декабря 2017 г. 18:55:02
6 декабря 2017 г. 18:55:01


PS>

Вычитание дат:

В жизни мы иногда задаемся вопросом: «Сколько же с тех пор времени прошло?», и даже не верится, что в PowerShell такую операцию можно совершить с помощью знака минус, но как бы это ни казалось чудом, это возможно 🙂
Результатом вычитания двух дат будет объект класса [TimeSpan] (следует понимать как временной интервал)

PS> (Get-Date) - (Get-Date).AddDays(-3)


Days              : 3
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 0
Ticks             : 2592000000000
TotalDays         : 3
TotalHours        : 72
TotalMinutes      : 4320
TotalSeconds      : 259200
TotalMilliseconds : 259200000



PS>

Кстати убедимся, что это действительно [TimeSpan]

PS> $TimeSpan = (Get-Date) - (Get-Date).AddDays(-3)
PS> $TimeSpan.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     TimeSpan                                 System.ValueType


PS>

А теперь самое интересное: две даты конечно сложить нельзя, но к дате можно прибавить TimeSpan

PS> (Get-Date) + $TimeSpan

21 июля 2017 г. 1:15:33


PS>

Причем к TimeSpan нельзя прибавить дату, это не совсем математика, от перестановки слагаемых вы получите ошибку. Это вызвано невозможностью преобразования типа данных.

Создать объект класса TimeSpan можно несколькими способами, вот 3 варианта на выбор:

[TimeSpan]::new(0,1,0,0,0)
New-Object TimeSpan (0,1,0,0,0)
[TimeSpan]::FromHours(1)

Как видим, результат один и тот же:

PS> $Date + [TimeSpan]::new(0,1,0,0,0)

17 июля 2017 г. 18:06:47


PS>
PS> $Date + (New-Object TimeSpan (0,1,0,0,0) )

17 июля 2017 г. 18:06:47


PS>
PS> $Date + [TimeSpan]::FromHours(1)

17 июля 2017 г. 18:06:47


PS>

Форматирование

Нам потребуется метод ToString

Сначала несколько слов в продолжение о TimeSpan,  по умолчанию TimeSpan форматирует вывод следующим образом:

PS> $TimeSpan = [TimeSpan]::new(0,3,0,0,0)
PS> $TimeSpan.ToString()
03:00:00
PS>
PS>
PS> $TimeSpan = [TimeSpan]::new(1,3,0,0,0)
PS> $TimeSpan.ToString()
1.03:00:00
PS>

Видим неприятный момент, если TimeSpan меньше одного дня, то день вообще не попадает в вывод, у нас есть возможность указать свой формат, но лучше заранее знать разрядность количества дней. Для этого мы должны передать методу ToString аргумент в виде строки (примеры тут):

PS> $TimeSpan.ToString('dd\.hh\:mm\:ss')
01.03:00:00
PS>
PS> $TimeSpan.ToString('ddd\.hh\:mm\:ss')
001.03:00:00
PS>

Можно вставлять любые символы, но их обязательно нужно экранировать с помощью «\» (backslash)

PS> $TimeSpan.ToString('\d\:ddd\.\h\:hh\.\m\:mm\.\s\:ss')
d:001.h:03.m:00.s:00
PS>
PS>

Вернемся к DateTime, мы уже видели пример работы метода ToString, где аргументом у нас служил объект CultureInfo, но надо сказать, что никто не загоняет вас в рамки, аргументом может служить и строка.

Пожалуйста:

PS> (Get-Date).ToString('ddd, dd.MM.yyyy')
Вт, 18.07.2017
PS>
PS> (Get-Date).ToString('ddd, dd.MM.yyyy', [cultureinfo]::GetCultureInfo('en-US'))
Tue, 18.07.2017
PS>

Или так:

PS> (Get-Date).ToString('hh:mm, ddd, dd MMM yyyy')
03:46, Вт, 18 июл 2017
PS>
PS>
PS> (Get-Date).ToString('hh:mm tt, ddd, dd MMM yyyy', [cultureinfo]::GetCultureInfo('en-US'))
03:46 PM, Tue, 18 Jul 2017
PS>

На последок бонус)))
Метод определения дней в месяце и проверки високосного года

PS> [DateTime]::DaysInMonth(2017,07)
31
PS>
PS> [DateTime]::IsLeapYear(2016)
True
PS>

На этом все. Этой темы мы еще будем касаться, а пока можете попробовать придумать как рассчитать ваш возраст. В одной из следующих статей я расскажу вам о том, как решал ее я.

# ——————-
Статья написана после анализа интересов людей, пытающихся найти помощь по PowerShell в интернете. Больше всего кликов было на один из моих первых постов, который представлял собой маленький черновик по [DateTime] с примерами и особо без объяснений 🙂

Реклама

PowerShell — Дата и Время: 2 комментария

  1. Автору большое спасибо за творческий энтузиазм и рвение каждый раз делиться своим опытом. Побольше таких «разжеванных» статей! 🙂

    Нравится

    Ответить

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s