Skip to content

Commit 2b921e7

Browse files
committed
Let hosts pin the clock formulas read
FormulaClock.pinned sets the instant now() and today() (and relative times) take, so a host can show an app at a given moment: previews of a given day and reproducible screenshots of date-dependent screens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3B1XCZZ7QGvZLUGgSG5H4
1 parent 784ee82 commit 2b921e7

3 files changed

Lines changed: 41 additions & 3 deletions

File tree

‎lib/src/transformers/utils/property_value_delegate.dart‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1081,7 +1081,7 @@ String formatDate(DateTime date) {
10811081
/// Formats [date] relative to now: `Just now`, `5 min ago`, `2 hours ago`,
10821082
/// `3 days ago`, then [formatDate] after a week.
10831083
String formatTimeAgo(DateTime date, {DateTime? now}) {
1084-
final Duration diff = (now ?? DateTime.now()).difference(date);
1084+
final Duration diff = (now ?? FormulaClock.now()).difference(date);
10851085
if (diff.inMinutes < 1) return 'Just now';
10861086
if (diff.inHours < 1) return '${diff.inMinutes} min ago';
10871087
if (diff.inDays < 1) {

‎lib/src/utils/formula.dart‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -996,12 +996,25 @@ Duration? _offset(Object? value) {
996996
return Duration(minutes: m[1] == '-' ? -minutes : minutes);
997997
}
998998

999+
/// The instant formulas take as now (`now()`, `today()`).
1000+
///
1001+
/// A host can pin it to show an app at a given moment, for previews and
1002+
/// reproducible screenshots of date-dependent screens (the Codelessly site
1003+
/// pins it from a `?now=` URL parameter).
1004+
abstract final class FormulaClock {
1005+
/// The pinned instant, or null for the device clock.
1006+
static DateTime? pinned;
1007+
1008+
/// Now: [pinned], or the device clock.
1009+
static DateTime now() => pinned ?? DateTime.now();
1010+
}
1011+
9991012
/// The wall-clock time now at the UTC [offset] (see [_offset]), or local
10001013
/// time without one. Returned without a zone, like other formula dates.
10011014
DateTime _nowAt(Object? offset) {
10021015
final Duration? delta = _offset(offset);
1003-
if (delta == null) return DateTime.now();
1004-
final DateTime utc = DateTime.now().toUtc().add(delta);
1016+
if (delta == null) return FormulaClock.now().toLocal();
1017+
final DateTime utc = FormulaClock.now().toUtc().add(delta);
10051018
return DateTime(
10061019
utc.year,
10071020
utc.month,

‎test/formula_clock_test.dart‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import 'package:codelessly_sdk/codelessly_sdk.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
4+
void main() {
5+
tearDown(() => FormulaClock.pinned = null);
6+
7+
String eval(String formula) => FormulaEvaluator.stringify(
8+
FormulaEvaluator.evaluate(formula, (path) => null),
9+
);
10+
11+
test('a pinned clock is what now() and today() read', () {
12+
FormulaClock.pinned = DateTime.parse('2026-10-22T13:20:00-07:00');
13+
expect(
14+
eval("formatDate(now('-07:00'), 'EEE, MMM d · h:mm a')"),
15+
'Thu, Oct 22 · 1:20 PM',
16+
);
17+
expect(eval("formatDate(today('-07:00'), 'yyyy-MM-dd')"), '2026-10-22');
18+
expect(eval("formatDate(now('Z'), 'HH:mm')"), '20:20');
19+
});
20+
21+
test('without a pin, now() is the device clock', () {
22+
final String year = '${DateTime.now().year}';
23+
expect(eval("formatDate(now(), 'yyyy')"), year);
24+
});
25+
}

0 commit comments

Comments
 (0)