Skip to content

SyncEngine fails to parse standard ISO8601 dates from CloudKit #427

@Armenm

Description

@Armenm

Description

When CloudKit returns dates in standard ISO8601 format (e.g., 2026-03-21T03:57:40.026Z), SyncEngine fails to parse them with the error:

Cannot parse 2026-03-21T03:57:40.026Z. String should adhere to the preferred format of the locale, such as 2026-03-21 03:57:40.

Root Cause

The ISO8601.swift internal helpers use a non-standard date format:

// Current implementation (incorrect for CloudKit)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"  // Space separator, no timezone

// On iOS 15+
.dateTimeSeparator(.space)  // Also expects space instead of 'T'

But CloudKit returns standard ISO8601 with T separator and Z timezone suffix:

  • 2026-03-21T03:57:40.026Z

Suggested Fix

Update ISO8601.swift to handle the standard format:

extension DateFormatter {
  fileprivate static let iso8601Fractional: DateFormatter = {
    let formatter = DateFormatter()
    formatter.calendar = Calendar(identifier: .iso8601)
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"  // Standard ISO8601
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.timeZone = TimeZone(secondsFromGMT: 0)
    return formatter
  }()
}

@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
extension Date.ISO8601FormatStyle {
  fileprivate func currentTimestamp(includingFractionalSeconds: Bool) -> Self {
    year().month().day()
      .dateTimeSeparator(.standard)  // Use 'T' separator
      .time(includingFractionalSeconds: includingFractionalSeconds)
      .timeZone(separator: .omitted)  // Handle 'Z' suffix
  }
}

Alternatively, the parser should try both formats - space-separated (for SQLite storage) and standard ISO8601 (for CloudKit).

Reproducibility

  1. Create a @Table with a Date field
  2. Sync to CloudKit via SyncEngine
  3. On a different device, attempt to sync down the record
  4. Date parsing fails with the above error

Environment

  • sqlite-data: 1.6.0
  • iOS/macOS: 17+/14+
  • CloudKit: Private database

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions