# Date Handling Philosophy

## Core Principle

**All calendar dates in ThrillWiki are timezone-agnostic and stored as DATE (not TIMESTAMP) in the database.**

When a user selects "January 1, 2024," that date should be stored as `2024-01-01` regardless of the user's timezone. We never use UTC conversion for calendar date fields.

## Why This Matters

### The Problem with `.toISOString()`

```typescript
// ❌ WRONG - Creates timezone shifts
const date = new Date('2024-01-01T23:00:00-08:00'); // User in UTC-8 selects Jan 1, 2024 11:00 PM
date.toISOString().split('T')[0]; // Returns "2024-01-02" ❌ (shifted to UTC)
```

When a user in UTC-8 (Pacific Time) selects "January 1, 2024" at 11:00 PM local time:
- Their local time: `2024-01-01 23:00 PST`
- UTC time: `2024-01-02 07:00 UTC`
- `.toISOString()` returns: `"2024-01-02T07:00:00.000Z"`
- `.split('T')[0]` extracts: `"2024-01-02"` ❌

**Result**: User selected Jan 1, but we stored Jan 2!

### The Solution: `toDateOnly()`

```typescript
// ✅ CORRECT - Preserves local date
import { toDateOnly } from '@/lib/dateUtils';

const date = new Date('2024-01-01T23:00:00-08:00');
toDateOnly(date); // Returns "2024-01-01" ✅ (local timezone preserved)
```

## Database Schema

All calendar date columns use the `DATE` type (not `TIMESTAMP`):

```sql
-- Parks
opening_date DATE
closing_date DATE

-- Rides
opening_date DATE
closing_date DATE

-- Companies
founded_date DATE

-- Photos
date_taken DATE

-- Reviews
visit_date DATE
```

## When to Use What

### Use `DATE` and `toDateOnly()` for:
- Opening/closing dates (parks, rides)
- Founded dates (companies)
- Birth dates (user profiles)
- Visit dates (reviews)
- Photo capture dates
- Any calendar date where the specific time doesn't matter

### Use `TIMESTAMP` and `.toISOString()` for:
- `created_at` / `updated_at` timestamps
- Submission timestamps
- Upload timestamps
- Any moment in time where the exact second matters

## API Reference

### Core Functions

#### `toDateOnly(date: Date): string`
Converts a Date object to YYYY-MM-DD string in LOCAL timezone.

```typescript
const date = new Date('2024-01-01T23:00:00-08:00');
toDateOnly(date); // "2024-01-01"
```

#### `parseDateOnly(dateString: string): Date`
Parses a YYYY-MM-DD string to a Date object at midnight local time.

```typescript
parseDateOnly('2024-01-01'); // Date object for Jan 1, 2024 00:00:00 local
```

#### `formatDateDisplay(dateString: string, precision: DatePrecision): string`
Formats a date string for display based on precision.

```typescript
formatDateDisplay('2024-01-01', 'year');  // "2024"
formatDateDisplay('2024-01-01', 'month'); // "January 2024"
formatDateDisplay('2024-01-01', 'day');   // "January 1, 2024"
```

#### `getCurrentDateLocal(): string`
Gets current date as YYYY-MM-DD string in local timezone.

```typescript
getCurrentDateLocal(); // "2024-01-15"
```

### Validation Functions

#### `isValidDateString(dateString: string): boolean`
Validates YYYY-MM-DD format.

```typescript
isValidDateString('2024-01-01'); // true
isValidDateString('01/01/2024'); // false
```

#### `isDateInRange(dateString: string, minYear?: number, maxYear?: number): boolean`
Checks if a date is within a valid range.

```typescript
isDateInRange('2024-01-01', 1800, 2030); // true
isDateInRange('1799-01-01', 1800, 2030); // false
```

## Form Implementation Pattern

### Correct Implementation

```typescript
import { toDateOnly } from '@/lib/dateUtils';

<FlexibleDateInput
  value={watch('opening_date') ? new Date(watch('opening_date')) : undefined}
  precision={(watch('opening_date_precision') as DatePrecision) || 'day'}
  onChange={(date, precision) => {
    setValue('opening_date', date ? toDateOnly(date) : undefined);
    setValue('opening_date_precision', precision);
  }}
  label="Opening Date"
/>
```

### Incorrect Implementation

```typescript
// ❌ DON'T DO THIS
<FlexibleDateInput
  onChange={(date, precision) => {
    setValue('opening_date', date ? date.toISOString().split('T')[0] : undefined); // ❌
  }}
/>
```

## Date Precision Handling

ThrillWiki supports three precision levels for historical accuracy:

### Year Precision
Used when only the year is known (e.g., "Founded in 1972")
- Stored as: `1972-01-01`
- Precision field: `year`
- Displayed as: `"1972"`

### Month Precision
Used when month and year are known (e.g., "Opened June 1985")
- Stored as: `1985-06-01`
- Precision field: `month`
- Displayed as: `"June 1985"`

### Day Precision
Used when exact date is known (e.g., "Opened July 4, 1976")
- Stored as: `1976-07-04`
- Precision field: `day`
- Displayed as: `"July 4, 1976"`

## Common Mistakes and Fixes

### Mistake 1: Using `.toISOString()` for Dates

```typescript
// ❌ WRONG
date.toISOString().split('T')[0]

// ✅ CORRECT
toDateOnly(date)
```

### Mistake 2: Storing Time Components for Calendar Dates

```typescript
// ❌ WRONG - Don't store time for calendar dates
created_at TIMESTAMP // OK for creation timestamp
opening_date TIMESTAMP // ❌ Wrong! Use DATE

// ✅ CORRECT
created_at TIMESTAMP // OK for creation timestamp
opening_date DATE // ✅ Correct for calendar date
```

### Mistake 3: Not Handling Precision

```typescript
// ❌ WRONG - Always shows full date
<span>{format(new Date(park.opening_date), 'PPP')}</span>

// ✅ CORRECT - Respects precision
<FlexibleDateDisplay 
  date={park.opening_date} 
  precision={park.opening_date_precision}
/>
```

## Testing Checklist

When implementing date handling, test these scenarios:

### Timezone Edge Cases
- [ ] User in UTC-12 selects December 31, 2024 11:00 PM → Stores as `2024-12-31`
- [ ] User in UTC+14 selects January 1, 2024 1:00 AM → Stores as `2024-01-01`
- [ ] User in UTC-8 selects date → No timezone shift occurs

### Precision Handling
- [ ] Year precision stores as YYYY-01-01
- [ ] Month precision stores as YYYY-MM-01
- [ ] Day precision stores as YYYY-MM-DD
- [ ] Display respects precision setting

### Date Comparisons
- [ ] Closing date validation: closing > opening
- [ ] Future date restrictions work correctly
- [ ] Historical date limits (nothing before 1800) enforced

### Form Persistence
- [ ] Edit park with opening_date → Date doesn't shift when re-opening form
- [ ] Moderation queue shows correct dates in previews
- [ ] Date survives form validation and submission

## Migration Notes

### Updating Existing Code

1. **Import the utility**:
   ```typescript
   import { toDateOnly } from '@/lib/dateUtils';
   ```

2. **Replace `.toISOString().split('T')[0]`**:
   ```typescript
   // Before
   setValue('date', date ? date.toISOString().split('T')[0] : undefined);
   
   // After
   setValue('date', date ? toDateOnly(date) : undefined);
   ```

3. **Update form components** (See files updated in Phase 2 of implementation)

### Database Schema

No database migrations needed - all date columns are already using the `DATE` type.

## Related Files

- **Core Utilities**: `src/lib/dateUtils.ts`
- **Form Components**: 
  - `src/components/admin/ParkForm.tsx`
  - `src/components/admin/RideForm.tsx`
  - `src/components/admin/ManufacturerForm.tsx`
  - `src/components/reviews/ReviewForm.tsx`
- **UI Components**:
  - `src/components/ui/flexible-date-input.tsx`
  - `src/components/ui/flexible-date-display.tsx`
- **Validation**: `src/lib/entityValidationSchemas.ts`
- **Server Validation**: `supabase/functions/process-selective-approval/validation.ts`

## Support

If you encounter date-related bugs:
1. Check the browser console for the actual date values being submitted
2. Verify the database column type is `DATE` (not `TIMESTAMP`)
3. Ensure `toDateOnly()` is being used instead of `.toISOString().split('T')[0]`
4. Test in different timezones (use browser DevTools to simulate)
