1 /** 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * ``` 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * ``` 10 * 11 * Unless required by applicable law or agreed to in writing, software distributed under the License 12 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 * or implied. See the License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.android.healthconnect.controller.utils 17 18 import android.content.Context 19 import android.text.format.DateFormat.* 20 import android.text.format.DateUtils 21 import com.android.healthconnect.controller.R 22 import dagger.hilt.android.qualifiers.ApplicationContext 23 import java.time.Instant 24 import java.time.ZoneId 25 import java.time.format.DateTimeFormatter 26 import java.util.Locale 27 import javax.inject.Inject 28 29 /** Formatter for printing time and time ranges. */ 30 class LocalDateTimeFormatter @Inject constructor(@ApplicationContext private val context: Context) { 31 32 companion object { 33 // Example: "Sun, Aug 20, 2023" 34 private const val WEEKDAY_DATE_FORMAT_FLAGS_WITH_YEAR: Int = 35 DateUtils.FORMAT_SHOW_WEEKDAY or 36 DateUtils.FORMAT_SHOW_DATE or 37 DateUtils.FORMAT_ABBREV_ALL 38 39 // Example: "Sun, Aug 20" 40 private const val WEEKDAY_DATE_FORMAT_FLAGS_WITHOUT_YEAR: Int = 41 WEEKDAY_DATE_FORMAT_FLAGS_WITH_YEAR or DateUtils.FORMAT_NO_YEAR 42 43 // Example: "Aug 20, 2023" 44 private const val DATE_FORMAT_FLAGS_WITH_YEAR: Int = 45 DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_ABBREV_ALL 46 47 // Example: "Aug 20" 48 private const val DATE_FORMAT_FLAGS_WITHOUT_YEAR: Int = 49 DATE_FORMAT_FLAGS_WITH_YEAR or DateUtils.FORMAT_NO_YEAR 50 51 // Example: "August 2023". 52 private const val MONTH_FORMAT_FLAGS_WITH_YEAR: Int = 53 DateUtils.FORMAT_SHOW_DATE or 54 DateUtils.FORMAT_SHOW_YEAR or 55 DateUtils.FORMAT_NO_MONTH_DAY 56 57 // Example: "August". 58 private const val MONTH_FORMAT_FLAGS_WITHOUT_YEAR: Int = 59 DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_NO_MONTH_DAY 60 } 61 <lambda>null62 private val timeFormat by lazy { getTimeFormat(context) } <lambda>null63 private val longDateFormat by lazy { getLongDateFormat(context) } <lambda>null64 private val shortDateFormat by lazy { 65 val systemFormat = getBestDateTimePattern(Locale.getDefault(), "dMMMM") 66 DateTimeFormatter.ofPattern(systemFormat, Locale.getDefault()) 67 } <lambda>null68 private val monthAndYearFormat by lazy { 69 val systemFormat = getBestDateTimePattern(Locale.getDefault(), "MMMMYYYY") 70 DateTimeFormatter.ofPattern(systemFormat, Locale.getDefault()) 71 } <lambda>null72 private val monthFormat by lazy { 73 val systemFormat = getBestDateTimePattern(Locale.getDefault(), "MMMM") 74 DateTimeFormatter.ofPattern(systemFormat, Locale.getDefault()) 75 } 76 77 /** Returns localized time. */ formatTimenull78 fun formatTime(instant: Instant): String { 79 return timeFormat.format(instant.toEpochMilli()) 80 } 81 82 /** Returns localized long versions of date. */ formatLongDatenull83 fun formatLongDate(instant: Instant): String { 84 return longDateFormat.format(instant.toEpochMilli()) 85 } 86 87 /** Returns localized short versions of date, such as "15 August" */ formatShortDatenull88 fun formatShortDate(instant: Instant): String { 89 return instant.atZone(ZoneId.systemDefault()).format(shortDateFormat) 90 } 91 92 /** Returns localized time range. */ formatTimeRangenull93 fun formatTimeRange(start: Instant, end: Instant): String { 94 return context.getString(R.string.time_range, formatTime(start), formatTime(end)) 95 } 96 97 /** Returns accessible and localized time range. */ formatTimeRangeA11ynull98 fun formatTimeRangeA11y(start: Instant, end: Instant): String { 99 return context.getString(R.string.time_range_long, formatTime(start), formatTime(end)) 100 } 101 102 /** Formats date with weekday and year (e.g. "Sun, Aug 20, 2023"). */ formatWeekdayDateWithYearnull103 fun formatWeekdayDateWithYear(time: Instant): String { 104 return DateUtils.formatDateTime( 105 context, 106 time.toEpochMilli(), 107 WEEKDAY_DATE_FORMAT_FLAGS_WITH_YEAR or DateUtils.FORMAT_ABBREV_ALL) 108 } 109 110 /** Formats date with weekday (e.g. "Sun, Aug 20"). */ formatWeekdayDateWithoutYearnull111 fun formatWeekdayDateWithoutYear(time: Instant): String { 112 return DateUtils.formatDateTime( 113 context, 114 time.toEpochMilli(), 115 WEEKDAY_DATE_FORMAT_FLAGS_WITHOUT_YEAR or DateUtils.FORMAT_ABBREV_ALL) 116 } 117 118 /** Formats date range with year(e.g. "Aug 21 - 27, 2023", "Aug 28 - Sept 3, 2023"). */ formatDateRangeWithYearnull119 fun formatDateRangeWithYear(startTime: Instant, endTime: Instant): String { 120 return DateUtils.formatDateRange( 121 context, startTime.toEpochMilli(), endTime.toEpochMilli(), DATE_FORMAT_FLAGS_WITH_YEAR) 122 } 123 124 /** Formats date range (e.g. "Aug 21 - 27", "Aug 28 - Sept 3"). */ formatDateRangeWithoutYearnull125 fun formatDateRangeWithoutYear(startTime: Instant, endTime: Instant): String { 126 return DateUtils.formatDateRange( 127 context, 128 startTime.toEpochMilli(), 129 endTime.toEpochMilli(), 130 DATE_FORMAT_FLAGS_WITHOUT_YEAR) 131 } 132 133 /** Formats month and year (e.g. "August 2023"). */ formatMonthWithYearnull134 fun formatMonthWithYear(time: Instant): String { 135 return DateUtils.formatDateTime(context, time.toEpochMilli(), MONTH_FORMAT_FLAGS_WITH_YEAR) 136 } 137 138 /** Formats month (e.g. "August"). */ formatMonthWithoutYearnull139 fun formatMonthWithoutYear(time: Instant): String { 140 return DateUtils.formatDateTime( 141 context, time.toEpochMilli(), MONTH_FORMAT_FLAGS_WITHOUT_YEAR) 142 } 143 } 144