A fixed timestamp is not enough when the result still depends on the machine running the test. CultureInfo.CurrentCulture is the default for culture-sensitive formatting and parsing. TimeZoneInfo.Local is the default zone when code converts an instant to local time. That is how a test passes on a German developer machine and fails on a CI runner set to US English and UTC.
Pass both values to the code that needs them:
using System.Globalization;
public static string FormatAppointment(
DateTimeOffset startsAt,
CultureInfo culture,
TimeZoneInfo timeZone)
{
DateTimeOffset localTime = TimeZoneInfo.ConvertTime(startsAt, timeZone);
return localTime.ToString("g", culture);
}
The test now explicitly selects the instant, culture, and time zone instead of inheriting them from the host:
[Fact]
public void Formats_appointment_for_Germany()
{
var startsAt = new DateTimeOffset(
2026, 7, 15, 12, 0, 0, TimeSpan.Zero);
CultureInfo culture = CultureInfo.GetCultureInfo("de-DE");
TimeZoneInfo timeZone =
TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
string result = FormatAppointment(startsAt, culture, timeZone);
Assert.Equal("15.07.2026 14:00", result);
}
Machine defaults are no longer part of the test. The data still comes from somewhere: de-DE uses the runtime’s globalization data, and Europe/Berlin uses the installed time-zone database. Use a named zone when daylight-saving rules are part of the behavior. If the test only needs a known offset, a custom fixed-offset zone avoids that database dependency.
If production code intentionally reads CultureInfo.CurrentCulture, set it at the test boundary and restore the previous value in a finally block. NUnit handles this with [SetCulture("de-DE")]; xUnit can set culture for the whole test assembly, but a per-test [UseCulture] attribute is custom. Set CurrentUICulture only when the test loads localized resources. It selects resources, while CurrentCulture controls formatting, parsing, sorting, and casing.
Do not change DefaultThreadCurrentCulture, the process TZ environment variable, or the host time zone from a parallel unit test. Those changes can leak into unrelated tests. Run a test that must change them in an isolated process. If production code gets its local zone from an injected TimeProvider, give the test a FakeTimeProvider and call SetLocalTimeZone(...). This changes the zone returned by that provider. It does not override TimeZoneInfo.Local.
Use InvariantCulture only when the contract itself is invariant. Wire formats and persisted machine-readable values often need it. Test user-facing output with the culture and time zone required by the scenario.