# Timestamp Management Strategy

## Problem Statement

Firebase provides two different Timestamp implementations across its SDKs:

1. **Client-side Timestamp**: `import { Timestamp } from 'firebase/firestore'`

   - Used in client applications (web, mobile)
   - Lives in the `firebase/firestore` package

2. **Admin-side Timestamp**: `import * as admin from 'firebase-admin'; admin.firestore.Timestamp`
   - Used in server-side code (Cloud Functions)
   - Lives in the `firebase-admin` package

These types are structurally similar but are different JavaScript classes, causing type conflicts when:

- Data with client Timestamps is processed in admin code
- Data with admin Timestamps is sent to client code
- Shared type definitions across client and admin code

## Solution: TimestampUtils

We've created a central `TimestampUtils` class providing:

1. Conversion utilities between admin and client Timestamps
2. Best practices for timestamp handling
3. Consistent patterns for timestamp operations

## Usage Guidelines

### 1. Type Definitions

All shared type definitions (in `src/types/*`) should:

- Import Timestamp from client SDK: `import { Timestamp } from 'firebase/firestore'`
- Define fields using this client Timestamp: `timestamp: Timestamp`

This ensures types are consumable by both client and admin code.

### 2. Admin Code (Cloud Functions)

When writing admin code that:

- **Reads data from Firestore**: No conversion needed, as Firestore returns admin Timestamps
- **Processes timestamps**: Use standard admin timestamp methods
- **Writes data to shared types**: Convert admin → client using `TimestampUtils.adminToClient()`
- **Creates server timestamps**: For fields created with `serverTimestamp()`, use Firebase Admin's version: `admin.firestore.FieldValue.serverTimestamp()`

Example:

```typescript
import * as admin from "firebase-admin";
import { TimestampUtils } from "../../utils/TimestampUtils";
import { YourSharedType } from "../../types/shared";

// In your admin service
async function processAndUpdateData() {
  const adminTsNow = admin.firestore.Timestamp.now();

  // When populating a shared type that clients will use
  const data: YourSharedType = {
    // Convert admin timestamp to client timestamp
    createdAt: TimestampUtils.adminToClient(adminTsNow),

    // For server timestamps, use admin version
    updatedAt: admin.firestore.FieldValue.serverTimestamp(),

    // Other fields...
  };

  // When you need to process objects with nested timestamps
  const objectWithNestedTimestamps = {
    // Complex data from another source
  };

  // Convert all timestamps in the object from admin to client
  const clientCompatible = TimestampUtils.convertObjectTimestampsAdminToClient(
    objectWithNestedTimestamps
  );
}
```

### 3. Client Code (Web/Mobile Apps)

Client code should:

- Use `firebase/firestore` Timestamp when needed
- No conversion is necessary for data returned by `getDocs()` or similar client SDK methods
- For new timestamps, use `Timestamp.now()` or `Timestamp.fromDate()`

### 4. Utility Functions

The `TimestampUtils` class provides these key functions:

- `adminToClient(adminTimestamp)`: Converts admin → client Timestamp
- `clientToAdmin(clientTimestamp)`: Converts client → admin Timestamp
- `nowAsClient()`: Creates current timestamp as client Timestamp
- `dateToClientTimestamp(date)`: Converts Date → client Timestamp
- `dateToAdminTimestamp(date)`: Converts Date → admin Timestamp
- `convertObjectTimestampsAdminToClient(obj)`: Deep conversion of all timestamps in an object from admin → client
- `convertObjectTimestampsClientToAdmin(obj)`: Deep conversion of all timestamps in an object from client → admin

## Common Patterns

### 1. Creating New Documents

```typescript
// In admin code
const adminTimestamp = admin.firestore.Timestamp.now();
const clientCompatibleTimestamp = TimestampUtils.adminToClient(adminTimestamp);

const newDocument = {
  id: "doc123",
  createdAt: clientCompatibleTimestamp,
  // For server-managed timestamps, use serverTimestamp()
  updatedAt: admin.firestore.FieldValue.serverTimestamp(),
};

await docRef.set(newDocument);
```

### 2. Updating Fields in a Document

```typescript
// In admin code
await docRef.update({
  someField: "new value",
  // For immediate timestamp, convert admin to client
  processedAt: TimestampUtils.adminToClient(admin.firestore.Timestamp.now()),
  // For server-managed timestamps, use serverTimestamp()
  updatedAt: admin.firestore.FieldValue.serverTimestamp(),
});
```

### 3. Processing Calendar Events

Calendar events and date ranges should be consistently handled:

```typescript
// Calendar event time conversion
const firestoreCompatibleEventTime = {
  start: {
    seconds: newEventTime.start.seconds,
    nanoseconds: newEventTime.start.nanoseconds,
  },
  end: {
    seconds: newEventTime.end.seconds,
    nanoseconds: newEventTime.end.nanoseconds,
  },
};
```

## Edge Cases and Limitations

1. **null/undefined Handling**: All utility methods handle null safely
2. **Serialization**: If serializing for JSON, convert to ISO strings first
3. **Performance**: The conversion is lightweight, but for large batches, process efficiently

## Migration Strategy

1. Identify timestamp usage in your code using static analysis or grep
2. Replace direct timestamp conversions with TimestampUtils methods
3. Update methods like:
   - `adminTimestampToClientTimestamp` → `TimestampUtils.adminToClient`
   - Direct creation of client timestamps → `TimestampUtils.nowAsClient`
4. Test thoroughly after each change

## Conclusion

Following this strategy will:

- Eliminate type errors between admin and client code
- Provide consistent timestamp handling throughout the codebase
- Minimize conversion bugs by centralizing conversion logic
- Make it easier to onboard new developers to the codebase
