The major trick is being able to grant your "SharePoint Administrator" account as site collection administrator temporarily for that operation without changing the default Site Owner.

The PnP command to help you in that case is "**Set-PnPTenantSite**" and some CSOM usages in an old school way to avoid PnP context mixed-up.

For you information, "**Set-PnPUser**" works only if your account is already granted as site collection administrator.

The PS script below would help you.

```powershell
Import-Module SharePointPnPPowerShellOnline

$tenantName = "<YOUR_TENANT_NAME>"
$localeId = 2057 # UK
$timeZoneId = 2 # London


#Connect to SPO Tenant
$cnx = Connect-PnPOnline "https://$tenantName-admin.sharepoint.com" -UseWebLogin -ReturnConnection


#Get Tenant CSOM instance
$tenant = new-object Microsoft.Online.SharePoint.TenantAdministration.Tenant([Microsoft.SharePoint.Client.ClientRuntimeContext]$cnx.Context)
function Remove-PnPTenantSiteCollectionAdministrator{
    param([string]$siteUrl, [string[]]$users)
    $ctx = $tenant.Context;
    $targetSite = $tenant.GetSiteByUrl($siteUrl);
    $ctx.Load($targetSite);
    $ctx.ExecuteQuery();

    $users |%{
        $user = $targetSite.RootWeb.EnsureUser($_);
        $ctx.Load($user)
        $ctx.ExecuteQuery();
        if($user.IsSiteAdmin){
            $user.IsSiteAdmin = $false;
            $user.Update();
        }
    }
    $ctx.ExecuteQuery();
}
function Get-PnPTenantSiteAuthor{
    param([string]$siteUrl)
    $ctx = $tenant.Context;
    $targetSite = $tenant.GetSiteByUrl($siteUrl);
    $author = $targetSite.RootWeb.Author
    $ctx.Load($author);
    $ctx.ExecuteQuery()
    [pscustomobject]@{
        LoginName = $author.LoginName
        DisplayName = $author.Title
        UserPrincipalName = $author.UserPrincipalName
        Email = $author.Email
    }
}

#Get current UserName
$tenantWeb = Get-PnPWeb
$tenantWeb.Context.Load($tenantWeb.CurrentUser)
$tenantWeb.Context.ExecuteQuery()
$currentUser = $tenantWeb.CurrentUser



#Get All sites then execute
Get-PnPTenantSite | %{
    $site = $_;
    
    #Grant current user as admin if it's not already it's own
    if($site.OwnerEmail -ne $currentUser.Email){
        $site.Url | Set-PnPTenantSite -Owners $currentUser.Email |out-null
    }

    $user = Get-PnPTenantSiteAuthor $site.Url

    try{
        $ctx = $tenant.Context;

        $targetSite = $tenant.GetSiteByUrl($site.Url);
        $web = $targetSite.RootWeb;

        $timeZones = $web.RegionalSettings.TimeZones
        $ctx.Load($timeZones)
        $ctx.ExecuteQuery()

        $timeZone = $timeZones | ?{ $_.Id -eq $timeZoneId }

        $web.RegionalSettings.LocaleId = $localeId
        $web.RegionalSettings.TimeZone = $timeZone

        $web.Update()
        $ctx.ExecuteQuery()
    
    }
    finally{
    
        #Remove Current User as admin except for it's own
        if($site.OwnerEmail -ne $currentUser.Email){
            Remove-PnPTenantSiteCollectionAdministrator $site.Url $currentUser.LoginName |out-null
        }
        #ensure owner is the real owner
        if([string]::IsNullOrEmpty($site.Owner)){
            $site.Url | Set-PnPTenantSite -Owners $res.UserPrincipalName |out-null
        }

    }

}
```