I am trying to build a chat app and I want to store date for every message in Firebase RealTime-Database using ServerValue.TIMESTAMP
which stores date and time in milliseconds and it is server sided. All I want to achieve is to convert milliseconds to a safe date format which will have a pattern like "Thursday, 11th November 2022, 14:42" but I read a lot of things about Java's unsafe date and format libraries. My app uses FirebaseUI which is like an asynchronous way to observe/notify/listen to changes of my db so I need something that will be safe and supported from API/SDK level 21
Here is my code that I currently have
Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(model.sentOnDateInMilliseconds); String date = DateFormat.getDateInstance(DateFormat.FULL).format(calendar.getTime());
and I make attempts to preview the format that I want like this
textView.setText(MessageFormat.format("{0} {1}:{2} {3}", date, calendar.get(Calendar.HOUR), calendar.get(Calendar.MINUTE), calendar.get(Calendar.AM_PM)));
any suggestions?
CodePudding user response:
Avoid legacy classes
Never use SimpleDateFormat
, Calendar
, or either Date
class. These legacy classes are terribly flawed with poor design decisions by people who did not understand date-time handling.
Their modern replacements are the java.time classes defined in JSR 310.
java.time
You are using terrible date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310.
Though not documented clearly, apparently a firebase.database.ServerValue.TIMESTAMP
type represents a moment as a count of milliseconds since the epoch reference of the first moment of 1970 in UTC, 1970-01-01T00:00:00Z.
java.time.Instant
The matching class in Java is java.time.Instant
.
Instant instant = Instant.ofEpochMilli( millisSinceEpoch ) ;
You can extract that millisecond count from an Instant
. Beware of data loss: A Instant
has a resolution of nanoseconds, so getting a count of milliseconds ignores the microseconds/nanoseconds.
Instant instant = Instant.now() ; // Capture the current moment as seen with an offset of zero hours-minutes-seconds from UTC.
long millisSinceEpoch = instant.toEpochMilli() ;
java.time.ZonedDateTime
convert milliseconds to a safe date format which will have a pattern like "Thursday, 11th November 2022, 14:42"
To generate text in formats other than standard ISO 8601, we need a class more flexible than Instant
. And we need to adjust that moment from an offset of zero hours-minutes-seconds from UTC to a time zone (or offset) expected by the user.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
java.time.format.DateTimeFormatter
To generate text, we must specify a format. You can hard-code a format, or you can let java.time automatically localize. To localize, we specify a Locale
to determine the human language and cultural norms needed for localization such as name of day of week.
Locale locale = Locale.UK ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG ).withLocale( locale ) ;
String output = zdt.format( f ) ;
Dump to console.
System.out.println( "instant = " instant );
System.out.println( "millisSinceEpoch = " millisSinceEpoch );
System.out.println( "output = " output );
When run.
instant = 2022-11-18T19:27:22.561715Z
millisSinceEpoch = 1668799642561
output = Saturday, November 19, 2022 at 4:27:22 AM Japan Standard Time
Android 26 carries an implementation of the java.time classes. For earlier Android, the latest tooling provides access to most of the java.time functionality via “API desugaring”.