Skip to main content

Date and Time Handling

Ormed provides robust support for date and time handling, integrating the standard Dart DateTime with the Carbonized library for a fluent, Laravel-like experience.

info

The carbonized package is automatically exported by package:ormed/ormed.dart, so you don't need to add it to your pubspec.yaml or import it separately.

DateTime vs Carbon​

While you can use standard DateTime objects for all your model fields, Ormed's built-in features like Timestamps and Soft Deletes return CarbonInterface instances.

Why Carbon?​

Carbon extends DateTime functionality with:

  • Fluent manipulation: date.addDays(5).subHours(2)
  • Human-readable differences: date.diffForHumans()
  • Easy formatting: date.format('Y-m-d')
  • Timezone management: date.toUtc(), date.setTimezone('America/New_York')

Mutable vs Immutable Carbon​

:::warning Important Carbon provides two variants with very different behavior. Understanding this distinction is critical to avoid subtle bugs. :::

Carbon (Mutable)​

The Carbon class is mutable - methods like subDay(), addDays(), etc. modify the instance in-place and return this:

// ⚠️ WARNING: Carbon (mutable) mutates in-place!
void mutableCarbonPitfall() {
final date = Carbon.parse('2024-12-21');
print(date.toDateString()); // 2024-12-21

final yesterday = date.subDay(); // Mutates `date` in-place!
print(date.toDateString()); // 2024-12-20 (unexpected!)
print(identical(date, yesterday)); // true - same object!
}

CarbonImmutable (Safe)​

The CarbonImmutable class is immutable - methods return new instances, leaving the original unchanged:

// ✅ CarbonImmutable is safe - methods return new instances
void immutableCarbonSafe() {
final date = CarbonImmutable.parse('2024-12-21');
print(date.toDateString()); // 2024-12-21

final yesterday = date.subDay(); // Returns NEW instance
print(date.toDateString()); // 2024-12-21 (unchanged!)
print(yesterday.toDateString()); // 2024-12-20
}

Ormed Returns Immutable Timestamps​

All Ormed timestamp getters (createdAt, updatedAt, deletedAt) return immutable Carbon instances. This design choice prevents accidental mutation of model state:

// Ormed timestamp getters return immutable Carbon instances
void ormedTimestampsAreImmutable() {
// When you access createdAt/updatedAt/deletedAt from a model,
// Ormed returns immutable instances so you can safely chain methods:

// final user = await repo.find(1);
// final createdAt = user.createdAt; // CarbonImmutable
//
// // Safe to chain - original is never mutated
// final yesterday = createdAt.subDay();
// final isRecent = createdAt.isAfter(Carbon.now().subDays(7));
//
// print(createdAt); // Still the original value ✓
}

Default to immutable date handling in app code; convert to mutable only in narrow, controlled utility flows.

Converting Between Mutable and Immutable​

// Converting between mutable and immutable
void convertingCarbon() {
// Mutable → Immutable
final mutable = Carbon.now();
final immutable = mutable.toImmutable();

// Immutable → Mutable (creates a copy)
final backToMutable = immutable.toMutable();

// Create a copy of mutable Carbon
final copy = mutable.copy();
copy.subDay(); // Only affects the copy
}

Configuration​

You can configure global Carbon settings using CarbonConfig. This is typically done during application bootstrap.

import 'package:ormed/ormed.dart';

void main() async {
// Basic configuration
CarbonConfig.configure(
defaultTimezone: 'UTC',
defaultLocale: 'en_US',
);

// For named timezone support (e.g., 'Europe/London'),
// you must initialize TimeMachine data:
await CarbonConfig.configureWithTimeMachine(
defaultTimezone: 'America/New_York',
);
}

Timestamps and Soft Deletes​

When using the Timestamps or SoftDeletes mixins, the generated fields (createdAt, updatedAt, deletedAt) provide a flexible API.

Getters​

The getters return CarbonInterface?, allowing you to immediately use fluent methods:

final user = await repo.find(1);
print(user?.createdAt?.diffForHumans());

Setters​

The setters are typed as Object? and accept:

  • DateTime
  • CarbonInterface (including Carbon instances)
  • null
user.updatedAt = DateTime.now();
user.updatedAt = Carbon.now().addHours(1);

Timezones​

TimestampsTZ and SoftDeletesTZ​

If you use the TZ variants of the mixins (TimestampsTZ, SoftDeletesTZ), Ormed ensures that:

  1. Values are converted to UTC before being sent to the database.
  2. Values retrieved from the database are treated as UTC.

Driver Support​

Different database drivers handle timezones differently:

  • PostgreSQL: Supports TIMESTAMP WITH TIME ZONE.
  • MySQL: DATETIME columns are typically timezone-naive. Ormed formats these as strings.
  • SQLite: Stored as ISO-8601 strings or integers.

It is highly recommended to store all timestamps in UTC and convert to the user's local timezone only for display.

Custom Date Fields​

If you define your own DateTime fields in a model, they will use the DateTimeCodec by default. This codec automatically handles Carbon instances during encoding and decoding.

()
class Event extends Model<Event> {
final DateTime scheduledAt;

Event({required this.scheduledAt});
}

// Usage
final event = $Event(scheduledAt: Carbon.tomorrow());
await repo.insert(event);

Best Practices​

// Best practices for Carbon in Ormed
void carbonBestPractices() {
// 1. Prefer CarbonImmutable for local variables
final date = CarbonImmutable.now();

// 2. Use toImmutable() when you need to store a reference
final stored = Carbon.now().toImmutable();

// 3. Model timestamps are already immutable - just use them
// final yesterday = user.createdAt?.subDay(); // Safe!

// 4. Use copy() if you need to mutate a mutable Carbon
final mutable = Carbon.now();
final safeCopy = mutable.copy();
safeCopy.addDays(5); // Only copy is affected
}

Summary​

SituationRecommendation
Model timestamps (createdAt, etc.)Already immutable - just use them
Local date variablesUse CarbonImmutable.now()
Need to mutate a dateUse copy() first, or toMutable()
Storing a date referenceCall toImmutable()
Passing dates to functionsPrefer CarbonInterface parameter type

Verify Date/Time Behavior​

  1. Assert timestamp values round-trip correctly through insert/query.
  2. Assert TimestampsTZ paths store/retrieve UTC values.
  3. Add a test that catches accidental mutable Carbon side effects.

Read This Next​