I'm putting some simple automation together for this website, to publish the static files regularly, and I had an itch to scratch that I thought was worth writing about.
The posts on the site all have the publish date encoded in their file name. The static file generator is based around some simple XML transforms, which is how I've re-skinned the site over the years. To be smarter about the use of publish dates, then, I decided I would have to do some date manipulation.
Changing templates and looking at results feels like too much work for the first few steps, wihch is to teach myself how dates work in XSLT. I need to convert them to and from strings, get the current date (or read it via a parameter) and do basic before/after comparisons. All things I should be able to do in isolation.
Enter PowerShell. Because it has access to .NET types, I can just prototype things very quickly. I need to get comfortable with dates, strings and XML.
Dates in PowerShell
First, how do dates work in PowerShell? These are the most straightforard, as you can simply get the current date with Get-Date and go from there.
PS > $d = Get-Date PS > $d Sunday, August 11, 2019 10:09:01 AM PS > $d.GetType().FullName System.DateTime PS > $d.ToString("yyyy-MM-dd") 2019-08-11 PS > $tomorrow = $d.AddDays(1) PS > $tomorrow Monday, August 12, 2019 10:09:01 AM PS > if ($d -lt $tomorrow) { echo "time works correctly" } PS > $yesterday = [DateTime]::Parse("2019-08-10") PS > $yesterday Saturday, August 10, 2019 12:00:00 AM
Strings in PowerShell
Strings are reasonably straightforward, especially if you're using simple statements, but it's easy to make mistakes in some contexts. For example, this probably does what you expect if you're familiar with C#.
PS > $s1= "one" PS > $s2= $s1 + " two" PS > $s2 one two
But this is surprising.
PS > Write-Host $s1 + " two" one + two
What you see here is that Write-Host takes an array of objects as its input, and PowerShell sees this as a variable, a "+" string, and the " two" literal.
XML in PowerShell
There are a couple of nice built-in utilities for working with XML in PowerShell. Convert-To-Xml takes an object and serializes into a document, then returns it as a document object or a string. This gets very interesting with the ability to create objects with custom types on the fly, and with the ability to round-trip objects, but I'm looking for something more straightforward here.
To create a simple XML document, we can do use the New-Object command. This gives us access to the .NET object model.
PS > $doc = New-Object -Type System.Xml.XmlDocument PS > $doc.LoadXml("<root><e publish='2019-08-01'>content</e><e publish='2119-08-20'>future</e></root>") PS > $doc.root.SelectNodes("e") publish #text ------- ----- 2019-08-01 content 2119-08-20 future
So now, we can create our own little navigator and evaluate XPath expressions here.
PS > $n = $doc.CreateNavigator() PS > $n.Evaluate("string(/root/e[1]/@publish)") 2019-08-01
Go play!
Putting everything together, and remembering that sadly XPath 1.0 doesn't deal with string ordering, only equality, we resort to turning dates into numerical representation.
PS > $todayStr = $d.ToString("yyyyMMdd") PS > $todayStr 20190811 PS > $n.Evaluate("/root/e[translate(@publish, '-', '') < '$todayStr']") NameTable : System.Xml.NameTable NodeType : Element LocalName : e NamespaceURI : Name : e Prefix : Value : content BaseURI : IsEmptyElement : False XmlLang : UnderlyingObject : e HasAttributes : True HasChildren : True SchemaInfo : System.Xml.XmlName CanEdit : True IsNode : True XmlType : TypedValue : content ValueType : System.String ValueAsBoolean : ValueAsDateTime : ValueAsDouble : ValueAsInt : ValueAsLong : OuterXml : <e publish="2019-08-01">content</e> InnerXml : content
Enjoy!
Tags: powershell