Engineering / WEAR API LAB

Wearable data quality: handle gaps, time zones, and duplicates

A practical model for timestamps, provenance, missing observations, duplicate delivery, and revised records that keeps wearable charts explainable.

Neon data points and branching time markers framed by multicolor stripes with WearAPI.com branding.

A wearable chart can be wrong even when every request returns successfully. A sleep period lands on the wrong day, a repeated delivery doubles a workout, or an absent reading becomes zero. These failures usually begin when the application erases context while normalizing records. Improving wearable data quality means preserving enough information to explain a value before calculating with it.

Start by defining the questions the feature must answer. A live workout display needs continuity and freshness. A weekly activity report needs a stable day boundary and a clear source-selection policy. A sleep trend needs careful handling of revised periods. The wearable API overview can help identify the acquisition route, but each route still needs an explicit data contract.

Store observation context before normalizing

Use a small, consistent envelope around each source payload. Include the source system, connected account reference, record type, source identifier, observation interval or instant, ingestion time, original units, and any available quality information. Preserve the unmodified source payload only when retention is appropriate and the application has a clear reason to keep it. Context is useful; unnecessary sensitive copies create another responsibility.

Keep observation time separate from arrival time. A record received this morning may describe yesterday's workout. A newly delivered version may modify a period from last week. If everything is grouped by when your server received it, reports become charts of synchronization behavior rather than the underlying activity.

Define the origin of each timestamp. A timestamp supplied by a device, a platform record interval, and a phone's receipt timestamp have different meanings. Represent that distinction in the schema. The ingestion code should not populate a field named measured_at with whatever clock value happens to be available.

Make local days an explicit policy

Store instants in a form suitable for UTC ordering while preserving available local offsets and source-assigned dates. An offset tells you how a particular local time relates to UTC. It does not, by itself, identify the full regional time-zone rules. If a source provides a named time zone, retain it separately; if it does not, avoid inventing one from the user's current location.

Consider two recorded times during a repeated autumn clock hour: 01:30 at offset minus seven hours and 01:30 at offset minus eight hours. The wall-clock text matches, but the instants are an hour apart. Deduplicating on a local timestamp alone would delete valid data. During a spring transition, subtracting local clock labels can produce an elapsed duration that is also wrong.

Choose how reports assign records to days. Possibilities include the source's day label, the original recording location's day, or a user-selected reporting zone. None should emerge accidentally from a database session setting. For an interval crossing midnight, specify whether the feature divides a quantity across days, assigns the entire session to a starting day, or retains the vendor's grouping.

Make the policy visible where it affects interpretation. An activity report may say times shown in your reporting time zone, while a travel diary may preserve original local times. Changing that preference should alter presentation predictably without rewriting the underlying observations.

Preserve missingness and units

A missing observation and an observed zero carry different information. Zero steps can be a reported quantity for a known interval. No step record can mean that nothing has arrived, permission is unavailable, the device was not worn, or the source does not report that interval. Do not choose one explanation unless the integration has evidence for it.

Represent availability separately from value. A practical model might contain a nullable value plus a status such as available, pending, permission_required, or unavailable_reason_unknown. Keep these statuses specific to what your pipeline actually knows. An empty list after a successful request is a transport result; it does not automatically establish why observations are absent.

Normalize units at a documented boundary. Keep the original unit and the conversion used when traceability matters. A duration in seconds and a display duration in minutes must not share an ambiguous field named duration. Use readable field names or typed quantity objects, and round for display after calculations rather than repeatedly rounding during ingestion.

For aggregates, define a denominator that matches the question. Averaging available nightly durations answers a different question from treating missing nights as zero and dividing by every calendar day. Show the number of included nights or usable observation coverage when it materially changes how a reader should interpret the result.

Separate retries from overlapping sources

The Android Health Connect metadata reference illustrates useful identity concepts. Records have a platform-assigned identifier and data origin; metadata can also carry client-provided identifiers, versions, device information, and a recording method. For the same client record identifier in the relevant client and data-type context, the highest client record version takes precedence. These fields support traceability, but they do not make two independent devices' records equivalent.

For ingestion, construct an identity from the connection or source namespace, record type, and stable source identifier. Reprocessing that identity should update its stored representation safely. Include the namespace: unrelated sources can use the same short identifier. Avoid using a measurement value as identity, because the same heart rate can occur many times legitimately.

Then handle overlapping sources as a separate product decision. A watch and a phone can both describe activity during one interval. These are not necessarily duplicate deliveries. Choose a source preference, use a platform aggregate whose behavior meets your requirements, or show sources separately. Preserve the original records so a later change to the selection rule can be explained.

The Health Connect and HealthKit comparison develops the platform access decision. Whichever route you choose, document whether the values displayed are source records, platform aggregates, or your own calculations.

Design for revisions and deletions

Do not assume a completed historical interval will never change. Build ingestion to accept revised records and source deletions when the integration exposes them. Keep the source's version or modification marker where available. When a later arrival contains an older version, a simple last-arrival-wins rule can restore stale data over a newer record.

Define a revision window for routine reconciliation. This is an application policy based on source behavior, feature needs, and operational cost, not a universal number of days. Recheck a bounded recent interval, and provide a controlled path for deeper repair when an outage or corrected source record requires it. The cost planning guide explains why reconciliation belongs in the capacity model.

Recalculate only the views affected by a change when possible. If a sleep period shifts across a day boundary, both old and new day aggregates may need rebuilding. If a record is deleted, remove its contribution and retain only the minimal operational evidence needed by the retention policy. Derived tables should not preserve an observation indefinitely after its source record has been removed.

Test the questions that break charts

  1. Create two records with identical local clock text but different offsets. Expect two ordered instants.
  2. Import the same identifier twice with identical content. Expect one stored record and one contribution to totals.
  3. Import a newer version, followed by an older version. Expect the newer state to remain authoritative where version semantics support it.
  4. Supply a valid zero, a missing field, and an empty response. Expect distinguishable availability states.
  5. Move an interval across midnight in a revision. Expect both affected daily views to reconcile.
  6. Provide overlapping phone and watch records. Expect the documented source policy, rather than automatic summation.
  7. Interrupt a paginated import and resume it. Expect the same final dataset as a complete uninterrupted run.

Make expected outputs concrete. Assert the displayed date, quantity, unit, coverage label, and source, as well as database counts. Tests that only confirm an HTTP request succeeded leave the interpretation layer unexamined. Use synthetic records for these cases so they can be shared and repeated without exposing a person's history.

Make quality observable

Monitor the pipeline with measures that describe its work: oldest unfinished import, stale connections, rejected payload shapes, repeated deliveries, and records awaiting reconciliation. Separate a provider outage from a parser regression and an authorization problem. Each needs a different response, and grouping them under no data makes recovery slower.

For a first release, aim for explainable records and deterministic calculations. Preserve time context, units, source identity, and missingness; then make revisions safe. The wearable integration guide connects these decisions to the full delivery process. A chart earns trust when its values can be traced through the system and its gaps remain visible.