Showing posts with label Date Math. Show all posts
Showing posts with label Date Math. Show all posts

Friday, January 14, 2011

Replicate UNIX 'cal' in PowerShell

This is a good exercise in using dates in a loop. This code replicates somewhat the functionality of the command line application 'cal' that first appeared in Version 5 AT&T UNIX. In the code, you will perform while loops based on months and days. Save the code in a file called 'Output-Calendar.ps1'. If you provide no parameters, it will display a calendar of the current month. If you provide a valid date as a parameter, it will display that month's calendar. If you provide two months, it will generate all the calendars between those two dates. It does not validate if the input are valid dates and assumes the culture is English.

Examples:

.\Output-Calendar.ps1
.\Output-Calendar.ps1 1/2000
.\Output-Calendar.ps1 1/1/2010 12/31/2010
param([string]$start,[string]$end)

Function Get-LastDayOfMonth($date) {
 return ((Get-Date ((((Get-Date $date).AddMonths(1)).Month).ToString() + "/1/" + (((Get-Date $date).AddMonths(1)).Year).ToString()))) - (New-TimeSpan -seconds 1)
}

Set-Variable -name dayOfWeekLine -option Constant -value " Su Mo Tu We Th Fr Sa "
if($start -eq "" -and $end -eq "") {
 $startDate = Get-Date
 $endDate = Get-Date
} elseif($end -eq "") {
 $startDate = Get-Date $start
 $endDate = Get-Date $start
} else {
 $startDate = Get-Date $start
 $endDate = Get-Date $end 
}
if($startDate -gt $endDate) {
 Write-Warning "The Start Date must be earlier than the End Date"
 exit
}
$month = $startDate
Write-Host ""
while($month -le $endDate) {
 $firstDayOfMonth = Get-Date ((((Get-Date $month).Month).ToString() + "/1/" + ((Get-Date $month).Year).ToString() + " 00:00:00"))
 $lastDayOfMonth = Get-LastDayOfMonth $firstDayOfMonth
 $day = $firstDayOfMonth
 $headline = ((Get-Date $firstDayOfMonth -Format MMMM) + " " + $firstDayOfMonth.Year)
 Write-Host (" " * (($dayOfWeekLine.Length - $headline.Length) / 2)) -noNewline
 Write-Host $headline
 Write-Host $dayOfWeekLine
 while($day -le $lastDayOfMonth) {
  if($day.day -eq 1 -and $day.DayOfWeek -ne "Sunday") {
   Write-Host " " -noNewline
   if($day.DayOfWeek -eq "Saturday") {
    Write-Host (" " * 15) -noNewline
   } elseif($day.DayOfWeek -eq "Friday") {
    Write-Host (" " * 12) -noNewline
   } elseif($day.DayOfWeek -eq "Thursday") {
    Write-Host (" " * 9) -noNewline
   } elseif($day.DayOfWeek -eq "Wednesday") {
    Write-Host (" " * 6) -noNewline
   } elseif($day.DayOfWeek -eq "Tuesday") {
    Write-Host (" " * 3) -noNewline
   }
  }
  if($day.DayOfWeek -eq "Saturday") {
   Write-Host (" " + (Get-Date $day -Format dd))
  } else {
   Write-Host (" " + (Get-Date $day -Format dd)) -noNewline
  }
  $day = $day.AddDays(1)
 }
 if($day.DayOfWeek -ne "Sunday") {
  Write-Host ""
 }
 Write-Host ""
 $month = $firstDayOfMonth.AddMonths(1)
}

Friday, January 7, 2011

Active Directory Epoch vs. PowerShell Epoch in Attribute Based LDAP Queries

Active Directory stores time as the number of 100-nanosecond intervals (ticks) that have elapsed since midnight, January 1, 1601 UTC (GMT) in attributes such as LastLogon, LastLogonTimestamp, LastPwdSet and AccountExpires. PowerShell's epoch begins midnight, January 1, 0001 UTC. This leads to a little problem when you want to search objects by time-based attributes via an LDAP query in Active Directory using PowerShell. You can't simply say "(AccountExpires<=" + ((Get-Date).AddDays(-90)).Ticks + ")" to search for user objects that expired 90 days ago. You have to calculate the offset between Active Directory epoch and PowerShell epoch. To do this, you need to store the Active Directory epoch as  DateTime in PowerShell and subract its Ticks from the DateTime Ticks you are interested in querying against. Commonly, you will find scripts on the Internet that do the same process but have you return all objects and loop them through an if/then statement to perform the same process on a calculated attribute. Doing this ticks-based query upfront is more efficient and returns results much faster -- particularly in domains with large numbers of objects. Here is an example of an LDAP filter that could be used to find user objects that have expired 90 or more days ago.
$activeDirectoryEpoch = (Get-Date "1601-1-1T00:00:00-00:00").ToUniversalTime()
$expirationDate = (((Get-Date).AddDays(-90)).ToUniversalTime()).Ticks - $activeDirectoryEpoch.ticks
$ldapFilter = "(&(objectCategory=person)(objectClass=user)(AccountExpires<=$expirationDate)(!AccountExpires=9223372036854775807)(!AccountExpires=0))"

Thursday, January 6, 2011

Determine the Last Day of the Month in PowerShell

I run into situations where I need to export mailboxes using Export-Mailbox and need to break up the returned PST files by month. To do this, I need to know the start of the month (easy) and the end of the month (not as easy). This is how I generate that DateTime variable.
$date = "02/15/2008" # Using a date in a leap year for fun
$firstDayOfMonth = Get-Date ((((Get-Date $date).Month).ToString() + "/1/" + ((Get-Date $date).Year).ToString() + " 00:00:00"))
$lastDayOfMonth = ((Get-Date ((((Get-Date $firstDayOfMonth).AddMonths(1)).Month).ToString() + "/1/" + (((Get-Date $firstDayOfMonth).AddMonths(1)).Year).ToString()))) - (New-TimeSpan -seconds 1)
Write-Host ("-StartDate " + (Get-Date $firstDayOfMonth -format d) + " -EndDate " + (Get-Date $lastDayOfMonth -format d))