Skip to content

Commit 8645155

Browse files
Format booking duration following web's logic
1 parent 6da2cb0 commit 8645155

File tree

5 files changed

+337
-7
lines changed

5 files changed

+337
-7
lines changed

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/BookingMapper.kt

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.woocommerce.android.ui.bookings.compose.BookingSummaryModel
1515
import com.woocommerce.android.ui.bookings.details.CancelStatus
1616
import com.woocommerce.android.ui.bookings.list.BookingListItem
1717
import com.woocommerce.android.util.CurrencyFormatter
18+
import com.woocommerce.android.viewmodel.ResourceProvider
1819
import kotlinx.coroutines.Dispatchers
1920
import kotlinx.coroutines.withContext
2021
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingCustomerInfo
@@ -29,7 +30,8 @@ import javax.inject.Inject
2930

3031
class BookingMapper @Inject constructor(
3132
private val currencyFormatter: CurrencyFormatter,
32-
private val getLocations: GetLocations
33+
private val getLocations: GetLocations,
34+
private val resourceProvider: ResourceProvider
3335
) {
3436
private val summaryDateFormatter: DateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(
3537
FormatStyle.MEDIUM,
@@ -62,16 +64,18 @@ class BookingMapper @Inject constructor(
6264
staffMemberStatus: BookingStaffMemberStatus?,
6365
cancelStatus: CancelStatus,
6466
): BookingAppointmentDetailsModel {
65-
val durationMinutes = Duration.between(start, end).toMinutes()
67+
val duration = Duration.between(start, end)
68+
.normalizeBookingDuration()
69+
.toHumanReadableFormat()
6670
return BookingAppointmentDetailsModel(
6771
date = detailsDateFormatter.format(start),
6872
time = "${timeRangeFormatter.format(start)} - ${timeRangeFormatter.format(end)}",
6973
staff = staffMemberStatus,
7074
// TODO replace mocked values when available from API
7175
location = "238 Willow Creek Drive, Montgomery AL 36109",
72-
duration = "$durationMinutes min",
7376
price = currencyFormatter.formatCurrency(cost, currency),
7477
cancelStatus = cancelStatus,
78+
duration = duration,
7579
)
7680
}
7781

@@ -131,6 +135,110 @@ class BookingMapper @Inject constructor(
131135
)
132136
}
133137

138+
/**
139+
* Normalize booking duration by adjusting for precision issues.
140+
*
141+
* This function handles cases where a booking duration is very close to
142+
* common time boundaries (days/hours) but falls short due to precision issues.
143+
* It rounds up durations that are within one minute of these boundaries.
144+
*/
145+
private fun Duration.normalizeBookingDuration(): Duration {
146+
val dayInSeconds = Duration.ofDays(1).seconds
147+
val hourInSeconds = Duration.ofHours(1).seconds
148+
val minuteInSeconds = Duration.ofMinutes(1).seconds
149+
150+
var durationInSeconds = this.seconds
151+
val boundaries = listOf(dayInSeconds, hourInSeconds)
152+
for (boundary in boundaries) {
153+
val remainder = durationInSeconds % boundary
154+
val difference = if (remainder == 0L) 0L else boundary - remainder
155+
if (difference > 0 && difference <= minuteInSeconds) {
156+
durationInSeconds += difference
157+
}
158+
}
159+
return Duration.ofSeconds(durationInSeconds)
160+
}
161+
162+
@Suppress("LongMethod")
163+
private fun Duration.toHumanReadableFormat(): String {
164+
val totalSeconds = seconds
165+
val dayInSeconds = Duration.ofDays(1).toSeconds()
166+
val hourInSeconds = Duration.ofHours(1).toSeconds()
167+
val minuteInSeconds = Duration.ofMinutes(1).toSeconds()
168+
169+
return when {
170+
totalSeconds >= dayInSeconds -> {
171+
val days = (totalSeconds / dayInSeconds).toInt()
172+
val hours = ((totalSeconds % dayInSeconds) / hourInSeconds).toInt()
173+
val minutes = ((totalSeconds % hourInSeconds) / minuteInSeconds).toInt()
174+
175+
val parts = mutableListOf<String>()
176+
parts += resourceProvider.getQuantityString(
177+
quantity = days,
178+
default = R.string.booking_duration_days,
179+
one = R.string.booking_duration_day
180+
)
181+
if (hours > 0) {
182+
parts += resourceProvider.getQuantityString(
183+
quantity = hours,
184+
default = R.string.booking_duration_hours,
185+
one = R.string.booking_duration_hour
186+
)
187+
}
188+
if (minutes > 0) {
189+
parts += resourceProvider.getQuantityString(
190+
quantity = minutes,
191+
default = R.string.booking_duration_minutes,
192+
one = R.string.booking_duration_minute
193+
)
194+
}
195+
parts.joinToString(separator = " ")
196+
}
197+
198+
totalSeconds >= hourInSeconds -> {
199+
val hours = (totalSeconds / hourInSeconds).toInt()
200+
val minutes = ((totalSeconds % hourInSeconds) / minuteInSeconds).toInt()
201+
if (minutes == 0) {
202+
resourceProvider.getQuantityString(
203+
quantity = hours,
204+
default = R.string.booking_duration_hours,
205+
one = R.string.booking_duration_hour
206+
)
207+
} else {
208+
val hoursPart = resourceProvider.getQuantityString(
209+
quantity = hours,
210+
default = R.string.booking_duration_hours,
211+
one = R.string.booking_duration_hour
212+
)
213+
val minutesPart = resourceProvider.getQuantityString(
214+
quantity = minutes,
215+
default = R.string.booking_duration_minutes,
216+
one = R.string.booking_duration_minute
217+
)
218+
"$hoursPart $minutesPart"
219+
}
220+
}
221+
222+
totalSeconds >= minuteInSeconds -> {
223+
val minutes = (totalSeconds / minuteInSeconds).toInt()
224+
resourceProvider.getQuantityString(
225+
quantity = minutes,
226+
default = R.string.booking_duration_minutes,
227+
one = R.string.booking_duration_minute
228+
)
229+
}
230+
231+
else -> {
232+
val seconds = totalSeconds.toInt()
233+
resourceProvider.getQuantityString(
234+
quantity = seconds,
235+
default = R.string.booking_duration_seconds,
236+
one = R.string.booking_duration_second
237+
)
238+
}
239+
}
240+
}
241+
134242
private suspend fun BookingCustomerInfo.address(): Address? {
135243
val countryCode = billingCountry ?: return null
136244
val (country, state) = withContext(Dispatchers.IO) {

WooCommerce/src/main/res/values/strings.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4234,6 +4234,14 @@
42344234
<string name="booking_payment_mark_as_paid">Mark as paid</string>
42354235
<string name="booking_payment_view_order">View order</string>
42364236
<string name="booking_fetch_error">Error fetching booking</string>
4237+
<string name="booking_duration_minute">%1$d minute</string>
4238+
<string name="booking_duration_minutes">%1$d minutes</string>
4239+
<string name="booking_duration_hour">%1$d hour</string>
4240+
<string name="booking_duration_hours">%1$d hours</string>
4241+
<string name="booking_duration_day">%1$d day</string>
4242+
<string name="booking_duration_days">%1$d days</string>
4243+
<string name="booking_duration_second">%1$d second</string>
4244+
<string name="booking_duration_seconds">%1$d seconds</string>
42374245

42384246
<string name="or_use_password" a8c-src-lib="module:login">Use password to sign in</string>
42394247
<string name="about_automattic_main_page_title">About %1$s</string>

0 commit comments

Comments
 (0)