How can I falsify/fast-forward the runner's system clock?

I would like to increment the system clock on a GitHub-hosted runner, as part of our CI suite involves a test that should pass only if the system clock is a certain number of days after the product’s build date. i.e. We build the product, incremement the clock by X number of days, attempt to run the product and then the product should refuse to work and the test will pass.

This is all working except for the actual business of incrementing the system clock. How can I accomplish this? Ideally on Windows but in Linux as well.

I’ve tried setting the system clock by running: sudo date --set='+20 days', then checking it by running date however that doesn’t seem to actually increment the date. Does anyone have any suggestions?

On Linux it seems to be required to intercept system calls with e.g. libfaketime, at least if you use Docker containers (date --set and hwclock are not permitted): https://serverfault.com/a/825073/481659

Something like this:

- name: Build and install libfaketime
  run: |
    git clone https://github.com/wolfcw/libfaketime.git
    cd libfaketime
    make install

- name: Test with actual time
  run: date

- name: Test with fake time
  env:
    FAKETIME: +20 days
    LD_PRELOAD: /usr/local/lib/faketime/libfaketime.so.1
  run: date

On Windows with PowerShell, you can use the following command:

Set-Date -Adjust (New-TimeSpan -Days 20)

However, this requires elevated rights. I’m not sure if PowerShell is run as administrator in GitHub hosted runners, or if you can elevate a process. I found scripts online which trigger a UAC prompt like so:

Start-Process powershell.exe "-Command `"Set-Date -Adjust (New-TimeSpan -Days 20)`"" -Verb RunAs

but no idea what it does in a headless environment, if it works at all.

Ubuntu has package faketime and libfaketime, so on the default runner VMs you can just install those instead of compiling.

Libfaketime works for linux, thanks. Any solutions for Windows?

My solution works just fine. I guess the runners run as admin or without UAC:

One potential thing to look out for: the system might have automatic clock adjustment enabled and sync it again to actual time at some point.

1 Like

Awesome! I shall give this a go! Although I notice github now thinks the job has taken 20 days… :confused:

Just set the time back, then the duration will be correct.

name: Fake time
on: push
jobs:
  windows:
    runs-on: windows-latest
    steps:
      - name: Before time change
        run: (Get-Date).toString()
      - name: Change time
        run: |
          (Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\services\W32Time\Parameters).Type
          Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\services\W32Time\Parameters -Name Type -Value NoSync
          (Set-Date -Adjust (New-TimeSpan -Days 20)).toString()
          #[Threading.Thread]::Sleep($(New-TimeSpan -Minutes 1))
          (New-TimeSpan -Minutes 1).TotalSeconds | Start-Sleep
          (Set-Date -Adjust (New-TimeSpan -Days -20)).toString()
      - name: After time change
        run: (Get-Date).toString()
  linux:
    runs-on: ubuntu-latest
    steps:
      - run: sudo apt-get install libfaketime
      - name: actual time
        run: date
      - name: faketime
        env:
          FAKETIME: +20 days
          LD_PRELOAD: /usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1
        run: date