How to Display Modified Time and Modified By Fields Correctly with Powershell - powershell

I have a script that outputs to CSV all items/files from all Lists and Libraries. In addition, it displays the current and previous versions of an item/file. This also displays which user modified the file for each version and also displays the date/time the file was modified for every version:
function Get-DocInventory([string]$siteUrl) {
$web = Get-SPWeb "http://contoso.com/sites/Depts3/HBG"
foreach ($list in $web.Lists) {
foreach ($item in $list.Items) {
foreach($version in $item.Versions){
$data = #{
"Version" = $version.VersionLabel
"List Name" = $list.Title
"Created By" = $item["Author"]
"Created Date" = ($item["Created"] -as [datetime]).DateTime
"Modified By" = $version["Editor"]
"Modified Date" = ($version["Modified"] -as [datetime]).DateTime
"Item Name" = $item.Name
}
New-Object PSObject -Property $data | Select "List Name", "Item Name", "Version", "Created By", "Created Date", "Modified By", "Modified Date"
}
}
$web.Dispose();
}
}
Get-DocInventory | Export-Csv -NoTypeInformation -Path C:\GenerateReport.csv
Below is a sample of what the script outputs:
And below is an excel example of what I see when I go to the Version History of the Lions.pdf file:
I have 2 problems:
The column Modified By is displaying email and domain username which is not required.
My script displays the column Modified Date as 5 hours ahead. The script output for Version 1 of Lions.pdf displays time as 11:23 AM. But when I go to Version History of Lions.pdf it says that Version 1 was modified at 6:23 AM. The time discrepancy is the same across the board as 5 hours ahead which is incorrect.
I can't seem to figure out what I am doing wrong here. Can someone please assist in the issue I am having? My main concern is the time and I would greatly appreciate any assistance.

User Fields
Normally in server side code, accessing a user field value using array notation (eg myItem["Author"]) would return an object that you would cast to an appropriate type (Microsoft.SharePoint.SPFieldUserValue). Powershell, however, automatically casts these values to strings, which is why you're getting undesirable values.
Fortunately, there's a way around it! First, get the field itself as an object, then pass the field value into the field's GetFieldValue() method.
$userField = $item.Fields.GetField("Author");
$authorObject = $userField.GetFieldValue($item["Author"]);
You'll then have an object with properties you can access to retrieve the desired values, such as LookupValue for the user's display name on the site.
$authorName = $authorObject.LookupValue;
Date Fields
Date fields are little easier, because Powershell will actually hand them over to you as DateTime objects.
To format a DateTime object, you can just call .ToString() and pass in a parameter indicating the desired format.
$data = #{
...
"Modified Date" = $item["Modified"].ToString("MM/dd/yyyy h:mm tt");
...
}
Time Discrepancy
This is most likely attributable to your local timezone. The times displayed on a SharePoint site via the browser are determined by your PC's timezone settings. The times actually saved to SharePoint's underlying database are determined by the server's timezone settings.
To reconcile them, you can use the DateTime object's .ToUniversalTime() method to get a corresponding DateTime object in UTC time (and optionally offset it as desired until it aligns with the local timezone and daylight savings adjustments).
Edit: I'm guessing you're in US Eastern time (UTC-5) and it's giving you results in UTC time (Universal Coordinated Time, which is 5 hours ahead of you except during daylight saving time). You should be able to offset the hours manually to account for this.
$localOffset = -5;
$modified = $version["Modified"] -as [datetime];
if($modified.IsDaylightSavingTime()){$localOffset += 1;}
$modifiedLocal = $modified.addHours(-$localOffset);

Related

I want the sorted list of date using powershell

I am getting list of dates and storing it into an array like
$arraylist={2022-03-24T12:05:05Z 2022-03-29T12:04:10Z 2022-03-23T12:54:07Z 2022-03-26T13:15:36Z 2022-03-22T10:06:11Z 2022-03-23T09:54:08Z}
by using a command $arrayList +=$listdate. Now I want the sorted list of date alone like
$sortedlist={2022-03-29 2022-03-26 2022-03-24 2022-03-23 2022-03-23 2022-03-22}
Not with timing. I am doing these things in PowerShell. The details of date which I am getting is in string format not in date-time format. can anyone help me out with this?
I initially split the date with $arrayList.Split('T') and stored the date alone in another array. But it is not sorted form. I tried to sort it but I am not getting desired sorted list.
Is there any other way to get desired sorted list of date?
If you are getting your dates in a single string (as per the example of data you have provided) it would be possible to split the string into an array and then convert the array of strings into datetime objects:
$DateString = "2022-03-24T12:05:05Z 2022-03-29T12:04:10Z 2022-03-23T12:54:07Z 2022-03-26T13:15:36Z 2022-03-22T10:06:11Z 2022-03-23T09:54:08Z"
$array = $DateString -split " "
$FormattedDates = Foreach ($item in $array){
[DateTime]::ParseExact($Item, "yyyy-MM-ddTHH:mm:ssK", $null)
}
$FormattedDates | Sort | ForEach-Object {$_.ToString("yyyy-MM-dd")}

IF between two times powershell

I am trying to figure out if an issue happened during a certain period of time however for what ever reason nothing seems to be working.
Here is my powershell.ps1 file
$AUS_start = Get-Date -Hour 22 -Minute 00 -Second 00
$AUS_end = Get-Date -Hour 06 -Minute 00 -Second 00
$AS = $AUS_start.ToString("HH:mm:ss")
$AE = $AUS_end.ToString("HH:mm:ss")
foreach ($SHtime in $Rservices.start_time) {
$x = $SHtime.ToString("HH:mm:ss")
$x
if ($x -gt $AS -and $x -lt $AE) {
Write-Host "true"
}
else {
Write-Host "false"
}
}
Here is the response I get
22:18:01
false
19:11:00
false
05:15:00
false
05:15:00
false
02:36:43
false
As you can see, there are certain times for example "22:18:01" that definitely meet the criteria of greater than UTC 22:00:00 and less than 06:00:00 but it still returns a false value.
Any ideas?
this does what i think you want. [grin]
instead of testing for in 2200-0600 it tests for NOT in 0600-2200. that test is simpler since it gives one a continuous range that is all in one day. if you need to allow 2200-2259, you can simply shift the $EndHour to 21.
please note that this explicitly ignores the date and only uses the hour-of-day. if you need to look at the date, that will require some extra code. if i understand your intent, tho, that seems unlikely.
what the code does ...
creates a set of datetime objects to work with
when ready to work with real data, simply remove the entire #region/#endregion block and replace it with a call to your data source.
sets the start/end hours
builds the excluded hours range
iterates thru the collection of test datetime objects
checks to see if the .Hour of the current object is in the $ExcludedHours range
if yes, writes a warning to the screen to not disturb the "normal working hours" folks
if NO, writes a msg that it is safe to do IT work while the office folks are all far away
here's the code ...
#region >>> create some date time items to test with
# when ready to use real data, replace the entire "#region/#endregion" block with the real data source
$RServices = #(
[PSCustomObject]#{
Name = 'LastYearElevenP'
Start_Time = (Get-Date).Date.AddYears(-1).AddHours(23)
},
[PSCustomObject]#{
Name = 'LastMonthElevenP'
Start_Time = (Get-Date).Date.AddMonths(-1).AddHours(23)
},
[PSCustomObject]#{
Name = 'YesterdayElevenP'
Start_Time = (Get-Date).Date.AddDays(-1).AddHours(23)
},
[PSCustomObject]#{
Name = 'ZeroA'
Start_Time = (Get-Date).Date
},
[PSCustomObject]#{
Name = 'OneA'
Start_Time = (Get-Date).Date.AddHours(1)
},
[PSCustomObject]#{
Name = 'ThreeA'
Start_Time = (Get-Date).Date.AddHours(3)
},
[PSCustomObject]#{
Name = 'SixA'
Start_Time = (Get-Date).Date.AddHours(6)
},
[PSCustomObject]#{
Name = 'SevenA'
Start_Time = (Get-Date).Date.AddHours(7)
},
[PSCustomObject]#{
Name = 'NineA'
Start_Time = (Get-Date).Date.AddHours(9)
},
[PSCustomObject]#{
Name = 'ElevenP'
Start_Time = (Get-Date).Date.AddHours(23)
}
)
#endregion >>> create some date time items to test with
$StartHour = 6
$EndHour = 22
$ExcludedHours = $StartHour..$EndHour
foreach ($RS_Item in $RServices)
{
if ($RS_Item.Start_Time.Hour -in $ExcludedHours)
{
Write-Warning (' {0} is in the Excluded Hours ...' -f $RS_Item.Start_Time.ToString('yyyy-MM-dd HH:mm:ss'))
Write-Warning ' Do nothing disruptive at this time.'
}
else
{
'{0} is NOT in the excluded hour range.' -f $RS_Item.Start_Time.ToString('yyyy-MM-dd HH:mm:ss')
' Now is the time to do things that the office folks might complain about.'
}
'=' * 30
}
the screen output ...
2019-04-29 23:00:00 is NOT in the excluded hour range.
Now is the time to do things that the office folks might complain about.
==============================
2020-03-29 23:00:00 is NOT in the excluded hour range.
Now is the time to do things that the office folks might complain about.
==============================
2020-04-28 23:00:00 is NOT in the excluded hour range.
Now is the time to do things that the office folks might complain about.
==============================
2020-04-29 00:00:00 is NOT in the excluded hour range.
Now is the time to do things that the office folks might complain about.
==============================
2020-04-29 01:00:00 is NOT in the excluded hour range.
Now is the time to do things that the office folks might complain about.
==============================
2020-04-29 03:00:00 is NOT in the excluded hour range.
Now is the time to do things that the office folks might complain about.
==============================
WARNING: 2020-04-29 06:00:00 is in the Excluded Hours ...
WARNING: Do nothing disruptive at this time.
==============================
WARNING: 2020-04-29 07:00:00 is in the Excluded Hours ...
WARNING: Do nothing disruptive at this time.
==============================
WARNING: 2020-04-29 09:00:00 is in the Excluded Hours ...
WARNING: Do nothing disruptive at this time.
==============================
2020-04-29 23:00:00 is NOT in the excluded hour range.
Now is the time to do things that the office folks might complain about.
==============================

Is there a way to grab Outlook calendar start times for recurring events in Powershell?

In Powershell, I am trying to get start times of calendar entries from my Outlook - this works fine except for recurring appointment, the date populated appears to be from when the recurring event was initially created. Is there a way to get the calendar date entry, as opposed to when the recurring event was first entered in? I may be using the incorrect property;
$outlook = New-Object -ComObject outlook.application
$namespace = $outlook.GetNameSpace('MAPI')
$Calendar = [Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderCalendar
$folder = $namespace.getDefaultFolder($Calendar).items
#$folder.IncludeRecurrences = $True
$StartWeek = '02/12/2019'
$StartWeekDT = [datetime]::ParseExact($StartWeek,'dd/MM/yyyy', [cultureinfo]::InvariantCulture)
$EndWeekDT = $StartWeekDT.AddDays(5)
$Start = $StartWeekDT.ToString('dd/MM/yyyy') + " 00:00"
$End = $EndWeekDT.ToString('dd/MM/yyyy') + " 00:00"
$Filter = "[Start]>= '$Start' AND [End] <= '$End'"
$data = $Folder.Restrict($filter) | Select-Object
So here you can see that the filter says that the start property must be greater than 02/12 - which does pick up the recurring event entry. However, when I attempt to view the start property, it shows a date from January (when it was created) instead. I cannot see the correct date in any of the properties, and I've tried setting IsRecurring to false (to see if it would recreate the entry with the correct start) and by using .IncludeRecurrences as suggested by a few online articles, but no luck. Has anyone had this issue before?
05/12/2019 10:30:00
05/12/2019 10:00:00
05/12/2019 15:00:00
02/12/2019 16:30:00
03/12/2019 11:00:00
04/12/2019 16:00:00
05/12/2019 15:30:00
05/12/2019 16:30:00
14/01/2019 08:30:00
11/01/2019 16:30:00
16/01/2019 12:00:00
Could not figure out how to get the 'just this occurrence' start property, so I just found the start value a different way by adding on 7 days (from the recurring start date) until it was within my search scope, then assigned this to the $entry.start variable - for further processing. Note that this does not amend the calendar entry, but just updates $entry.start to the value within the week after the property has already been pulled. I can then query to see if it was on a monday, tuesday, etc.
Foreach($Entry in $data){
IF ($Entry.Start -lt $EndWeekDT){
for($i = $Entry.Start; $i -le $EndWeekDT; $i = $i.AddDays(7))
{
$Entry.Start = $i
}
}
etc..
}

How to convert powershell UTC datetime object to EST

I have date time strings coming in, formatted like the following:
2017-08-03T12:30:00.000Z
I need to be able to convert these to EST. Every function I have tried throws one error or another, typically being:
"String was not recognized as a valid DateTime."
I have tried variations of the below:
$time = '2017-08-03T12:30:00.000Z'
[datetime]$datetime = $time
$culture = [Globalization.CultureInfo]::InvariantCulture
[DateTime]::ParseExact($datetime, 'MM-dd-yyyy HH:mm:ss', $culture)
I think it has something to do with how the Date Time string I am referencing has the **T** and then the UTC time, but can't figure out what to do about it. Maybe I should parse out the time, convert it and then reattach to the first part of the string, the date, and combine them together for the final output? Seems like way too much work and a solution which would cause potential errors in the future.
You should be able to convert a Zulu time string to a DateTime value simply by casting it. However, the resulting value will be in local time, so you should convert it back to UTC for further calculations:
$timestamp = '2017-08-03T12:30:00.000Z'
$datetime = ([DateTime]$timestamp).ToUniversalTime()
Then you can use the TimeZoneInfo class to convert the UTC timestamp to the desired timezone:
[TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($datetime, 'Eastern Standard Time')
Use [TimeZoneInfo]::GetSystemTimeZones() | Select-Object Id, DisplayName to get a list of the recognized timezones.
Try using the static ConvertTimeBySystemTimeZoneId() method of the [System.TimeZoneInfo] class:
$time = '2017-08-03T12:30:00.000Z'
$result = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date -Date $time), 'Eastern Standard Time')
The returned $result is a [DateTime].
BTW, if you ever need to convert it back, here's how:
Get-Date -Date $result -Format FileDateTimeUniversal
Hope this helps.

Powershell variable interrogation of date & time difference

Morning All,
I have a variable as follows: $machines = $user2,$name,$serial,$purchased
sample data stored in $machines is:
User1,
Laptop1,
xyz1234,
01/01/2010
I am wanting to create a new variable called $tobereplaced containing all of the records in $machines with a date greater than 4 years old from todays date.
the fuzzy logic code for this im expecting to be someting like $tobereplaced = $machines.$purchased | where {$_$purchased = -getdate > 4 years} etc etc but i cant quite figure it out.
Assistance would be greatly appreciated.
Thanks
$fourYearsAgo = (Get-Date).AddYears(-4)
$tobereplaced = $machines | Where-Object { (Get-Date $_[-1]) -le $fourYearsAgo }
Convert the date as DateTime and compare it against a date four years ago. Like so,
# Assuming $m[3] contains the timestamp, parse it as a DateTime and compare
# against a date four years ago.
if([DateTime]::Parse($m[3]) -le [DateTime]::Now.AddYear(-4)) {
$tobereplaced += $m
}
Depending on your locale, you might need to tell [DateTime]::Parse() how to parse the date. Is 01/12/2010 1st of December, 2010 or 12th January, 2010?