Add form value persistence across cache revalidation re-renders 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
245 lines
6.4 KiB
Markdown
Executable File
245 lines
6.4 KiB
Markdown
Executable File
---
|
|
name: date-time
|
|
description: Working with dates and times in RSX using Rsx_Time and Rsx_Date classes. Use when formatting dates/times for display, handling user timezones, working with datetime columns, converting between formats, or displaying relative times like "2 hours ago".
|
|
---
|
|
|
|
# RSX Date & Time Handling
|
|
|
|
## Two Classes - Strict Separation
|
|
|
|
| Class | Purpose | Example |
|
|
|-------|---------| --------|
|
|
| `Rsx_Time` | Moments in time (with timezone) | `2025-12-24T15:30:00Z` |
|
|
| `Rsx_Date` | Calendar dates (no timezone) | `2025-12-24` |
|
|
|
|
**Critical**: Functions throw if wrong type passed (datetime to date function or vice versa). This is intentional - mixing types causes bugs.
|
|
|
|
---
|
|
|
|
## Strings, Not Objects
|
|
|
|
RSX uses ISO strings, not Carbon objects:
|
|
- **Dates**: `"2025-12-24"`
|
|
- **Datetimes**: `"2025-12-24T15:30:00Z"`
|
|
|
|
Same format in PHP, JavaScript, JSON, and database queries. No serialization surprises.
|
|
|
|
```php
|
|
$model->created_at // "2025-12-24T15:30:45.123Z" (string)
|
|
$model->due_date // "2025-12-24" (string)
|
|
Rsx_Time::now_iso() // "2025-12-24T15:30:45.123Z" (string)
|
|
```
|
|
|
|
Carbon is used internally for calculations, never exposed externally.
|
|
|
|
---
|
|
|
|
## Model Casts (Automatic)
|
|
|
|
`Rsx_Model_Abstract` auto-applies casts based on column type:
|
|
- DATE columns → `Rsx_Date_Cast`
|
|
- DATETIME columns → `Rsx_DateTime_Cast`
|
|
|
|
**Never define** `$casts` with `'date'`, `'datetime'`, or `'timestamp'` - these use Carbon and are blocked by `rsx:check`.
|
|
|
|
---
|
|
|
|
## Rsx_Time - Moments in Time
|
|
|
|
### PHP Usage
|
|
|
|
```php
|
|
use App\RSpade\Core\Time\Rsx_Time;
|
|
|
|
// Current time
|
|
Rsx_Time::now(); // Carbon (for calculations only)
|
|
Rsx_Time::now_iso(); // ISO 8601: 2025-12-24T15:30:00Z
|
|
|
|
// Formatting for display
|
|
Rsx_Time::format_datetime($datetime); // "Dec 24, 2025 3:30 PM"
|
|
Rsx_Time::format_datetime_with_tz($datetime); // "Dec 24, 2025 3:30 PM CST"
|
|
Rsx_Time::format_time($datetime); // "3:30 PM"
|
|
Rsx_Time::relative($datetime); // "2 hours ago"
|
|
|
|
// Database storage (always UTC)
|
|
Rsx_Time::to_database($datetime); // Converts to MySQL format
|
|
|
|
// Timezone
|
|
Rsx_Time::get_user_timezone(); // User's timezone or default
|
|
Rsx_Time::to_user_timezone($datetime); // Convert to user's timezone
|
|
```
|
|
|
|
### JavaScript Usage
|
|
|
|
```javascript
|
|
// Current time (synced with server)
|
|
Rsx_Time.now(); // Date object
|
|
Rsx_Time.now_iso(); // ISO string
|
|
|
|
// Formatting
|
|
Rsx_Time.format_datetime(datetime); // "Dec 24, 2025 3:30 PM"
|
|
Rsx_Time.format_datetime_with_tz(datetime); // "Dec 24, 2025 3:30 PM CST"
|
|
Rsx_Time.format_time(datetime); // "3:30 PM"
|
|
Rsx_Time.relative(datetime); // "2 hours ago", "in 3 days"
|
|
|
|
// Arithmetic
|
|
Rsx_Time.add(datetime, 3600); // Add seconds, returns ISO string
|
|
Rsx_Time.subtract(datetime, 3600); // Subtract seconds
|
|
```
|
|
|
|
---
|
|
|
|
## Rsx_Date - Calendar Dates
|
|
|
|
### PHP Usage
|
|
|
|
```php
|
|
use App\RSpade\Core\Time\Rsx_Date;
|
|
|
|
// Current date
|
|
Rsx_Date::today(); // "2025-12-24" (user's timezone)
|
|
|
|
// Formatting
|
|
Rsx_Date::format($date); // "Dec 24, 2025"
|
|
|
|
// Comparisons
|
|
Rsx_Date::is_today($date); // Boolean
|
|
Rsx_Date::is_past($date); // Boolean
|
|
Rsx_Date::is_future($date); // Boolean
|
|
Rsx_Date::diff_days($date1, $date2); // Days between
|
|
```
|
|
|
|
### JavaScript Usage
|
|
|
|
```javascript
|
|
// Current date
|
|
Rsx_Date.today(); // "2025-12-24"
|
|
|
|
// Formatting
|
|
Rsx_Date.format(date); // "Dec 24, 2025"
|
|
|
|
// Comparisons
|
|
Rsx_Date.is_today(date); // Boolean
|
|
Rsx_Date.is_past(date); // Boolean
|
|
```
|
|
|
|
---
|
|
|
|
## Live Countdown/Countup (JavaScript)
|
|
|
|
For real-time updating displays:
|
|
|
|
```javascript
|
|
// Countdown to future time
|
|
const ctrl = Rsx_Time.countdown(this.$sid('timer'), deadline, {
|
|
short: true, // "2h 30m" vs "2 hours and 30 minutes"
|
|
on_complete: () => this.reload()
|
|
});
|
|
|
|
// Stop countdown when leaving page
|
|
this.on_stop(() => ctrl.stop());
|
|
|
|
// Countup from past time (elapsed time)
|
|
Rsx_Time.countup(this.$sid('elapsed'), started_at, { short: true });
|
|
```
|
|
|
|
---
|
|
|
|
## Server Time Sync
|
|
|
|
Client time syncs automatically via `rsxapp` data on page load and AJAX responses. No manual sync required.
|
|
|
|
```javascript
|
|
// Client always has accurate server time
|
|
const server_now = Rsx_Time.now(); // Synced with server, corrects for clock skew
|
|
```
|
|
|
|
---
|
|
|
|
## User Timezone
|
|
|
|
Stored in `login_users.timezone` (IANA format, e.g., `America/Chicago`).
|
|
|
|
Falls back to `config('rsx.datetime.default_timezone')`.
|
|
|
|
```php
|
|
// Get user's timezone
|
|
$tz = Rsx_Time::get_user_timezone();
|
|
|
|
// All Rsx_Time methods automatically use user's timezone for display
|
|
```
|
|
|
|
---
|
|
|
|
## Component Expectations
|
|
|
|
### Date_Picker Component
|
|
|
|
- `val()` returns `"YYYY-MM-DD"` or `null`
|
|
- `val(value)` accepts `"YYYY-MM-DD"` or `null`
|
|
- **Throws** if passed datetime format
|
|
- Display shows localized format (e.g., "Dec 24, 2025")
|
|
|
|
### Datetime_Picker Component
|
|
|
|
- `val()` returns ISO 8601 string or `null`
|
|
- `val(value)` accepts ISO 8601 string or `null`
|
|
- **Throws** if passed date-only format
|
|
- Display shows localized time in user's timezone
|
|
|
|
---
|
|
|
|
## Common Patterns
|
|
|
|
### Display in Template
|
|
|
|
```jqhtml
|
|
<span><%= Rsx_Time.format_datetime(this.data.record.created_at) %></span>
|
|
<span><%= Rsx_Time.relative(this.data.record.updated_at) %></span>
|
|
<span><%= Rsx_Date.format(this.data.record.due_date) %></span>
|
|
```
|
|
|
|
### Conditional Display
|
|
|
|
```jqhtml
|
|
<% if (Rsx_Date.is_past(this.data.task.due_date)) { %>
|
|
<span class="text-danger">Overdue</span>
|
|
<% } %>
|
|
```
|
|
|
|
### Save to Database
|
|
|
|
```php
|
|
// Datetime - store in UTC
|
|
$record->scheduled_at = Rsx_Time::to_database($params['scheduled_at']);
|
|
|
|
// Date - store as-is
|
|
$record->due_date = $params['due_date']; // Already "YYYY-MM-DD"
|
|
```
|
|
|
|
### Parse User Input
|
|
|
|
```php
|
|
// If user enters a datetime string
|
|
$datetime = Rsx_Time::parse($params['datetime']); // Returns Carbon
|
|
$iso = Rsx_Time::to_iso($datetime); // Convert back to string
|
|
|
|
// If user enters a date string
|
|
$date = Rsx_Date::parse($params['date']); // Returns "YYYY-MM-DD"
|
|
```
|
|
|
|
---
|
|
|
|
## Key Rules
|
|
|
|
1. **Never use Carbon directly** - RSX uses string-based dates
|
|
2. **Never use PHP's date()** - Use Rsx_Time/Rsx_Date
|
|
3. **Store datetimes in UTC** - Use `Rsx_Time::to_database()`
|
|
4. **Display in user's timezone** - Automatic via Rsx_Time format methods
|
|
5. **Dates have no timezone** - Use Rsx_Date for calendar dates
|
|
6. **Wrong types throw** - Date functions reject datetimes and vice versa
|
|
|
|
## More Information
|
|
|
|
Details: `php artisan rsx:man time`
|