2019-03-22 23:08:13 +01:00
package info.nightscout.androidaps.utils ;
2016-06-05 01:40:35 +02:00
2019-05-16 13:57:37 +02:00
import androidx.collection.LongSparseArray ;
2017-12-22 02:55:40 +01:00
import org.joda.time.DateTime ;
2018-04-01 18:44:58 +02:00
import org.joda.time.format.DateTimeFormat ;
2017-12-22 02:55:40 +01:00
import org.joda.time.format.DateTimeFormatter ;
import org.joda.time.format.ISODateTimeFormat ;
2017-01-03 19:06:35 +01:00
2016-06-05 01:40:35 +02:00
import java.text.DateFormat ;
2019-06-02 16:24:51 +02:00
import java.text.DecimalFormat ;
import java.text.DecimalFormatSymbols ;
2016-06-05 01:40:35 +02:00
import java.text.SimpleDateFormat ;
2016-10-20 23:50:31 +02:00
import java.util.Calendar ;
2016-06-05 01:40:35 +02:00
import java.util.Date ;
2016-10-20 23:50:31 +02:00
import java.util.GregorianCalendar ;
2017-10-02 17:38:45 +02:00
import java.util.Locale ;
2016-06-05 01:40:35 +02:00
import java.util.TimeZone ;
2016-10-20 23:50:31 +02:00
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
2016-06-05 01:40:35 +02:00
2017-01-03 19:06:35 +01:00
import info.nightscout.androidaps.MainApp ;
2017-06-26 12:44:03 +02:00
import info.nightscout.androidaps.R ;
2017-01-03 19:06:35 +01:00
2016-06-05 01:40:35 +02:00
/ * *
* The Class DateUtil . A simple wrapper around SimpleDateFormat to ease the handling of iso date string & lt ; - & gt ; date obj
* with TZ
* /
2016-07-16 22:37:10 +02:00
public class DateUtil {
2016-06-05 01:40:35 +02:00
2016-07-16 22:37:10 +02:00
/ * *
* The date format in iso .
* /
2017-12-22 00:37:22 +01:00
private static String FORMAT_DATE_ISO_OUT = " yyyy-MM-dd'T'HH:mm:ss'Z' " ;
2016-06-05 01:40:35 +02:00
/ * *
* Takes in an ISO date string of the following format :
* yyyy - mm - ddThh : mm : ss . ms + HoMo
*
* @param isoDateString the iso date string
* @return the date
* /
2019-08-05 14:12:39 +02:00
public static Date fromISODateString ( String isoDateString ) {
2017-12-03 17:34:29 +01:00
2017-12-22 02:55:40 +01:00
DateTimeFormatter parser = ISODateTimeFormat . dateTimeParser ( ) ;
DateTime dateTime = DateTime . parse ( isoDateString , parser ) ;
return dateTime . toDate ( ) ;
2016-06-05 01:40:35 +02:00
}
/ * *
* Render date
*
2016-07-16 22:37:10 +02:00
* @param date the date obj
2016-06-05 01:40:35 +02:00
* @param format - if not specified , will use FORMAT_DATE_ISO
2016-07-16 22:37:10 +02:00
* @param tz - tz to set to , if not specified uses local timezone
2016-06-05 01:40:35 +02:00
* @return the iso - formatted date string
* /
2016-07-16 22:37:10 +02:00
public static String toISOString ( Date date , String format , TimeZone tz ) {
2017-12-22 00:37:22 +01:00
if ( format = = null ) format = FORMAT_DATE_ISO_OUT ;
2016-07-16 22:37:10 +02:00
if ( tz = = null ) tz = TimeZone . getDefault ( ) ;
2017-10-02 17:38:45 +02:00
DateFormat f = new SimpleDateFormat ( format , Locale . getDefault ( ) ) ;
2016-06-05 01:40:35 +02:00
f . setTimeZone ( tz ) ;
return f . format ( date ) ;
}
2016-07-16 22:37:10 +02:00
public static String toISOString ( Date date ) {
2017-12-22 00:37:22 +01:00
return toISOString ( date , FORMAT_DATE_ISO_OUT , TimeZone . getTimeZone ( " UTC " ) ) ;
2016-07-16 22:37:10 +02:00
}
2017-12-03 17:34:29 +01:00
2016-07-21 00:18:45 +02:00
public static String toISOString ( long date ) {
2017-12-22 00:37:22 +01:00
return toISOString ( new Date ( date ) , FORMAT_DATE_ISO_OUT , TimeZone . getTimeZone ( " UTC " ) ) ;
2016-07-21 00:18:45 +02:00
}
2016-10-20 23:50:31 +02:00
2019-06-02 16:24:51 +02:00
public static String toISOAsUTC ( final long timestamp ) {
final SimpleDateFormat format = new SimpleDateFormat ( " yyyy-MM-dd'T'HH:mm:ss.SSS'0000Z' " , Locale . US ) ;
format . setTimeZone ( TimeZone . getTimeZone ( " UTC " ) ) ;
return format . format ( timestamp ) ;
}
public static String toISONoZone ( final long timestamp ) {
final SimpleDateFormat format = new SimpleDateFormat ( " yyyy-MM-dd'T'HH:mm:ss " , Locale . US ) ;
format . setTimeZone ( TimeZone . getDefault ( ) ) ;
return format . format ( timestamp ) ;
}
2016-10-20 23:50:31 +02:00
public static Date toDate ( Integer seconds ) {
Calendar calendar = new GregorianCalendar ( ) ;
2018-04-01 18:44:58 +02:00
calendar . set ( Calendar . MONTH , 0 ) ; // Set january to be sure we miss DST changing
2016-10-20 23:50:31 +02:00
calendar . set ( Calendar . HOUR_OF_DAY , seconds / 60 / 60 ) ;
calendar . set ( Calendar . MINUTE , ( seconds / 60 ) % 60 ) ;
calendar . set ( Calendar . SECOND , 0 ) ;
return calendar . getTime ( ) ;
}
public static int toSeconds ( String hh_colon_mm ) {
2019-11-22 10:40:48 +01:00
Pattern p = Pattern . compile ( " ( \\ d+):( \\ d+)( a.m.| p.m.| AM| PM|AM|PM|) " ) ;
2016-10-20 23:50:31 +02:00
Matcher m = p . matcher ( hh_colon_mm ) ;
int retval = 0 ;
if ( m . find ( ) ) {
retval = SafeParse . stringToInt ( m . group ( 1 ) ) * 60 * 60 + SafeParse . stringToInt ( m . group ( 2 ) ) * 60 ;
2019-11-22 10:40:48 +01:00
if ( ( m . group ( 3 ) . equals ( " a.m. " ) | | m . group ( 3 ) . equals ( " AM " ) | | m . group ( 3 ) . equals ( " AM " ) ) & & m . group ( 1 ) . equals ( " 12 " ) )
2017-08-16 19:33:41 +02:00
retval - = 12 * 60 * 60 ;
2019-11-22 10:40:48 +01:00
if ( ( m . group ( 3 ) . equals ( " p.m. " ) | | m . group ( 3 ) . equals ( " PM " ) | | m . group ( 3 ) . equals ( " PM " ) ) & & ! ( m . group ( 1 ) . equals ( " 12 " ) ) )
2017-08-16 19:33:41 +02:00
retval + = 12 * 60 * 60 ;
2016-10-20 23:50:31 +02:00
}
return retval ;
}
2017-01-03 19:06:35 +01:00
public static String dateString ( Date date ) {
2017-01-07 23:26:28 +01:00
DateFormat df = DateFormat . getDateInstance ( DateFormat . SHORT ) ;
return df . format ( date ) ;
2017-01-03 19:06:35 +01:00
}
public static String dateString ( long mills ) {
2017-01-07 23:26:28 +01:00
DateFormat df = DateFormat . getDateInstance ( DateFormat . SHORT ) ;
return df . format ( mills ) ;
2017-01-03 19:06:35 +01:00
}
2019-12-09 19:03:26 +01:00
public static String dateStringShort ( long mills ) {
String format = " MM/dd " ;
if ( android . text . format . DateFormat . is24HourFormat ( MainApp . instance ( ) ) ) {
format = " dd/MM " ;
}
return new DateTime ( mills ) . toString ( DateTimeFormat . forPattern ( format ) ) ;
}
2017-01-03 19:06:35 +01:00
public static String timeString ( Date date ) {
2019-03-31 21:54:31 +02:00
String format = " hh:mma " ;
if ( android . text . format . DateFormat . is24HourFormat ( MainApp . instance ( ) ) ) {
format = " HH:mm " ;
}
return new DateTime ( date ) . toString ( DateTimeFormat . forPattern ( format ) ) ;
2017-01-03 19:06:35 +01:00
}
public static String timeString ( long mills ) {
2019-03-31 21:54:31 +02:00
String format = " hh:mma " ;
if ( android . text . format . DateFormat . is24HourFormat ( MainApp . instance ( ) ) ) {
format = " HH:mm " ;
}
return new DateTime ( mills ) . toString ( DateTimeFormat . forPattern ( format ) ) ;
2017-01-03 19:06:35 +01:00
}
2018-08-17 13:58:06 +02:00
public static String timeFullString ( long mills ) {
return new DateTime ( mills ) . toString ( DateTimeFormat . fullTime ( ) ) ;
}
2017-01-03 19:06:35 +01:00
public static String dateAndTimeString ( Date date ) {
return dateString ( date ) + " " + timeString ( date ) ;
}
2017-12-03 17:34:29 +01:00
2018-04-09 19:52:01 +02:00
public static String dateAndTimeRangeString ( long start , long end ) {
return dateAndTimeString ( start ) + " - " + timeString ( end ) ;
}
2017-01-03 19:06:35 +01:00
public static String dateAndTimeString ( long mills ) {
2019-08-05 14:12:39 +02:00
if ( mills = = 0 ) return " " ;
2017-01-03 19:06:35 +01:00
return dateString ( mills ) + " " + timeString ( mills ) ;
}
2017-06-26 12:44:03 +02:00
2018-08-17 13:58:06 +02:00
public static String dateAndTimeFullString ( long mills ) {
return dateString ( mills ) + " " + timeFullString ( mills ) ;
}
2017-06-26 12:44:03 +02:00
public static String minAgo ( long time ) {
2018-04-01 18:44:58 +02:00
int mins = ( int ) ( ( now ( ) - time ) / 1000 / 60 ) ;
2018-02-07 22:50:58 +01:00
return MainApp . gs ( R . string . minago , mins ) ;
2017-06-26 12:44:03 +02:00
}
2018-06-23 18:24:04 +02:00
public static String minAgoShort ( long time ) {
Integer mins = ( int ) ( ( time - now ( ) ) / 1000 / 60 ) ;
return ( mins > 0 ? " + " : " " ) + mins . toString ( ) ;
}
2018-01-29 19:05:39 +01:00
public static String hourAgo ( long time ) {
2018-04-01 18:44:58 +02:00
double hours = ( now ( ) - time ) / 1000d / 60 / 60 ;
2018-02-07 22:50:58 +01:00
return MainApp . gs ( R . string . hoursago , hours ) ;
2018-01-29 19:05:39 +01:00
}
2017-10-02 19:57:41 +02:00
private static LongSparseArray < String > timeStrings = new LongSparseArray < > ( ) ;
public static String timeStringFromSeconds ( int seconds ) {
String cached = timeStrings . get ( seconds ) ;
if ( cached ! = null )
return cached ;
2018-04-01 18:44:58 +02:00
String t = timeString ( toDate ( seconds ) ) ;
2017-10-02 19:57:41 +02:00
timeStrings . put ( seconds , t ) ;
return t ;
}
2017-12-03 17:34:29 +01:00
public static String timeFrameString ( long timeInMillis ) {
long remainingTimeMinutes = timeInMillis / ( 1000 * 60 ) ;
long remainingTimeHours = remainingTimeMinutes / 60 ;
remainingTimeMinutes = remainingTimeMinutes % 60 ;
2019-11-06 20:31:05 +01:00
return " ( " + ( ( remainingTimeHours > 0 ) ? ( remainingTimeHours + MainApp . gs ( R . string . shorthour ) + " " ) : " " ) + remainingTimeMinutes + " ') " ;
2017-10-29 02:06:06 +02:00
}
2017-12-03 17:34:29 +01:00
public static String sinceString ( long timestamp ) {
return timeFrameString ( System . currentTimeMillis ( ) - timestamp ) ;
2017-10-29 02:06:06 +02:00
}
2017-12-03 17:34:29 +01:00
public static String untilString ( long timestamp ) {
return timeFrameString ( timestamp - System . currentTimeMillis ( ) ) ;
2017-10-29 02:06:06 +02:00
}
2018-03-26 16:51:25 +02:00
public static long now ( ) {
return System . currentTimeMillis ( ) ;
}
2017-10-29 02:06:06 +02:00
2018-04-05 09:39:18 +02:00
public static long roundDateToSec ( long date ) {
return date - date % 1000 ;
}
2018-09-18 20:17:45 +02:00
2019-03-22 23:08:13 +01:00
public static boolean isCloseToNow ( long date ) {
long diff = Math . abs ( date - now ( ) ) ;
return diff < T . mins ( 2 ) . msecs ( ) ;
}
2018-09-18 20:17:45 +02:00
public static GregorianCalendar gregorianCalendar ( ) {
return new GregorianCalendar ( ) ;
}
2019-07-14 20:00:42 +02:00
2019-06-02 16:24:51 +02:00
public static long getTimeZoneOffsetMs ( ) {
return new GregorianCalendar ( ) . getTimeZone ( ) . getRawOffset ( ) ;
}
public static int getTimeZoneOffsetMinutes ( final long timestamp ) {
return TimeZone . getDefault ( ) . getOffset ( timestamp ) / 60000 ;
}
public static String niceTimeScalar ( long t ) {
String unit = MainApp . gs ( R . string . unit_second ) ;
t = t / 1000 ;
if ( t ! = 1 ) unit = MainApp . gs ( R . string . unit_seconds ) ;
if ( t > 59 ) {
unit = MainApp . gs ( R . string . unit_minute ) ;
t = t / 60 ;
if ( t ! = 1 ) unit = MainApp . gs ( R . string . unit_minutes ) ;
if ( t > 59 ) {
unit = MainApp . gs ( R . string . unit_hour ) ;
t = t / 60 ;
if ( t ! = 1 ) unit = MainApp . gs ( R . string . unit_hours ) ;
if ( t > 24 ) {
2019-07-15 12:33:03 +02:00
unit = MainApp . gs ( R . string . unit_day ) + " \" " ;
2019-06-02 16:24:51 +02:00
t = t / 24 ;
2019-07-15 12:33:03 +02:00
if ( t ! = 1 ) unit = MainApp . gs ( R . string . unit_days ) + " \" " ;
2019-06-02 16:24:51 +02:00
if ( t > 28 ) {
2019-07-15 12:33:03 +02:00
unit = MainApp . gs ( R . string . unit_week ) + " \" " ;
2019-06-02 16:24:51 +02:00
t = t / 7 ;
2019-07-15 12:33:03 +02:00
if ( t ! = 1 ) unit = MainApp . gs ( R . string . unit_weeks ) + " \" " ;
2019-06-02 16:24:51 +02:00
}
}
}
}
//if (t != 1) unit = unit + "s"; //implemented plurality in every step, because in other languages plurality of time is not every time adding the same character
return qs ( ( double ) t , 0 ) + " " + unit ;
}
// singletons to avoid repeated allocation
private static DecimalFormatSymbols dfs ;
private static DecimalFormat df ;
public static String qs ( double x , int digits ) {
if ( digits = = - 1 ) {
digits = 0 ;
if ( ( ( int ) x ! = x ) ) {
digits + + ;
if ( ( ( ( int ) x * 10 ) / 10 ! = x ) ) {
digits + + ;
if ( ( ( ( int ) x * 100 ) / 100 ! = x ) ) digits + + ;
}
}
}
if ( dfs = = null ) {
final DecimalFormatSymbols local_dfs = new DecimalFormatSymbols ( ) ;
local_dfs . setDecimalSeparator ( '.' ) ;
dfs = local_dfs ; // avoid race condition
}
final DecimalFormat this_df ;
// use singleton if on ui thread otherwise allocate new as DecimalFormat is not thread safe
if ( Thread . currentThread ( ) . getId ( ) = = 1 ) {
if ( df = = null ) {
final DecimalFormat local_df = new DecimalFormat ( " # " , dfs ) ;
local_df . setMinimumIntegerDigits ( 1 ) ;
df = local_df ; // avoid race condition
}
this_df = df ;
} else {
this_df = new DecimalFormat ( " # " , dfs ) ;
}
this_df . setMaximumFractionDigits ( digits ) ;
return this_df . format ( x ) ;
}
2017-10-29 02:06:06 +02:00
}