wangzhibo
4 天以前 ae6f40460dcd56af6c5f60ba52c883854c3bac55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*  Copyright (C) 2015-2024 Andreas Shimokawa, AndrewH, Carsten Pfeiffer,
    Daniele Gobbetti, José Rebelo, Pavel Elagin, Petr Vaněk
 
    This file is part of Gadgetbridge.
 
    Gadgetbridge is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    Gadgetbridge is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.
 
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util;
 
import android.content.Context;
import android.text.format.DateUtils;
 
import com.github.pfichtner.durationformatter.DurationFormatter;
 
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
 
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
 
public class DateTimeUtils {
    private static SimpleDateFormat DAY_STORAGE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
    private static SimpleDateFormat HOURS_MINUTES_FORMAT = new SimpleDateFormat("HH:mm", Locale.US);
    public static SimpleDateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US){
        //see https://github.com/Freeyourgadget/Gadgetbridge/issues/1076#issuecomment-383834116 and https://stackoverflow.com/a/30221245
 
        @Override
        public Date parse(String text, ParsePosition pos) {
            if (text.length() > 3) {
                text = text.substring(0, text.length() - 3) + text.substring(text.length() - 2);
            }
            return super.parse(text, pos);
 
        }
 
        @Override
        public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
            StringBuffer rfcFormat = super.format(date, toAppendTo, pos);
            if (this.getTimeZone().equals(TimeZone.getTimeZone("UTC"))) {
                rfcFormat.setLength(rfcFormat.length()-5);
                return rfcFormat.append("Z");
            } else {
                return rfcFormat.insert(rfcFormat.length() - 2, ":");
            }
        }
 
    }; //no public access, we have to workaround Android bugs
 
    public static String formatDateTime(Date date) {
        return DateUtils.formatDateTime(GBApplication.getContext(), date.getTime(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NO_YEAR);
    }
 
    public static String formatIso8601(Date date) {
        if(GBApplication.isRunningNougatOrLater()){
            return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.US).format(date);
        }
        ISO_8601_FORMAT.setTimeZone(TimeZone.getDefault());
        return ISO_8601_FORMAT.format(date);
    }
 
    public static String formatIso8601UTC(Date date) {
        if(GBApplication.isRunningNougatOrLater()){
            final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.US);
            sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
            return sdf.format(date);
        }
        ISO_8601_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
        return ISO_8601_FORMAT.format(date);
    }
 
    public static String formatDate(Date date) {
        return DateUtils.formatDateTime(GBApplication.getContext(), date.getTime(), DateUtils.FORMAT_SHOW_DATE|DateUtils.FORMAT_ABBREV_ALL);
    }
 
    public static String formatDate(Date date, int extraFlags) {
        return DateUtils.formatDateTime(GBApplication.getContext(), date.getTime(), DateUtils.FORMAT_SHOW_DATE|DateUtils.FORMAT_ABBREV_ALL|extraFlags);
    }
 
    public static String formatDurationHoursMinutes(long duration, TimeUnit unit) {
        DurationFormatter df = DurationFormatter.Builder.SYMBOLS
                .maximum(TimeUnit.DAYS)
                .minimum(TimeUnit.SECONDS)
                .suppressZeros(DurationFormatter.SuppressZeros.LEADING, DurationFormatter.SuppressZeros.TRAILING)
                .maximumAmountOfUnitsToShow(2)
                .build();
        return df.format(duration, unit);
    }
 
    public static String formatDateRange(Date from, Date to) {
        return DateUtils.formatDateRange(GBApplication.getContext(), from.getTime(), to.getTime(), DateUtils.FORMAT_SHOW_DATE|DateUtils.FORMAT_ABBREV_ALL);
    }
 
    public static String formatDateRange(Date from, Date to, int extraFlags) {
        return DateUtils.formatDateRange(GBApplication.getContext(), from.getTime(), to.getTime(), DateUtils.FORMAT_SHOW_DATE|DateUtils.FORMAT_ABBREV_ALL|extraFlags);
    }
 
    public static Date shiftByDays(Date date, int offset) {
        Calendar cal = GregorianCalendar.getInstance();
        cal.setTime(date);
        cal.add(GregorianCalendar.DAY_OF_YEAR, offset);
        Date newDate = cal.getTime();
        return newDate;
    }
 
    public static Date dayStart(final Date date) {
        final Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
 
    public static Calendar dayStart(final Calendar calendar) {
        final Calendar ret = (Calendar) calendar.clone();
        ret.set(Calendar.HOUR_OF_DAY, 0);
        ret.set(Calendar.MINUTE, 0);
        ret.set(Calendar.SECOND, 0);
        ret.set(Calendar.MILLISECOND, 0);
        return ret;
    }
 
    public static Date dayStartUtc(final LocalDate date) {
        final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        calendar.set(Calendar.YEAR, date.getYear());
        calendar.set(Calendar.MONTH, date.getMonthValue() - 1);
        calendar.set(Calendar.DAY_OF_MONTH, date.getDayOfMonth());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
 
    public static long utcDateTimeToLocal(final long timestamp) {
        final Calendar utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        utcCalendar.setTimeInMillis(timestamp);
        final Calendar localCalendar = Calendar.getInstance(TimeZone.getDefault());
        localCalendar.set(Calendar.YEAR, utcCalendar.get(Calendar.YEAR));
        localCalendar.set(Calendar.MONTH, utcCalendar.get(Calendar.MONTH));
        localCalendar.set(Calendar.DAY_OF_MONTH, utcCalendar.get(Calendar.DAY_OF_MONTH));
        localCalendar.set(Calendar.HOUR_OF_DAY, utcCalendar.get(Calendar.HOUR_OF_DAY));
        localCalendar.set(Calendar.MINUTE, utcCalendar.get(Calendar.MINUTE));
        localCalendar.set(Calendar.SECOND, utcCalendar.get(Calendar.SECOND));
        localCalendar.set(Calendar.MILLISECOND, utcCalendar.get(Calendar.MILLISECOND));
        return localCalendar.getTimeInMillis();
    }
 
    public static Date dayEnd(final Date date) {
        final Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }
 
    public static Date parseTimeStamp(int timestamp) {
        GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance();
        cal.setTimeInMillis(timestamp * 1000L); // make sure it's converted to long
        return cal.getTime();
    }
 
    public static Date parseTimestampMillis(long timestamp) {
        GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance();
        cal.setTimeInMillis(timestamp);
        return cal.getTime();
    }
 
    public static String dayToString(Date date) {
        return DAY_STORAGE_FORMAT.format(date);
    }
 
    public static Date dayFromString(String day) throws ParseException {
        return DAY_STORAGE_FORMAT.parse(day);
    }
 
    public static String timeToString(Date date) {
        return HOURS_MINUTES_FORMAT.format(date);
    }
 
    public static String formatTime(int hours, int minutes) {
        return String.format(Locale.US, "%02d", hours) + ":" + String.format(Locale.US, "%02d", minutes);
    }
 
 
    public static Date todayUTC() {
        Calendar cal = getCalendarUTC();
        return cal.getTime();
    }
 
    public static Calendar getCalendarUTC() {
        return GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));
    }
 
    public static String minutesToHHMM(int minutes) {
        return String.format(Locale.US, "%d:%02d", minutes / 60, minutes % 60); // no I do not want to use durationformatter :P
    }
    public static boolean isYesterday(Date d) {
        return DateUtils.isToday(d.getTime() + DateUtils.DAY_IN_MILLIS);
    }
 
    /**
     * Calculates new timestamp with a month offset (positive to add or negative to remove)
     * from a given time
     *
     * @param time
     * @param month
     */
    public static int shiftMonths(int time, int month) {
        Calendar day = Calendar.getInstance();
        day.setTimeInMillis(time * 1000L);
        day.add(Calendar.MONTH, month);
        return (int) (day.getTimeInMillis() / 1000);
    }
 
    /**
     * Calculates new timestamp with a day offset (positive to add or negative to remove)
     * from a given time
     *
     * @param time
     * @param days
     */
    public static int shiftDays(int time, int days) {
        Calendar day = Calendar.getInstance();
        day.setTimeInMillis(time * 1000L);
        day.add(Calendar.DAY_OF_YEAR, days);
        return (int) (day.getTimeInMillis() / 1000);
    }
 
    /**
     * Calculates difference in days between two timestamps
     *
     * @param time1
     * @param time2
     */
    public static int  getDaysBetweenTimes(int time1, int time2) {
        return (int) TimeUnit.MILLISECONDS.toDays((time2 - time1) * 1000L);
    }
 
    /**
     * Determine whether two Calendar instances are on the same day
     *
     * @param calendar1 The first calendar to compare
     * @param calendar2 The second calendar to compare
     * @return true if the Calendar instances are on the same day
     */
    public static boolean isSameDay(Calendar calendar1, Calendar calendar2) {
        return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR)
                && calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH)
                && calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH);
    }
 
    /**
     * Determine whether two Date instances are on the same day
     *
     * @param date1 The first date to compare
     * @param date2 The second date to compare
     * @return true if the Date instances are on the same day
     */
    public static boolean isSameDay(Date date1, Date date2) {
        final Calendar calendar1 = GregorianCalendar.getInstance();
        calendar1.setTime(date1);
        final Calendar calendar2 = GregorianCalendar.getInstance();
        calendar2.setTime(date2);
 
        return isSameDay(calendar1, calendar2);
    }
 
    /**
     * Determine whether two Calendar instances are in the same month
     *
     * @param calendar1 The first calendar to compare
     * @param calendar2 The second calendar to compare
     * @return true if the Calendar instances are in the same month
     */
    public static boolean isSameMonth(Calendar calendar1, Calendar calendar2) {
        return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR)
                && calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
    }
 
    public static String formatDateRelative(final Context context, final Date date) {
        if (DateUtils.isToday(date.getTime())) {
            return context.getString(R.string.activity_summary_today);
        } else if (DateTimeUtils.isYesterday(date)) {
            return context.getString(R.string.activity_summary_yesterday);
        } else {
            return DateTimeUtils.formatDate(date, DateUtils.FORMAT_SHOW_WEEKDAY);
        }
    }
 
    public static String formatDateTimeRelative(final Context context, final Date date) {
        if (date != null) {
            final String activityDay = formatDateRelative(context, date);
            final String activityTime = DateTimeUtils.formatTime(date.getHours(), date.getMinutes());
            return context.getString(R.string.date_placeholders__date__time, activityDay, activityTime);
        }
 
        return context.getString(R.string.unknown);
    }
}