Get-TimeSheet.ps1

function Get-TimeSheet {
    <#
    .SYNOPSIS
        Gets the contents of the timesheet file and parses it.
    .DESCRIPTION
        Gets the content of the timesheet file with the directory given by the
        -directory parameter, with the format yyyyMMdd.txt, and parses the
        data.
    .EXAMPLE
        PS C:\> Get-TimeSheet -Directory $HOME | Format-Table
 
        Date Subject Action Duration DurationToNearest15
        ---- ------- ------ -------- -------------------
        28/07/2022 08:37:00 Daily Checks Start 00:00:00 00:15:00
        28/07/2022 09:14:00 Daily Checks End 00:37:00 00:45:00
        28/07/2022 09:14:00 Daily Standup Start 00:00:00 00:15:00
        28/07/2022 10:07:00 Daily Standup End 00:53:00 01:00:00
        28/07/2022 10:14:00 Coffee Break Start 00:07:00 00:15:00
        28/07/2022 11:03:00 Coffee Break End 00:49:00 00:45:00
        28/07/2022 11:03:00 Dedicated Code Review Start 00:00:00 00:15:00
        28/07/2022 11:18:00 Dedicated Code Review End 00:15:00 00:15:00
        28/07/2022 11:22:00 Daily Checks Start 00:04:00 00:15:00
        28/07/2022 11:53:00 Daily Checks End 00:31:00 00:30:00
        28/07/2022 11:56:00 Review Upgrade plan Start 00:03:00 00:15:00
        28/07/2022 12:24:00 Review Upgrade plan End 00:28:00 00:30:00
        28/07/2022 12:35:00 Prep for Upgrade Start 00:11:00 00:15:00
        28/07/2022 12:59:00 Prep for Upgrade End 00:24:00 00:30:00
        28/07/2022 13:00:00 Call with PM: Issue Start 00:01:00 00:15:00
        28/07/2022 13:45:00 Call with PM: Issue End 00:45:00 00:45:00
        28/07/2022 14:24:00 Update Meeting Start 00:39:00 00:45:00
        28/07/2022 15:00:00 Update Meeting End 00:36:00 00:45:00
    #>

    
    [CmdletBinding()]

    param (
        # The date of the timesheet file
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [datetime] $FileDate = (Get-Date),

        # The directory that stores timesheet files
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [ValidateScript({ Test-Path -Path $_ })]
        [Alias('TimeSheetDirectory')]
        [string] $Directory = (
            Split-Path -Path $PSScriptRoot -Parent |
            Split-Path -Parent |
            Join-Path -ChildPath 'TimeSheets'
        )
    )

    process {
        $dateFormatted = Get-Date -Date $FileDate -Format FileDate

        Write-PSFMessage -Level Verbose -Message 'Finding timesheets'
        try {
            $gciParams = @{
                Path        = $Directory
                Filter      = "*$($dateFormatted)*"
                ErrorAction = 'Stop'
            }
            $timeSheets = Get-ChildItem @gciParams
        }
        catch {
            Write-PSFMessage -Level Warning -Message "Cannot find timesheets matching $dateFormatted in $Directory"
            break
        }

        Write-PSFMessage -Level Verbose -Message "Parsing timesheets"
        $contents = foreach ($timeSheet in $timeSheets) {
            foreach ($content in Get-Content -Path $timeSheet) {
                if ([string]::IsNullOrEmpty($content)) { continue }

                $Date, $Action, $Subject = $content.Split(' - ', 3)

                # Deal with `-as [datetime]` not working properly...
                $Date = [datetime]::ParseExact(
                    $Date,
                    'HH:mm dd/MM/yyyy',
                    $null
                )

                [PSCustomObject]@{
                    Date    = $Date -as [datetime]
                    Action  = $Action
                    Subject = $Subject
                }
            }
        }

        Write-PSFMessage -Level Verbose -Message 'Adding Duration property'
        $prevDate = ($contents | Sort-Object -Property Date)[0].Date

        foreach ($row in $contents) {
            $duration = $row.Date - $prevDate

            if (-not (Test-Path -Path Function:\Get-ClosestToMinute)) {
                $funcImport = '{0}{1}{2}.ps1' -f @(
                    $PSScriptRoot,
                    [IO.Path]::DirectorySeparatorChar,
                    'Get-ClosestToMinute'
                    )
                Write-PSFMessage -Level Verbose -Message "Importing function from $funcImport"

                try {
                    . $funcImport
                }
                catch {
                    Write-PSFMessage -Level Warning -Message "Error importing function from $funcImport"
                }
            }

            $durationAgg = Get-ClosestToMinute -InitialTimeSpan $duration

            [PSCustomObject]@{
                PSTypeName          = 'TimeSheet'
                Date                = $row.Date
                Subject             = $row.Subject
                Action              = $row.Action
                Duration            = $duration
                DurationToNearest15 = $durationAgg.newTimeSpan
            }

            $prevDate = $row.Date
        }
    }
}