TimeRangeFilter.kt
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.health.connect.client.time
import androidx.health.connect.client.records.Record
import java.time.Instant
import java.time.LocalDateTime
/**
* Specification of time range for read and delete requests.
*
* The time range can be specified in one of the following ways:
* - use [between] for a closed-ended time range, inclusive-exclusive;
* - use [before] for a open-ended start time range, end time is exclusive;
* - use [after] for a open-ended end time range, start time is inclusive.
*
* Time can be specified in one of the two ways:
* - use [Instant] for a specific point in time such as "2021-01-03 at 10:00 UTC+1";
* - use [LocalDateTime] for a user experienced time concept such as "2021-01-03 at 10 o'clock",
* without knowing which time zone the user was at that time. [Record] without specifying zoneOffset
* will assume the current system zone offset at query time.
*/
class TimeRangeFilter
internal constructor(
internal val startTime: Instant? = null,
internal val endTime: Instant? = null,
internal val localStartTime: LocalDateTime? = null,
internal val localEndTime: LocalDateTime? = null,
) {
companion object {
/**
* Creates a [TimeRangeFilter] for a time range within the [Instant] time range [startTime,
* endTime).
*
* If user created a [Record] at 2pm(UTC+1), crossed a time zone and created a new [Record]
* at 3pm(UTC). Filtering between 2pm(UTC) and 6pm(UTC) will include the record at 3pm(UTC)
* but not the record at 2pm(UTC+1), because 2pm(UTC+1) happened before 2pm(UTC).
*
* @param startTime start time of the filter.
* @param endTime end time of the filter.
* @return a [TimeRangeFilter] for filtering [Record]s.
*
* @see before for time range with open-ended [startTime].
* @see after for time range with open-ended [endTime].
*/
@JvmStatic
fun between(startTime: Instant, endTime: Instant): TimeRangeFilter {
require(startTime.isBefore(endTime)) { "end time needs be after start time" }
return TimeRangeFilter(startTime = startTime, endTime = endTime)
}
/**
* Creates a [TimeRangeFilter] for a time range within the [LocalDateTime] range [startTime,
* endTime).
*
* @param startTime start time of the filter.
* @param endTime end time of the filter.
* @return a [TimeRangeFilter] for filtering [Record]s.
*
* @see before for time range with open-ended [startTime].
* @see after for time range with open-ended [endTime].
*/
@JvmStatic
fun between(startTime: LocalDateTime, endTime: LocalDateTime): TimeRangeFilter {
require(startTime.isBefore(endTime)) { "end time needs be after start time" }
return TimeRangeFilter(
startTime = null,
endTime = null,
localStartTime = startTime,
localEndTime = endTime
)
}
/**
* Creates a [TimeRangeFilter] for a time range until the given [endTime].
*
* @param endTime end time of the filter.
* @return a [TimeRangeFilter] for filtering [Record]s.
*
* @see between for closed-ended time range.
* @see after for time range with open-ended [endTime]
*/
@JvmStatic
fun before(endTime: Instant) = TimeRangeFilter(startTime = null, endTime = endTime)
/**
* Creates a [TimeRangeFilter] for a time range until the given [endTime].
*
* @param endTime end time of the filter.
* @return a [TimeRangeFilter] for filtering [Record]s.
*
* @see between for closed-ended time range.
* @see after for time range with open-ended [endTime]
*/
@JvmStatic
fun before(endTime: LocalDateTime) =
TimeRangeFilter(
startTime = null,
endTime = null,
localStartTime = null,
localEndTime = endTime
)
/**
* Creates a [TimeRangeFilter] for a time range after the given [startTime].
*
* @param startTime start time of the filter.
* @return a [TimeRangeFilter] for filtering [Record]s.
*
* @see between for closed-ended time range.
* @see after for time range with open-ended [startTime]
*/
@JvmStatic fun after(startTime: Instant) = TimeRangeFilter(startTime = startTime)
/**
* Creates a [TimeRangeFilter] for a time range after the given [startTime].
*
* @param startTime start time of the filter.
* @return a [TimeRangeFilter] for filtering [Record]s.
*
* @see between for closed-ended time range.
* @see after for time range with open-ended [startTime]
*/
@JvmStatic
fun after(startTime: LocalDateTime) =
TimeRangeFilter(startTime = null, endTime = null, localStartTime = startTime)
/**
* Default [TimeRangeFilter] where neither start nor end time is specified, no [Record]s
* will be filtered.
*/
@JvmStatic internal fun none(): TimeRangeFilter = TimeRangeFilter()
}
internal fun isOpenEnded(): Boolean =
(localStartTime == null || localEndTime == null) && (startTime == null || endTime == null)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is TimeRangeFilter) return false
if (startTime != other.startTime) return false
if (endTime != other.endTime) return false
if (localStartTime != other.localStartTime) return false
if (localEndTime != other.localEndTime) return false
return true
}
override fun hashCode(): Int {
var result = 0
result = 31 * result + (startTime?.hashCode() ?: 0)
result = 31 * result + (endTime?.hashCode() ?: 0)
result = 31 * result + (localStartTime?.hashCode() ?: 0)
result = 31 * result + (localEndTime?.hashCode() ?: 0)
return result
}
}