Skip to main content

DateTime Formatting

Configure how DateTime, DateOnly, and TimeOnly values are stored and retrieved in DynamoDB.

Overview

FluentDynamoDb supports native serialization of .NET date and time types:

  • DateTime - Full date and time with optional timezone
  • DateTimeOffset - Date and time with explicit timezone offset
  • DateOnly - Date without time component (.NET 6+)
  • TimeOnly - Time without date component (.NET 6+)

All types are stored as strings in DynamoDB using ISO 8601 format by default, with support for custom format strings.

DateTime Serialization

Default Format (ISO 8601)

By default, DateTime values are serialized using ISO 8601 round-trip format:

[DynamoDbTable("Events")]
public partial class Event
{
[PartitionKey]
[DynamoDbAttribute("pk")]
public string Pk { get; set; } = string.Empty;

// Default ISO 8601 format: "2024-12-28T14:30:45.0000000Z"
[DynamoDbAttribute("createdAt")]
public DateTime CreatedAt { get; set; }
}

Custom Format

Use the Format property to specify a custom format string:

// Custom format: "12/28/2024 2:30:45 PM"
[DynamoDbAttribute("displayDate", Format = "MM/dd/yyyy h:mm:ss tt")]
public DateTime DisplayDate { get; set; }

DateOnly Serialization

DateOnly represents a date without a time component. Available in .NET 6+.

Default Format (ISO 8601)

[DynamoDbTable("Events")]
public partial class Event
{
[PartitionKey]
[DynamoDbAttribute("pk")]
public string Pk { get; set; } = string.Empty;

// Default ISO 8601 format: "2024-12-28"
[DynamoDbAttribute("eventDate")]
public DateOnly EventDate { get; set; }
}

Custom Format

// Custom format: "12/28/2024"
[DynamoDbAttribute("displayDate", Format = "MM/dd/yyyy")]
public DateOnly DisplayDate { get; set; }

// Long date format: "Saturday, December 28, 2024"
[DynamoDbAttribute("formattedDate", Format = "D")]
public DateOnly FormattedDate { get; set; }

DateOnly Format Specifiers

SpecifierDescriptionExample Output
o or OISO 8601 (default)2024-12-28
dShort date12/28/2024
DLong dateSaturday, December 28, 2024
yyyy-MM-ddCustom2024-12-28
MM/dd/yyyyCustom12/28/2024
dd-MMM-yyyyCustom28-Dec-2024

DateOnly in Queries

var today = DateOnly.FromDateTime(DateTime.Today);

var events = await table.Events.Query()
.Where(x => x.TenantId == tenantId)
.WithFilter(x => x.EventDate == today)
.ToListAsync();

TimeOnly Serialization

TimeOnly represents a time without a date component. Available in .NET 6+.

Default Format (ISO 8601)

[DynamoDbTable("Schedules")]
public partial class Schedule
{
[PartitionKey]
[DynamoDbAttribute("pk")]
public string Pk { get; set; } = string.Empty;

// Default ISO 8601 format: "14:30:45.0000000"
[DynamoDbAttribute("startTime")]
public TimeOnly StartTime { get; set; }
}

Custom Format

// Custom format: "2:30 PM"
[DynamoDbAttribute("displayTime", Format = "h:mm tt")]
public TimeOnly DisplayTime { get; set; }

// 24-hour format: "14:30"
[DynamoDbAttribute("militaryTime", Format = "HH:mm")]
public TimeOnly MilitaryTime { get; set; }

TimeOnly Format Specifiers

SpecifierDescriptionExample Output
o or OISO 8601 (default)14:30:45.0000000
tShort time2:30 PM
TLong time2:30:45 PM
HH:mmCustom 24-hour14:30
h:mm ttCustom 12-hour2:30 PM
HH:mm:ssCustom with seconds14:30:45

TimeOnly in Queries

var businessStart = new TimeOnly(9, 0);
var businessEnd = new TimeOnly(17, 0);

var schedules = await table.Schedules.Query()
.Where(x => x.LocationId == locationId)
.WithFilter(x => x.StartTime.CompareTo(businessStart) >= 0
&& x.StartTime.CompareTo(businessEnd) <= 0)
.ToListAsync();

Collections of Date Types

DateOnly and TimeOnly work in collections:

[DynamoDbTable("Availability")]
public partial class Availability
{
[PartitionKey]
[DynamoDbAttribute("pk")]
public string Pk { get; set; } = string.Empty;

// List of available dates
[DynamoDbAttribute("availableDates")]
public List<DateOnly> AvailableDates { get; set; } = new();

// List of available time slots
[DynamoDbAttribute("timeSlots")]
public List<TimeOnly> TimeSlots { get; set; } = new();
}

Nullable Support

All date types support nullable values:

[DynamoDbAttribute("cancelledAt")]
public DateTime? CancelledAt { get; set; }

[DynamoDbAttribute("expiryDate")]
public DateOnly? ExpiryDate { get; set; }

[DynamoDbAttribute("endTime")]
public TimeOnly? EndTime { get; set; }

Update Expressions

DateOnly and TimeOnly work in update expressions:

await table.Events.Update(eventId)
.Set(x => new EventUpdateModel
{
EventDate = DateOnly.FromDateTime(DateTime.Today.AddDays(7)),
StartTime = new TimeOnly(10, 0)
})
.UpdateAsync();

Best Practices

Use DateOnly for Date-Only Data

// ✅ Good - DateOnly for birth dates, event dates, etc.
[DynamoDbAttribute("birthDate")]
public DateOnly BirthDate { get; set; }

// ⚠️ Avoid - DateTime when you only need the date
[DynamoDbAttribute("birthDate")]
public DateTime BirthDate { get; set; }

Use TimeOnly for Time-Only Data

// ✅ Good - TimeOnly for schedules, opening hours, etc.
[DynamoDbAttribute("openingTime")]
public TimeOnly OpeningTime { get; set; }

// ⚠️ Avoid - DateTime when you only need the time
[DynamoDbAttribute("openingTime")]
public DateTime OpeningTime { get; set; }

Consider Sort Key Compatibility

When using dates as sort keys, ensure the format sorts correctly:

// ✅ Good - ISO 8601 sorts lexicographically
[DynamoDbAttribute("eventDate")] // Default format: "2024-12-28"
public DateOnly EventDate { get; set; }

// ⚠️ Caution - MM/dd/yyyy doesn't sort correctly
[DynamoDbAttribute("eventDate", Format = "MM/dd/yyyy")] // "12/28/2024"
public DateOnly EventDate { get; set; }

See Also