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, 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:

  1. objektXml.selectNodes(xPath) - metoda objektów xml pozwalająca wybierać węzły przy pomocy xPatha,
  2. $_.'#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,
  3. objektDateTime.AddMilliseconds - jest też wariant AddSeconds, jeśli masz do czynienia z prawdziwym timestampem Unix,
  4. 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? Na dodatek w dużej części Europy 1 stycznia nie był Nowym Rokiem w 1601 roku