Repository: PrefectHQ/prefect
Pull request: Use platform independent datetime in `prefect version`
closes #17689 

also consolidates scattered windows tests (that were not running or passing) in preparation of bringing back windows tests in a later PR in some limited capacity, which are currently failing database tests due to a timestamp related unique constraint conflict

Problem statement:
Parsing of version in windows leads to error 'ValueError: Invalid format string'
### Bug summary

Running 'prefect version' leads to the following error:
  File "\Lib\site-packages\prefect\cli\root.py", line 115, in version
    "Built": parse_datetime(prefect.__version_info__["date"]).strftime(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: Invalid format string

Erronous code (line 115-117):
        "Built": parse_datetime(prefect.__version_info__["date"]).strftime(
            "%a, %b %d, %Y %-I:%M %p"
        ),

This code probably works fine in Linux/Unix based systems, but not on windows. 

Possible fix:
        "Built": parse_datetime(prefect.__version_info__["date"]).strftime(
            "%a, %b %d, %Y %I:%M %p"
        ),


### Version info

```Text
Version:             3.3.1
API version:         0.8.4
Python version:      3.12.8
Git commit:          6a164620
Built:               Tue, Apr 01, 2025 02:39 AM
OS/Arch:             win32/AMD64
Profile:             ephemeral
Server type:         cloud
Pydantic version:    2.11.1
Integrations:
  prefect-azure:     0.4.3
  prefect-dbt:       0.6.6
  prefect-docker:    0.6.2
  prefect-shell:     0.3.1
```

### Additional context

_No response_

Hints from issue discussion:
thanks @freekmenger for the issue, [I think you are right](https://github.com/PrefectHQ/marvin/issues/795#issuecomment-1902541274). this should be fixed in the PR above

Commit message: omit windows tests to fix in another PR

Patch to review:
diff --git a/.github/workflows/test-windows-unc.yaml b/.github/workflows/test-windows-unc.yaml
deleted file mode 100644
index 3def94b4ec23..000000000000
--- a/.github/workflows/test-windows-unc.yaml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: Test Windows UNC Paths
-
-on:
-  workflow_dispatch: # Allow manual triggering
-  pull_request:
-    paths:
-      - "src/prefect/utilities/filesystem.py"
-      - "scripts/test_unc_paths.py"
-      - ".github/workflows/test-windows-unc.yaml"
-      - "requirements.txt"
-      - "requirements-client.txt"
-
-jobs:
-  test-unc-paths:
-    runs-on: windows-latest
-
-    steps:
-      - uses: actions/checkout@v4
-        with:
-          persist-credentials: false
-          fetch-depth: 0
-
-      - name: Set up Python
-        uses: actions/setup-python@v5
-        with:
-          python-version: "3.12"
-
-      - name: Install dependencies
-        run: |
-          python -m pip install -U pip
-          pip install -e .
-
-      - name: Create test UNC path and run flow
-        shell: pwsh
-        run: |
-          # Create a test directory
-          New-Item -ItemType Directory -Path "C:\ShareTest" -Force
-
-          # Create network share
-          New-SmbShare -Name "PrefectTest" -Path "C:\ShareTest" -FullAccess "Everyone"
-
-          # Run the test script from the current directory
-          # This will create and test flows in the UNC path
-          python scripts/test_unc_paths.py
-
-        env:
-          PYTHONPATH: ${{ github.workspace }}
-
-      - name: Cleanup
-        if: always()
-        shell: pwsh
-        run: |
-          Remove-SmbShare -Name "PrefectTest" -Force
diff --git a/.github/workflows/windows-pull-request.yaml b/.github/workflows/windows-pull-request.yaml
deleted file mode 100644
index 1871328bc009..000000000000
--- a/.github/workflows/windows-pull-request.yaml
+++ /dev/null
@@ -1,76 +0,0 @@
-name: Windows tests (Pull Request)
-
-on:
-  pull_request:
-    branches:
-      - main
-    types:
-      - opened
-      - reopened
-      - synchronize
-      - labeled
-      - unlabeled
-
-permissions: {}
-
-# Limit concurrency by workflow/branch combination.
-#
-# For pull request builds, pushing additional changes to the
-# branch will cancel prior in-progress and pending builds.
-#
-# For builds triggered on a branch push, additional changes
-# will wait for prior builds to complete before starting.
-#
-# https://docs.github.com/en/actions/using-jobs/using-concurrency
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
-
-jobs:
-  run-tests-sqlite:
-    name: Test with SQLite
-    if: contains(github.event.pull_request.labels.*.name, 'arch:windows')
-
-    strategy:
-      matrix:
-        python-version:
-          - "3.9"
-          - "3.10"
-          - "3.11"
-          - "3.12"
-
-      fail-fast: true
-
-    permissions:
-      contents: read
-
-    runs-on: windows-latest
-    timeout-minutes: 45
-
-    env:
-      # enable colored output
-      # https://github.com/pytest-dev/pytest/issues/7443
-      PY_COLORS: 1
-
-    steps:
-      - uses: actions/checkout@v4
-        with:
-          persist-credentials: false
-          fetch-depth: 0
-
-      - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v5
-        with:
-          python-version: ${{ matrix.python-version }}
-          cache: "pip"
-          cache-dependency-path: "requirements*.txt"
-
-      - name: Install packages
-        run: |
-          python -m pip install --upgrade pip
-          pip install --upgrade --upgrade-strategy eager -e .[dev]
-
-      - name: Run tests
-        run: |
-          # Parallelize tests by scope to reduce expensive service fixture duplication
-          pytest tests -vv --numprocesses auto --dist worksteal --exclude-services --durations=25
diff --git a/.github/workflows/windows-tests.yaml b/.github/workflows/windows-tests.yaml
index 6b2375c0c1e7..64784a67bf08 100644
--- a/.github/workflows/windows-tests.yaml
+++ b/.github/workflows/windows-tests.yaml
@@ -1,9 +1,14 @@
-name: Windows tests
+name: Windows Tests
 
 on:
-  workflow_dispatch: {}
-  schedule:
-    - cron: '0 16 * * *'  # every day at 4 p.m. UTC / 9 a.m. PDT
+  workflow_dispatch:
+  # pull_request:
+  #   branches:
+  #   paths:
+  #     - "pyproject.toml"
+  #     - "src/prefect/**/*.py"
+  #     - "scripts/test_unc_paths.py"
+  #     - ".github/workflows/windows-tests.yaml"
 
 permissions: {}
 
@@ -15,8 +20,6 @@ jobs:
       matrix:
         python-version:
           - "3.9"
-          - "3.10"
-          - "3.11"
           - "3.12"
 
       fail-fast: true
@@ -25,11 +28,10 @@ jobs:
       contents: read
 
     runs-on: windows-latest
-    timeout-minutes: 45
+    timeout-minutes: 10
 
     env:
       # enable colored output
-      # https://github.com/pytest-dev/pytest/issues/7443
       PY_COLORS: 1
 
     steps:
@@ -38,19 +40,55 @@ jobs:
           persist-credentials: false
           fetch-depth: 0
 
-      - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v5
+      - name: Set up uv and Python ${{ matrix.python-version }}
+        uses: astral-sh/setup-uv@v5
         with:
+          enable-cache: true
           python-version: ${{ matrix.python-version }}
-          cache: "pip"
-          cache-dependency-path: "requirements*.txt"
-
-      - name: Install packages
-        run: |
-          python -m pip install --upgrade pip
-          pip install --upgrade --upgrade-strategy eager -e .[dev]
+          cache-dependency-glob: "pyproject.toml"
 
       - name: Run tests
         run: |
           # Parallelize tests by scope to reduce expensive service fixture duplication
-          pytest tests -vv --numprocesses auto --dist worksteal --exclude-services --durations=25
+          uv run pytest tests -vv --numprocesses auto --dist worksteal --exclude-services --durations=25
+
+  test-unc-paths:
+    name: Test Windows UNC Paths
+    # Only run this job when triggered manually
+    if: github.event_name == 'workflow_dispatch'
+
+    runs-on: windows-latest
+    timeout-minutes: 5
+
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          persist-credentials: false
+          fetch-depth: 0
+
+      - name: Set up Python
+        uses: astral-sh/setup-uv@v5
+        with:
+          enable-cache: true
+          python-version: "3.12"
+          cache-dependency-glob: "pyproject.toml"
+
+      - name: Create test UNC path and run flow
+        shell: pwsh
+        run: |
+          # Create a test directory
+          New-Item -ItemType Directory -Path "C:\ShareTest" -Force
+
+          # Create network share
+          New-SmbShare -Name "PrefectTest" -Path "C:\ShareTest" -FullAccess "Everyone"
+
+          # Run the test script from the current directory
+          uv run scripts/test_unc_paths.py
+        env:
+          PYTHONPATH: ${{ github.workspace }}
+
+      - name: Cleanup
+        if: always()
+        shell: pwsh
+        run: |
+          Remove-SmbShare -Name "PrefectTest" -Force
diff --git a/src/prefect/cli/root.py b/src/prefect/cli/root.py
index 13bafa0e62c8..7b8453b1f356 100644
--- a/src/prefect/cli/root.py
+++ b/src/prefect/cli/root.py
@@ -7,7 +7,7 @@
 import sys
 from importlib.metadata import PackageNotFoundError
 from importlib.metadata import version as import_version
-from typing import Any, Dict
+from typing import Any
 
 import typer
 
@@ -107,14 +107,16 @@ async def version(
     from prefect.server.utilities.database import get_dialect
     from prefect.settings import PREFECT_API_DATABASE_CONNECTION_URL
 
-    version_info = {
+    date = prefect.__version_info__["date"]
+    if date is None:
+        date = "unknown"
+
+    version_info: dict[str, Any] = {
         "Version": prefect.__version__,
         "API version": SERVER_API_VERSION,
         "Python version": platform.python_version(),
         "Git commit": prefect.__version_info__["full-revisionid"][:8],
-        "Built": parse_datetime(prefect.__version_info__["date"]).strftime(
-            "%a, %b %d, %Y %-I:%M %p"
-        ),
+        "Built": parse_datetime(date).strftime("%a, %b %d, %Y %I:%M %p"),
         "OS/Arch": f"{sys.platform}/{platform.machine()}",
         "Profile": prefect.context.get_settings_context().profile.name,
     }
@@ -143,21 +145,26 @@ async def version(
     display(version_info)
 
 
-def get_prefect_integrations() -> Dict[str, str]:
+def get_prefect_integrations() -> dict[str, str]:
     """Get information about installed Prefect integrations."""
     from importlib.metadata import distributions
 
-    integrations = {}
+    integrations: dict[str, str] = {}
     for dist in distributions():
         if dist.metadata["Name"].startswith("prefect-"):
             author_email = dist.metadata.get("Author-email", "").strip()
             if author_email.endswith("@prefect.io>"):
+                if (  # TODO: remove clause after updating `prefect-client` packaging config
+                    dist.metadata["Name"] == "prefect-client"
+                    and dist.version == "0.0.0"
+                ):
+                    continue
                 integrations[dist.metadata["Name"]] = dist.version
 
     return integrations
 
 
-def display(object: Dict[str, Any], nesting: int = 0) -> None:
+def display(object: dict[str, Any], nesting: int = 0) -> None:
     """Recursive display of a dictionary with nesting."""
     for key, value in object.items():
         key += ":"
diff --git a/tests/cli/test_version.py b/tests/cli/test_version.py
index 3d9017fbe040..cf376bf9efb9 100644
--- a/tests/cli/test_version.py
+++ b/tests/cli/test_version.py
@@ -18,6 +18,8 @@
 from prefect.testing.cli import invoke_and_assert
 from prefect.types._datetime import parse_datetime
 
+DESIRED_DATE_FORMAT = "%a, %b %d, %Y %I:%M %p"
+
 
 @pytest.mark.usefixtures("disable_hosted_api_server")
 def test_version_ephemeral_server_type():
@@ -82,7 +84,7 @@ def test_correct_output_ephemeral_sqlite(monkeypatch: pytest.MonkeyPatch):
                 API version:         {SERVER_API_VERSION}
                 Python version:      {platform.python_version()}
                 Git commit:          {version_info["full-revisionid"][:8]}
-                Built:               {built.strftime("%a, %b %d, %Y %-I:%M %p")}
+                Built:               {built.strftime(DESIRED_DATE_FORMAT)}
                 OS/Arch:             {sys.platform}/{platform.machine()}
                 Profile:             {profile.name}
                 Server type:         ephemeral
@@ -119,7 +121,7 @@ def test_correct_output_ephemeral_postgres(monkeypatch: pytest.MonkeyPatch):
                 API version:         {SERVER_API_VERSION}
                 Python version:      {platform.python_version()}
                 Git commit:          {version_info["full-revisionid"][:8]}
-                Built:               {built.strftime("%a, %b %d, %Y %-I:%M %p")}
+                Built:               {built.strftime(DESIRED_DATE_FORMAT)}
                 OS/Arch:             {sys.platform}/{platform.machine()}
                 Profile:             {profile.name}
                 Server type:         ephemeral
@@ -144,7 +146,7 @@ def test_correct_output_non_ephemeral_server_type():
 API version:         {SERVER_API_VERSION}
 Python version:      {platform.python_version()}
 Git commit:          {version_info["full-revisionid"][:8]}
-Built:               {built.strftime("%a, %b %d, %Y %-I:%M %p")}
+Built:               {built.strftime(DESIRED_DATE_FORMAT)}
 OS/Arch:             {sys.platform}/{platform.machine()}
 Profile:             {profile.name}
 Server type:         server


Code review comments:
diff_hunk: @@ -1,9 +1,14 @@
-name: Windows tests
+name: Windows Tests
 
 on:
-  workflow_dispatch: {}
-  schedule:
-    - cron: '0 16 * * *'  # every day at 4 p.m. UTC / 9 a.m. PDT
+  workflow_dispatch:
path: .github/workflows/windows-tests.yaml
text: @user1:
```suggestion
  workflow_dispatch: {}
```


@author:
it should work either way but updated since `permissions` is the same way and it's easier to read perhaps 👍

diff_hunk: @@ -107,14 +107,16 @@ async def version(
     from prefect.server.utilities.database import get_dialect
     from prefect.settings import PREFECT_API_DATABASE_CONNECTION_URL
 
-    version_info = {
+    date = prefect.__version_info__["date"]
+    if date is None:
+        date = "unknown"
+
+    version_info: dict[str, Any] = {
         "Version": prefect.__version__,
         "API version": SERVER_API_VERSION,
         "Python version": platform.python_version(),
         "Git commit": prefect.__version_info__["full-revisionid"][:8],
-        "Built": parse_datetime(prefect.__version_info__["date"]).strftime(
-            "%a, %b %d, %Y %-I:%M %p"
-        ),
+        "Built": parse_datetime(date).strftime("%a, %b %d, %Y %I:%M %p"),
path: src/prefect/cli/root.py
text: @user1:
Looks like `parse_datetime("unknown")` raises an error:

```python
ParserError: Unable to parse string [unknown]
```

@author:
good catch! updated in [5e3d633](https://github.com/PrefectHQ/prefect/pull/17692/commits/5e3d63315c11ed9459863b370da6962a85f8b9af)