Skip to content

Commit d9d8ed2

Browse files
Fix #687: Ensure calendar versioning tests use a consistent time contex
- prevent failures around midnight in non-UTC timezones - Update `meta` function to accept a time parameter for consistent behaviour in tests
1 parent d7a9c83 commit d9d8ed2

File tree

3 files changed

+45
-13
lines changed

3 files changed

+45
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
### Fixed
1717

1818
- fix #1145: ensure GitWorkdir.get_head_date returns consistent UTC dates regardless of local timezone
19+
- fix #687: ensure calendar versioning tests use consistent time context to prevent failures around midnight in non-UTC timezones
1920
- reintroduce Python 3.9 entrypoints shim for compatibility
2021
- fix #1136: update customizing.md to fix missing import
2122

src/setuptools_scm/version.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,12 @@ def meta(
212212
branch: str | None = None,
213213
config: _config.Configuration,
214214
node_date: date | None = None,
215+
time: datetime | None = None,
215216
) -> ScmVersion:
216217
parsed_version = _parse_tag(tag, preformatted, config)
217218
log.info("version %s -> %s", tag, parsed_version)
218219
assert parsed_version is not None, f"Can't parse version {tag}"
219-
return ScmVersion(
220+
scm_version = ScmVersion(
220221
parsed_version,
221222
distance=distance,
222223
node=node,
@@ -226,6 +227,9 @@ def meta(
226227
config=config,
227228
node_date=node_date,
228229
)
230+
if time is not None:
231+
scm_version = dataclasses.replace(scm_version, time=time)
232+
return scm_version
229233

230234

231235
def guess_next_version(tag_version: ScmVersion) -> str:
@@ -365,19 +369,25 @@ def guess_next_date_ver(
365369
head_date = node_date or today
366370
# compute patch
367371
if match is None:
368-
tag_date = today
372+
# For legacy non-date tags, always use patch=0 (treat as "other day")
373+
# Use yesterday to ensure tag_date != head_date
374+
from datetime import timedelta
375+
376+
tag_date = head_date - timedelta(days=1)
369377
else:
370378
tag_date = (
371379
datetime.strptime(match.group("date"), date_fmt)
372380
.replace(tzinfo=timezone.utc)
373381
.date()
374382
)
375383
if tag_date == head_date:
376-
patch = "0" if match is None else (match.group("patch") or "0")
377-
patch = int(patch) + 1
384+
assert match is not None
385+
# Same day as existing date tag - increment patch
386+
patch = int(match.group("patch") or "0") + 1
378387
else:
388+
# Different day or legacy non-date tag - use patch 0
379389
if tag_date > head_date and match is not None:
380-
# warn on future times
390+
# warn on future times (only for actual date tags, not legacy)
381391
warnings.warn(
382392
f"your previous tag ({tag_date}) is ahead your node date ({head_date})"
383393
)

testing/test_version.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from dataclasses import replace
44
from datetime import date
5+
from datetime import datetime
56
from datetime import timedelta
7+
from datetime import timezone
68
from typing import Any
79

810
import pytest
@@ -269,11 +271,14 @@ def test_custom_version_schemes() -> None:
269271
assert custom_computed == no_guess_dev_version(version)
270272

271273

274+
# Fixed time for consistent test behavior across timezone boundaries
275+
# This prevents issue #687 where tests failed around midnight in non-UTC timezones
276+
_TEST_TIME = datetime(2023, 12, 15, 12, 0, 0, tzinfo=timezone.utc)
277+
278+
272279
def date_offset(base_date: date | None = None, days_offset: int = 0) -> date:
273280
if base_date is None:
274-
from setuptools_scm.version import _source_epoch_or_utc_now
275-
276-
base_date = _source_epoch_or_utc_now().date()
281+
base_date = _TEST_TIME.date()
277282
return base_date - timedelta(days=days_offset)
278283

279284

@@ -304,12 +309,23 @@ def date_to_str(
304309
id="leading 0s",
305310
),
306311
pytest.param(
307-
meta(date_to_str(days_offset=3), config=c_non_normalize, dirty=True),
312+
meta(
313+
date_to_str(days_offset=3),
314+
config=c_non_normalize,
315+
dirty=True,
316+
time=_TEST_TIME,
317+
),
308318
date_to_str() + ".0.dev0",
309319
id="dirty other day",
310320
),
311321
pytest.param(
312-
meta(date_to_str(), config=c_non_normalize, distance=2, branch="default"),
322+
meta(
323+
date_to_str(),
324+
config=c_non_normalize,
325+
distance=2,
326+
branch="default",
327+
time=_TEST_TIME,
328+
),
313329
date_to_str() + ".1.dev2",
314330
id="normal branch",
315331
),
@@ -382,8 +398,8 @@ def test_calver_by_date(version: ScmVersion, expected_next: str) -> None:
382398
[
383399
pytest.param(meta("1.0.0", config=c), "1.0.0", id="SemVer exact stays"),
384400
pytest.param(
385-
meta("1.0.0", config=c_non_normalize, dirty=True),
386-
"09.02.13.1.dev0",
401+
meta("1.0.0", config=c_non_normalize, dirty=True, time=_TEST_TIME),
402+
"23.12.15.0.dev0",
387403
id="SemVer dirty is replaced by date",
388404
marks=pytest.mark.filterwarnings("ignore:.*legacy version.*:UserWarning"),
389405
),
@@ -397,7 +413,12 @@ def test_calver_by_date_semver(version: ScmVersion, expected_next: str) -> None:
397413
def test_calver_by_date_future_warning() -> None:
398414
with pytest.warns(UserWarning, match="your previous tag*"):
399415
calver_by_date(
400-
meta(date_to_str(days_offset=-2), config=c_non_normalize, distance=2)
416+
meta(
417+
date_to_str(days_offset=-2),
418+
config=c_non_normalize,
419+
distance=2,
420+
time=_TEST_TIME,
421+
)
401422
)
402423

403424

0 commit comments

Comments
 (0)