• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.getBestDateTimePattern
20 import android.text.format.DateFormat.getLongDateFormat
21 import android.text.format.DateFormat.getTimeFormat
22 import android.text.format.DateFormat.is24HourFormat
23 import android.text.format.DateUtils
24 import com.android.healthconnect.controller.R
25 import dagger.hilt.android.qualifiers.ApplicationContext
26 import java.time.Instant
27 import java.time.ZoneId
28 import java.time.format.DateTimeFormatter
29 import java.util.Locale
30 import javax.inject.Inject
31 
32 /** Formatter for printing time and time ranges. */
33 class LocalDateTimeFormatter @Inject constructor(@ApplicationContext private val context: Context) {
34 
35     companion object {
36         // Example: "Sun, Aug 20, 2023"
37         private const val WEEKDAY_DATE_FORMAT_FLAGS_WITH_YEAR: Int =
38             DateUtils.FORMAT_SHOW_WEEKDAY or
39                 DateUtils.FORMAT_SHOW_DATE or
40                 DateUtils.FORMAT_ABBREV_ALL
41 
42         // Example: "Sun, Aug 20"
43         private const val WEEKDAY_DATE_FORMAT_FLAGS_WITHOUT_YEAR: Int =
44             WEEKDAY_DATE_FORMAT_FLAGS_WITH_YEAR or DateUtils.FORMAT_NO_YEAR
45 
46         // Example: "Aug 20, 2023"
47         private const val DATE_FORMAT_FLAGS_WITH_YEAR: Int =
48             DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_ABBREV_ALL
49 
50         // Example: "Aug 20"
51         private const val DATE_FORMAT_FLAGS_WITHOUT_YEAR: Int =
52             DATE_FORMAT_FLAGS_WITH_YEAR or DateUtils.FORMAT_NO_YEAR
53 
54         // Example: "August 2023".
55         private const val MONTH_FORMAT_FLAGS_WITH_YEAR: Int =
56             DateUtils.FORMAT_SHOW_DATE or
57                 DateUtils.FORMAT_SHOW_YEAR or
58                 DateUtils.FORMAT_NO_MONTH_DAY
59 
60         // Example: "August".
61         private const val MONTH_FORMAT_FLAGS_WITHOUT_YEAR: Int =
62             DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_NO_MONTH_DAY
63     }
64 
65     private val timeFormat: java.text.DateFormat
66         get() = getTimeFormat(context)
67 
68     private val longDateFormat: java.text.DateFormat
69         get() = getLongDateFormat(context)
70 
71     private val shortDateFormat: DateTimeFormatter
72         get() {
73             val systemFormat = getBestDateTimePattern(Locale.getDefault(), "dMMMM")
74             return DateTimeFormatter.ofPattern(systemFormat, Locale.getDefault())
75         }
76 
77     // Example: "Aug 20, 14:06"
78     private val dateAndTime24HourFormat: DateTimeFormatter
79         get() {
80             val systemFormat = getBestDateTimePattern(Locale.getDefault(), "MMMd Hm")
81             return DateTimeFormatter.ofPattern(systemFormat, Locale.getDefault())
82         }
83 
84     // Example: "Aug 20, 2:06PM"
85     private val dateAndTime12HourFormat: DateTimeFormatter
86         get() {
87             val systemFormat = getBestDateTimePattern(Locale.getDefault(), "MMMd hm")
88             return DateTimeFormatter.ofPattern(systemFormat, Locale.getDefault())
89         }
90 
91     /** Returns localized time. */
formatTimenull92     fun formatTime(instant: Instant): String {
93         return timeFormat.format(instant.toEpochMilli())
94     }
95 
96     /** Returns localized long versions of date, such as "15 August 2022". */
formatLongDatenull97     fun formatLongDate(instant: Instant): String {
98         return longDateFormat.format(instant.toEpochMilli())
99     }
100 
101     /** Returns localized short versions of date, such as "15 August" */
formatShortDatenull102     fun formatShortDate(instant: Instant): String {
103         return instant.atZone(ZoneId.systemDefault()).format(shortDateFormat)
104     }
105 
106     /** Returns localized short versions of date, such as "15 Aug" */
formatShortDateWithoutYearnull107     fun formatShortDateWithoutYear(startTime: Instant): String {
108         return DateUtils.formatDateTime(
109             context,
110             startTime.toEpochMilli(),
111             DATE_FORMAT_FLAGS_WITHOUT_YEAR,
112         )
113     }
114 
115     /** Returns localized short versions of date, such as "15 Aug, 2022" */
formatShortDateWithYearnull116     fun formatShortDateWithYear(startTime: Instant): String {
117         return DateUtils.formatDateTime(
118             context,
119             startTime.toEpochMilli(),
120             DATE_FORMAT_FLAGS_WITH_YEAR,
121         )
122     }
123 
124     /** Returns localized short versions of date and time, such as "15 Aug, 14:06" */
formatDateAndTimenull125     fun formatDateAndTime(instant: Instant): String {
126         val format =
127             if (is24HourFormat(context)) {
128                 dateAndTime24HourFormat
129             } else {
130                 dateAndTime12HourFormat
131             }
132         return instant.atZone(ZoneId.systemDefault()).format(format)
133     }
134 
135     /** Returns localized time range. */
formatTimeRangenull136     fun formatTimeRange(start: Instant, end: Instant): String {
137         return context.getString(R.string.time_range, formatTime(start), formatTime(end))
138     }
139 
140     /** Returns accessible and localized time range. */
formatTimeRangeA11ynull141     fun formatTimeRangeA11y(start: Instant, end: Instant): String {
142         return context.getString(R.string.time_range_long, formatTime(start), formatTime(end))
143     }
144 
145     /** Formats date with weekday and year (e.g. "Sun, Aug 20, 2023"). */
formatWeekdayDateWithYearnull146     fun formatWeekdayDateWithYear(time: Instant): String {
147         return DateUtils.formatDateTime(
148             context,
149             time.toEpochMilli(),
150             WEEKDAY_DATE_FORMAT_FLAGS_WITH_YEAR or DateUtils.FORMAT_ABBREV_ALL,
151         )
152     }
153 
154     /** Formats date with weekday (e.g. "Sun, Aug 20"). */
formatWeekdayDateWithoutYearnull155     fun formatWeekdayDateWithoutYear(time: Instant): String {
156         return DateUtils.formatDateTime(
157             context,
158             time.toEpochMilli(),
159             WEEKDAY_DATE_FORMAT_FLAGS_WITHOUT_YEAR or DateUtils.FORMAT_ABBREV_ALL,
160         )
161     }
162 
163     /** Formats date range with year(e.g. "Aug 21 - 27, 2023", "Aug 28 - Sept 3, 2023"). */
formatDateRangeWithYearnull164     fun formatDateRangeWithYear(startTime: Instant, endTime: Instant): String {
165         return DateUtils.formatDateRange(
166             context,
167             startTime.toEpochMilli(),
168             endTime.toEpochMilli(),
169             DATE_FORMAT_FLAGS_WITH_YEAR,
170         )
171     }
172 
173     /** Formats date range (e.g. "Aug 21 - 27", "Aug 28 - Sept 3"). */
formatDateRangeWithoutYearnull174     fun formatDateRangeWithoutYear(startTime: Instant, endTime: Instant): String {
175         return DateUtils.formatDateRange(
176             context,
177             startTime.toEpochMilli(),
178             endTime.toEpochMilli(),
179             DATE_FORMAT_FLAGS_WITHOUT_YEAR,
180         )
181     }
182 
183     /** Formats month and year (e.g. "August 2023"). */
formatMonthWithYearnull184     fun formatMonthWithYear(time: Instant): String {
185         return DateUtils.formatDateTime(context, time.toEpochMilli(), MONTH_FORMAT_FLAGS_WITH_YEAR)
186     }
187 
188     /** Formats month (e.g. "August"). */
formatMonthWithoutYearnull189     fun formatMonthWithoutYear(time: Instant): String {
190         return DateUtils.formatDateTime(
191             context,
192             time.toEpochMilli(),
193             MONTH_FORMAT_FLAGS_WITHOUT_YEAR,
194         )
195     }
196 }
197