Files
rspade_system/app/RSpade/man/time.txt
root 1b57ec2785 Add datetime system (Rsx_Time/Rsx_Date) and .expect file documentation system
Tighten CLAUDE.dist.md for LLM audience - 15% size reduction
Add Repeater_Simple_Input component for managing lists of simple values
Add Polymorphic_Field_Helper for JSON-encoded polymorphic form fields
Fix incorrect data-sid selector in route-debug help example
Fix Form_Utils to use component.$sid() instead of data-sid selector
Add response helper functions and use _message as reserved metadata key

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-24 21:47:53 +00:00

384 lines
13 KiB
Plaintext
Executable File

TIME(7) RSpade Framework Manual TIME(7)
NAME
Rsx_Time, Rsx_Date - Date and datetime handling for RSpade applications
SYNOPSIS
PHP Datetime:
use App\RSpade\Core\Time\Rsx_Time;
$now = Rsx_Time::now();
$iso = Rsx_Time::to_iso($datetime);
$localized = Rsx_Time::to_user_timezone($datetime);
$formatted = Rsx_Time::format_datetime_with_tz($datetime);
$relative = Rsx_Time::relative($datetime);
PHP Date:
use App\RSpade\Core\Time\Rsx_Date;
$today = Rsx_Date::today();
$formatted = Rsx_Date::format($date);
$is_past = Rsx_Date::is_past($date);
JavaScript Datetime:
const now = Rsx_Time.now();
const iso = Rsx_Time.to_iso(datetime);
const formatted = Rsx_Time.format_datetime_with_tz(datetime);
const relative = Rsx_Time.relative(datetime);
JavaScript Date:
const today = Rsx_Date.today();
const formatted = Rsx_Date.format(date);
const is_past = Rsx_Date.is_past(date);
DESCRIPTION
RSpade provides two separate classes for handling temporal values:
Rsx_Time - Datetimes (moments in time)
Represents specific moments. Always has time component. Timezone-aware.
Stored in UTC, displayed in user's timezone.
Format: ISO 8601 "2024-12-24T15:30:45.123Z"
Rsx_Date - Dates (calendar dates)
Represents calendar dates without time. Timezone-agnostic.
"December 24, 2025" is the same day everywhere.
Format: Always "YYYY-MM-DD" (e.g., "2024-12-24")
CRITICAL: Type Separation
Date functions THROW if passed a datetime.
Datetime functions THROW if passed a date-only string.
This is intentional. Mixing dates and datetimes causes bugs:
- "2024-12-24" as datetime would become midnight UTC, wrong in other timezones
- "2024-12-24T00:00:00Z" as date loses the time information
Examples of errors:
Rsx_Time::parse('2024-12-24')
// THROWS: "Use Rsx_Date::parse() for dates without time components"
Rsx_Date::parse('2024-12-24T15:30:00Z')
// THROWS: "Use Rsx_Time::parse() for datetimes with time components"
DATE VS DATETIME
Use DATE when:
- Due dates (the task is due on this calendar day)
- Birth dates (born on this day, no time)
- Anniversaries, holidays
- Any value where time of day is irrelevant
Use DATETIME when:
- Event start/end times
- Created/updated timestamps
- Scheduled appointments
- Any value where the exact moment matters
Database columns:
DATE - For date-only fields
DATETIME(3) - For datetime fields (millisecond precision)
RSX_DATE CLASS
All functions work with "YYYY-MM-DD" format strings.
Parsing & Validation
parse($input)
Returns "YYYY-MM-DD" string or null.
THROWS on datetime input.
is_date($input)
Returns true if input is valid date string.
Current Date
today()
Returns today's date as "YYYY-MM-DD" in user's timezone.
PHP: $today = Rsx_Date::today();
JS: const today = Rsx_Date.today();
Formatting
format($date)
Display format: "Dec 24, 2025"
format_iso($date)
Ensures "YYYY-MM-DD" format.
Comparison
is_today($date) True if date is today
is_past($date) True if date is before today
is_future($date) True if date is after today
diff_days($d1, $d2) Days between dates (positive if d2 > d1)
Database
to_database($date)
Returns "YYYY-MM-DD" for database storage (same as ISO format).
RSX_TIME CLASS
All functions work with ISO 8601 datetime strings or Carbon/Date objects.
Parsing & Validation
parse($input)
Returns Carbon (PHP) or Date (JS) in UTC.
THROWS on date-only string input.
is_datetime($input)
Returns true if input is valid datetime (not date-only).
Current Time
now() Returns current time as Carbon/Date (UTC)
now_iso() Returns current time as ISO 8601 string
now_ms() Returns current time as Unix milliseconds
Timezone Handling
get_user_timezone()
Returns user's IANA timezone (e.g., "America/Chicago").
to_timezone($time, $tz)
Convert datetime to specific timezone.
to_user_timezone($time)
Convert datetime to user's timezone.
get_timezone_abbr($time, $tz)
Get timezone abbreviation (e.g., "CST", "CDT").
DST-aware based on the actual date.
Serialization
to_iso($time)
Returns ISO 8601 UTC string: "2024-12-24T15:30:45.123Z"
to_ms($time)
Returns Unix timestamp in milliseconds.
to_database($time) (PHP only)
Returns MySQL format: "2024-12-24 15:30:45.123"
Formatting
format_time($time, $tz) "3:30 PM"
format_datetime($time, $tz) "Dec 24, 2024, 3:30 PM"
format_datetime_with_tz($time) "Dec 24, 2024, 3:30 PM CST"
format($time, $format, $tz) (PHP only) Custom PHP date format
Duration & Relative
diff_seconds($start, $end)
Seconds between two datetimes.
seconds_until($time) (JS only)
Seconds until future time.
seconds_since($time) (JS only)
Seconds since past time.
duration_to_human($seconds, $short)
Long: "2 hours and 30 minutes"
Short: "2h 30m"
relative($time)
"2 hours ago", "in 3 days", "just now"
Arithmetic
add($time, $seconds)
Add seconds to time.
subtract($time, $seconds)
Subtract seconds from time.
Comparison
is_past($time) True if datetime is in the past
is_future($time) True if datetime is in the future
is_today($time) True if datetime is today (in user's timezone)
Live Updates (JavaScript only)
countdown($element, target_time, options)
Live countdown to future time. Updates every second.
const ctrl = Rsx_Time.countdown($('#timer'), deadline, {
short: true,
on_complete: () => alert('Done!')
});
ctrl.stop(); // Stop the countdown
countup($element, start_time, options)
Live elapsed time since past time.
Rsx_Time.countup($('.elapsed'), started_at, { short: true });
TIMEZONE INITIALIZATION
User timezone is resolved in order:
1. login_users.timezone (user's preference)
2. config('rsx.datetime.default_timezone')
3. 'America/Chicago' (hardcoded fallback)
Page Load
On page load, window.rsxapp includes:
server_time - ISO 8601 UTC timestamp from server
user_timezone - IANA timezone identifier
Rsx_Time._on_framework_core_init() reads these automatically.
AJAX Sync
Every AJAX response includes _server_time and _user_timezone.
Rsx_Time.sync_from_ajax() is called automatically to:
- Update user timezone if changed
- Sync server time offset on first request or timezone change
This corrects for client clock skew. Rsx_Time.now() returns
server-adjusted time.
COMPONENT EXPECTATIONS
Date Picker Components
val() returns "YYYY-MM-DD" or null
val(value) accepts "YYYY-MM-DD" or null
THROWS if passed datetime format
Internal display shows localized format (e.g., "Dec 24, 2025")
class Date_Picker extends Form_Input_Abstract {
val(value) {
if (arguments.length === 0) {
return this.state.value; // "YYYY-MM-DD" or null
}
if (value != null && !Rsx_Date.is_date(value)) {
throw new Error('Date_Picker requires YYYY-MM-DD format');
}
this.state.value = value;
this._update_display();
}
}
Datetime Picker Components
val() returns ISO 8601 string or null
val(value) accepts ISO 8601 string or null
THROWS if passed date-only format
Internal display shows localized time in user's timezone
class Datetime_Picker extends Form_Input_Abstract {
val(value) {
if (arguments.length === 0) {
return this.state.value; // ISO 8601 or null
}
if (value != null && !Rsx_Time.is_datetime(value)) {
throw new Error('Datetime_Picker requires ISO 8601 format');
}
this.state.value = value;
this._update_display();
}
}
DATA FLOW
Date Field (e.g., due_date)
Database: DATE column, value "2025-12-24"
|
PHP Model: $task->due_date = "2025-12-24" (string)
|
JSON Response: {"due_date": "2025-12-24"}
|
JS Model: task.due_date = "2025-12-24" (string)
|
Date Picker: val() = "2025-12-24", display "Dec 24, 2025"
|
Form Submit: {"due_date": "2025-12-24"}
|
PHP Controller: Rsx_Date::parse($params['due_date'])
|
Database: "2025-12-24"
Datetime Field (e.g., scheduled_at)
Database: DATETIME(3), value "2025-12-24 15:30:45.123" (UTC)
|
PHP Model: $event->scheduled_at = Carbon instance (UTC)
|
JSON Serialize: {"scheduled_at": "2025-12-24T15:30:45.123Z"}
|
JS Model: event.scheduled_at = "2025-12-24T15:30:45.123Z" (string)
|
Datetime Picker: val() = ISO string, display "Dec 24, 9:30 AM CST"
|
User edits to "Dec 24, 10:00 AM CST"
Picker converts to UTC: "2025-12-24T16:00:00.000Z"
|
Form Submit: {"scheduled_at": "2025-12-24T16:00:00.000Z"}
|
PHP Controller: Rsx_Time::parse($params['scheduled_at']) -> Carbon
|
Database: "2025-12-24 16:00:00.000"
EXAMPLES
PHP - Date handling:
$due_date = Rsx_Date::parse($params['due_date']); // "2025-12-24"
if (Rsx_Date::is_past($due_date)) {
return response_error(Ajax::ERROR_VALIDATION, [
'due_date' => 'Due date cannot be in the past'
]);
}
$task->due_date = Rsx_Date::to_database($due_date);
$task->save();
PHP - Datetime handling:
$event_time = Rsx_Time::parse($params['event_time']);
return [
'id' => $event->id,
'event_time' => Rsx_Time::to_iso($event_time),
'formatted' => Rsx_Time::format_datetime_with_tz($event_time),
'is_past' => Rsx_Time::is_past($event_time),
];
JavaScript - Date display:
const due = this.data.task.due_date; // "2025-12-24"
this.$sid('due').text(Rsx_Date.format(due)); // "Dec 24, 2025"
if (Rsx_Date.is_past(due)) {
this.$sid('due').addClass('text-danger');
}
JavaScript - Datetime display with countdown:
const event_time = this.data.event.scheduled_at; // ISO string
this.$sid('time').text(Rsx_Time.format_datetime(event_time));
this.$sid('relative').text(Rsx_Time.relative(event_time));
if (Rsx_Time.is_future(event_time)) {
this._countdown = Rsx_Time.countdown(
this.$sid('countdown'),
event_time,
{ short: true, on_complete: () => this.reload() }
);
}
ERROR HANDLING
Wrong type errors are thrown immediately:
// PHP
try {
Rsx_Time::parse('2025-12-24');
} catch (\InvalidArgumentException $e) {
// "Rsx_Time::parse() received date-only string..."
}
// JavaScript
try {
Rsx_Time.parse('2025-12-24');
} catch (e) {
// "Rsx_Time.parse() received date-only string..."
}
These errors indicate a programming mistake - the wrong function is
being used. Fix the code rather than catching the exception.
CONFIGURATION
system/config/rsx.php:
'datetime' => [
'default_timezone' => env('RSX_DEFAULT_TIMEZONE', 'America/Chicago'),
],
User timezone stored in login_users.timezone column.
SEE ALSO
Reference document: /var/www/html/date_vs_datetime_refactor.md
AUTHOR
RSpade Framework
RSpade December 2025 TIME(7)