• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.media.photopicker.util;
18 
19 import static android.icu.text.DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
20 import static android.icu.text.RelativeDateTimeFormatter.Style.LONG;
21 
22 import android.icu.text.DateFormat;
23 import android.icu.text.DisplayContext;
24 import android.icu.text.RelativeDateTimeFormatter;
25 import android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit;
26 import android.icu.text.RelativeDateTimeFormatter.Direction;
27 import android.icu.util.ULocale;
28 import android.text.format.DateUtils;
29 
30 import androidx.annotation.VisibleForTesting;
31 
32 import java.time.Instant;
33 import java.time.LocalDate;
34 import java.time.LocalDateTime;
35 import java.time.ZoneId;
36 import java.time.format.TextStyle;
37 import java.time.temporal.ChronoUnit;
38 import java.util.Locale;
39 
40 /**
41  * Provide the utility methods to handle date time.
42  */
43 public class DateTimeUtils {
44 
45     private static final String DATE_FORMAT_SKELETON_WITH_YEAR = "EMMMdy";
46     private static final String DATE_FORMAT_SKELETON_WITHOUT_YEAR = "EMMMd";
47     private static final String DATE_FORMAT_SKELETON_WITH_TIME = "MMMdyhmmss";
48 
49     /**
50      * Formats a time according to the local conventions for PhotoGrid.
51      *
52      * If the difference of the date between the time and now is zero, show
53      * "Today".
54      * If the difference is 1, show "Yesterday".
55      * If the difference is less than 7, show the weekday. E.g. "Sunday".
56      * Otherwise, show the weekday and the date. E.g. "Sat, Jun 5".
57      * If they have different years, show the weekday, the date and the year.
58      * E.g. "Sat, Jun 5, 2021"
59      *
60      * @param when    the time to be formatted. The unit is in milliseconds
61      *                since January 1, 1970 00:00:00.0 UTC.
62      * @return the formatted string
63      */
getDateHeaderString(long when)64     public static String getDateHeaderString(long when) {
65         // Get the system time zone
66         final ZoneId zoneId = ZoneId.systemDefault();
67         final LocalDate nowDate = LocalDate.now(zoneId);
68 
69         return getDateHeaderString(when, nowDate);
70     }
71 
72     /**
73      * Formats a time according to the local conventions for content description.
74      *
75      * The format of the returned string is fixed to {@code DATE_FORMAT_SKELETON_WITH_TIME}.
76      * E.g. "Feb 2, 2022, 2:22:22 PM"
77      *
78      * @param when    the time to be formatted. The unit is in milliseconds
79      *                since January 1, 1970 00:00:00.0 UTC.
80      * @return the formatted string
81      */
getDateTimeStringForContentDesc(long when)82     public static String getDateTimeStringForContentDesc(long when) {
83         return getDateTimeString(when, DATE_FORMAT_SKELETON_WITH_TIME, Locale.getDefault());
84     }
85 
86     @VisibleForTesting
getDateHeaderString(long when, LocalDate nowDate)87     static String getDateHeaderString(long when, LocalDate nowDate) {
88         // Get the system time zone
89         final ZoneId zoneId = ZoneId.systemDefault();
90         final LocalDate whenDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(when),
91                 zoneId).toLocalDate();
92 
93         final long dayDiff = ChronoUnit.DAYS.between(whenDate, nowDate);
94         if (dayDiff == 0) {
95             return getTodayString();
96         } else if (dayDiff == 1) {
97             return getYesterdayString();
98         } else if (dayDiff > 0 && dayDiff < 7) {
99             return whenDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault());
100         } else {
101             final String skeleton;
102             if (whenDate.getYear() == nowDate.getYear()) {
103                 skeleton = DATE_FORMAT_SKELETON_WITHOUT_YEAR;
104             } else {
105                 skeleton = DATE_FORMAT_SKELETON_WITH_YEAR;
106             }
107 
108             return getDateTimeString(when, skeleton, Locale.getDefault());
109         }
110     }
111 
112     @VisibleForTesting
getDateTimeString(long when, String skeleton, Locale locale)113     static String getDateTimeString(long when, String skeleton, Locale locale) {
114         final DateFormat format = DateFormat.getInstanceForSkeleton(skeleton, locale);
115         format.setContext(DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
116         return format.format(when);
117     }
118 
119     /**
120      * It is borrowed from {@link DateUtils} since it is no official API yet.
121      *
122      * @param oneMillis the first time. The unit is in milliseconds since
123      *                  January 1, 1970 00:00:00.0 UTC.
124      * @param twoMillis the second time. The unit is in milliseconds since
125      *                  January 1, 1970 00:00:00.0 UTC.
126      * @return True, the date is the same. Otherwise, return false.
127      */
isSameDate(long oneMillis, long twoMillis)128     public static boolean isSameDate(long oneMillis, long twoMillis) {
129         // Get the system time zone
130         final ZoneId zoneId = ZoneId.systemDefault();
131 
132         final Instant oneInstant = Instant.ofEpochMilli(oneMillis);
133         final LocalDateTime oneLocalDateTime = LocalDateTime.ofInstant(oneInstant, zoneId);
134 
135         final Instant twoInstant = Instant.ofEpochMilli(twoMillis);
136         final LocalDateTime twoLocalDateTime = LocalDateTime.ofInstant(twoInstant, zoneId);
137 
138         return (oneLocalDateTime.getYear() == twoLocalDateTime.getYear())
139                 && (oneLocalDateTime.getMonthValue() == twoLocalDateTime.getMonthValue())
140                 && (oneLocalDateTime.getDayOfMonth() == twoLocalDateTime.getDayOfMonth());
141     }
142 
143     @VisibleForTesting
getTodayString()144     static String getTodayString() {
145         final RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
146                 ULocale.getDefault(), null, LONG, CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
147         return fmt.format(Direction.THIS, AbsoluteUnit.DAY);
148     }
149 
150     @VisibleForTesting
getYesterdayString()151     static String getYesterdayString() {
152         final RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
153                 ULocale.getDefault(), null, LONG, CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
154         return fmt.format(Direction.LAST, AbsoluteUnit.DAY);
155     }
156 }
157