Reputation: 11
I am using SimpleDateFormat to format times in Android. The goal is to get a human readable time representation out of a long which contains the time in milliseconds:
var simpleDateFormat = SimpleDateFormat("s.SS")
simpleDateFormat.timeZone = TimeZone.getTimeZone("UTC")
var result = simpleDateFormat.format(timeInMillis)
I have encountered the following very weird bug: the second last digit of timeInMillis is a 7 or an 8 and the digit before is a 5, the formating is wrong, since the result 1/100 of a second too small.
Example:
timeInMillis = 1570
Expected value of result: 1.57
Actual value of result 1.56
Can anyone recreate this bug? And where can I report it?
Upvotes: 1
Views: 218
Reputation: 152787
An explanation for this behavior is that Android SimpleDateFormat uses inaccurate floating point math here:
// BEGIN Android-added: Better UTS#35 conformity for fractional seconds.
case PATTERN_MILLISECOND: // 'S'
// Fractional seconds must be treated specially. We must always convert the parsed
// value into a fractional second [0, 1) and then widen it out to the appropriate
// formatted size. For example, an initial value of 789 will be converted
// 0.789 and then become ".7" (S) or ".78" (SS) or "0.789" (SSS) or "0.7890" (SSSS)
// in the resulting formatted output.
if (current == null) {
value = (int) (((double) value / 1000) * Math.pow(10, count));
zeroPaddingNumber(value, count, count, buffer);
}
break;
// END Android-added: Better UTS#35 conformity for fractional seconds.
For example, the expression (int) (((double) value / 1000) * Math.pow(10, count))
yields 56 for value 570 and count 2.
As pointed out by other answerers, there are better alternatives for datetimes in Android development. For example Java 8 java.time desugaring.
Upvotes: 1
Reputation: 78975
I am not sure how you are trying to do it.
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class Main {
public static void main(String args[]) {
long timeInMillis = 1570;
var simpleDateFormat = new SimpleDateFormat("s.SS");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
var result = simpleDateFormat.format(timeInMillis);
System.out.println(result);
}
}
Output:
1.570
Check it also on ideone.
Note that the date-time API of java.util
and their formatting API, SimpleDateFormat
are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API. Learn more about the modern date-time API at Trail: Date Time.
Note: If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
Using the modern date-time API:
import java.time.Instant;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String args[]) {
long timeInMillis = 1570;
var dtf = DateTimeFormatter.ofPattern("s.SS");
LocalTime time = Instant.ofEpochMilli(timeInMillis).atOffset(ZoneOffset.UTC).toLocalTime();
System.out.println(time.format(dtf));
}
}
Output:
1.57
Upvotes: 0