5

#PSTip Comparing DateTime with a given precision

Note: This tip requires PowerShell 2.0 or above.

Comparing two System.DateTime objects in PowerShell is really easy. All you need to do is:

PS> $date1 = $date2 = Get-Date
PS> $date1 -eq $date2
True

When the two objects are compared their Ticks properties are in fact compared:

PS> $date1.Ticks -eq $date2.Ticks
True

Ticks are the most precise way to represent a DateTime; there are 10,000 ticks in one millisecond. That is 10 million ticks in one second. In my current project I needed the comparisons to be a little less precise and consider DateTime objects equal even if they were a few milliseconds apart. To do that I decided to trim the DateTime objects by removing any excess milliseconds and ticks.

Doing that in PowerShell 3.0 is pretty easy:

PS> $date = Get-Date
PS> $date.Ticks
635276593924675678

PS> Get-Date -Date ($date) -Millisecond 0  | Select -ExpandProperty Ticks
635276593260000000

This will take the current date and set its Millisecond and any extra Ticks to 0.

In PowerShell 2.0 the conversion is a little bit more challenging because there is no Millisecond parameter there. And you can’t simply use the AddMilliseconds method to remove the extra milliseconds, because that won’t affect the extra ticks:

PS> $date.AddMilliseconds(- $date.Millisecond) | Select -ExpandProperty Ticks
635276593920005678

Another workaround has to be used:

PS> [datetime]($Date.Ticks - $date.Ticks % 10000000) | Select -ExpandProperty Ticks
635276593260000000

I needed to make the script compatible with PowerShell 2.0 and I also wanted it to be readable. So I ended up writing function to do it, and more:

function Trim-Date {

    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [DateTime]$Date,

        [ValidateSet('Tick','Millisecond','Second','Minute','Hour','Day')]
        [String]$Precision = 'Second'
    )

    process {

        if ($Precision -eq 'Tick')
        {
            return $Date
        }

        $TickCount = Switch ($Precision)
        {
            'Millisecond' { 10000; break }
            'Second' { ( New-TimeSpan -Seconds 1 ).ticks; break }
            'Minute' { ( New-TimeSpan -Minutes 1 ).ticks; break }
            'Hour' { ( New-TimeSpan -Hours 1 ).ticks; break }
            'Day' { ( New-TimeSpan -Days 1 ).ticks; break }
        }

        $Result = $Date.Ticks - ( $Date.Ticks % $TickCount )
        [DateTime]$Result
    }
}

This function takes two parameters: Date and Precision. The Date is the DateTime object to be trimmed and the Precision specifies what is the biggest unit to be kept. If, for example, Hour precision is specified, minutes, seconds, millisecond and extra ticks are set to 0. In the following example the default Second precision is used so just milliseconds and any extra ticks are set to zero.

PS> $date | Trim-Date -Precision Second  | Select -ExpandProperty Ticks
635276593260000000

The function also turned out to be great for checking if two timestamps were created during the same day:

PS> ([datetime]"2013/12/10 12:14:36" | Trim-Date -Precision Day) -eq ([datetime]"2013/12/10 18:10:21" | Trim-Date -Precision Day)
True

or creating precise plans for the next day:

PS> (Get-Date -Hour 11).AddDays(1) | Trim-Date -Precision Hour
Tuesday, February 11, 2014 11:00:00 AM
Filed in: Columns, Tips and Tricks Tags: , ,

5 Responses to "#PSTip Comparing DateTime with a given precision"

  1. Mike Shepard says:

    This is interesting. I like the trim-date function, but wouldn’t it make sense to check the difference in the ticks property against some threshold instead?

    • nohandle says:

      Hi, thanks for the comment. I am sure that in some cases it would definitely make sense, but to me it sounds more like Round-Date. As usual there are more ways of doing it. Originally I searched for a way to trim a date because in my current project I save a lot of DateTime objects in CliXml format, and later compare them. When exported to CliXml the objects keep all the milliseconds and ticks. In the project I also log into log files and that’s where the problem arised. Two DateTime objects that are no equal can look exactly the same when converted to string “2014 02. 01. 10:30:05”. I got >>> “2014 02.01. 10:30:05” is not equal to “2014 02.01. 10:30:05” Updating<<< in my logs all the time and hunting such bugs was a nightmare. I realized I could either convert the dates myself and save them with the milliseconds, ticks and stuff and it would be really hard to quickly compare two DateTimes just by looking at them. Or I could use the default conversion to string but I would also have to make sure all my DateTimes are trimmed to seconds. This meant sacrificing some precision for convenience which I gladly did. In the end I ended up trimming all the DateTimes and just to be sure also compare them with a given precision. -Jakub

  2. Rob Campbell says:

    You can also use .tooadate(), and trim that down to decimal fractions of a day.

    • nohandle says:

      Hi thanks for the comment. You can also take the two dates and subtract them, and get a TimeSpan object as a result. You can then use .Total* properties of the object to evaluate the comparison. You can also work around the missing Millisecond parameter in PowerShell v2 by using a constructor of the System.DateTime type as such New-Object System.DateTime 2014,2,14,16,23,19,0 (Shay Levy pointed this out, thanks.) and go from there. But I chose the approach described in the article. -Jakub

Leave a Reply

Submit Comment

© 2016 PowerShell Magazine. All rights reserved. XHTML / CSS Valid.
Proudly designed by Theme Junkie.
%d bloggers like this: