Konwersja Linuxowych znaczników czasu w Powershellu
Powershell posiada bardzo fajny wbudowany parser xml. Wystarczy:
$plik=[xml](gc plik.xml)
by móc pracować z xml-em zawartym w pliku w obiektowy sposób, np:
$plik.body |where {$_.section.name -match 'abc'}
wyświetli nam obiekty związane z węzłami ,,section'' w części ,,body'' pliku, których nazwa pasuje do wyrażenia regularnego abc. Prawda że wygodne? Niestety jeśli używamy w xml-u linuxowych timestampów, wykorzystanie PowerShella wymaga dodatkowego kroku, który opisuję poniżej.
W xmlach z którymi ciągle pracuję istotne są daty będące (pod)węzłem interesujących węzłów. A daty te, dzięki Apache'owi, zależą od sposobu pobrania xml-a - jeśli pobiorę go z IE, to mogę dostać inny format dat niż gdy pobiorę narzędziami konsolowymi. Na szczęście jednym z elementów jest linuxo-podobny timestamp. W tym przypadku liczba milisekund od 1970-01-01 00:00:00. W windowsie używany jest tzw. file timestamp, zdefiniowany jako liczba setek nanosekund od 1601-01-01 12:001. Dlatego zwykłe:
$timestamp=12345677888000 get-date $timestamp
nie poda prawidłowej daty dla znacznika czasu ,,epoch''. W jaki sposób można więc (wydajnie, bo mowa o tysiącach timestampów do skonwertowania) zamieniać linuxowe timestampy? Można takim filtrem:
#czas poczatekunixa wystarczy zdefiniowac raz w skrypcie $poczatekunixa = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0 filter fromtimestamp () { $poczatekunixa.AddMilliseconds($_)|get-date -uformat '%Y-%m-%d;%H:%M' }
Teraz już możemy nasze znaczniki czasu konwertować:
$plik.selectNodes("//Timestamp") |foreach {$_.'#text'}|fromtimestamp|out-file -encoding "ascii" timestampy.csv
Przetestowane, działa i to całkiem wydajnie.
Przy okazji - w powyższych przykładach użyte są przydatne konstrukcje:
- objektXml.selectNodes(xPath) - metoda objektów xml pozwalająca wybierać węzły przy pomocy xPatha,
- $_.'#text' - jeśli twórca xml-a był tak genialny, by użyć znaku hash (#) w nazwie węzła to to jest właśnie sposób, by użyć takiej nazwy,
- objektDateTime.AddMilliseconds - jest też wariant AddSeconds, jeśli masz do czynienia z prawdziwym timestampem Unix,
- get-date -uformat "strformatujacy" - ukłon MS w stronę Unixowców - łańcuch formatujący ma format (prawie?) taki sam jak linuxowy date.
Mam nadzieję, że komuś się ta wiedza przyda:)
Footnotes:
1 ma ktoś pomysł, czemu przyszło to do głowy pomysłodawcy? Tym bardziej, że w dużej części Europy 1 stycznia nie był Nowym Rokiem w 1601 roku.