Domanda

I am attempting to get a formatted date (year, month, date) and time (hour, minute, second) string according to the user's settings. This post in the Android Developers Google Group describes the precise problem I am having, but no one helped that person solve it. I will summarize it:

Android has these classes that attempt to do the above, but none use both the user preferences and display the seconds.

  1. java.text.DateFormat
    Doesn't use preferences set in Settings app

  2. android.text.format.DateFormat
    Gets a java.text.DateFormat object that formats output correctly using getDateFormat() and getTimeFormat(), but getTimeFormat() doesn't include seconds.

  3. android.text.format.DateUtils
    Doesn't use preferences set for showing date in Settings app and no way to display seconds.

For example, with preferences in Settings set to DD/MM/YYYY and 24-hour format = on, running the following code:

long timestamp=System.currentTimeMillis();

sometextview.setText(
    java.text.format.DateFormat.getDateTimeInstance().format(new Date(timestamp)) 

    +"\n"+ 

    android.text.format.DateFormat.getDateFormat(this).format(new Date(timestamp))
    +" "+
    android.text.format.DateFormat.getTimeFormat(this).format(new Date(timestamp))

    +"\n"+

    android.text.format.DateUtils.formatDateTime(this,
                                                 timestamp,    
                                                 DateUtils.FORMAT_SHOW_DATE| 
                                                 DateUtils.FORMAT_SHOW_TIME|
                                                 DateUtils.FORMAT_SHOW_YEAR)
);

gives me the following output in the textview:

Apr 27,2014 5:47:18 PM
27/04/2014 17:47
April 27,2014, 17:47

None gives the desired output, which would be something like this using the aforementioned preferences:

27/04/2014 17:47:18 

I've looked into the joda-time-android library as well, but it doesn't seem to do what I need (correct me if I'm wrong).

TL;DR: How do you format date/time according to user preferences with seconds on Android?

È stato utile?

Soluzione

Using @I wish I could think of a good's suggestion, I made the following code that formats date using locale and user settings:

public static String timeDateStringFromTimestamp(Context applicationContext,long timestamp){
    String timeDate;
    String androidDateTime=android.text.format.DateFormat.getDateFormat(applicationContext).format(new Date(timestamp))+" "+
            android.text.format.DateFormat.getTimeFormat(applicationContext).format(new Date(timestamp));
    String javaDateTime = DateFormat.getDateTimeInstance().format(new Date(timestamp));
    String AmPm="";
    if(!Character.isDigit(androidDateTime.charAt(androidDateTime.length()-1))) {
        if(androidDateTime.contains(new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.AM])){
            AmPm=" "+new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.AM];
        }else{
            AmPm=" "+new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.PM];
        }
        androidDateTime=androidDateTime.replace(AmPm, "");
    }
    if(!Character.isDigit(javaDateTime.charAt(javaDateTime.length()-1))){
        javaDateTime=javaDateTime.replace(" "+new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.AM], "");
        javaDateTime=javaDateTime.replace(" "+new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.PM], "");
    }
    javaDateTime=javaDateTime.substring(javaDateTime.length()-3);
    timeDate=androidDateTime.concat(javaDateTime);
    return timeDate.concat(AmPm);
}

Altri suggerimenti

Try this:

long timestamp = System.currentTimeMillis();

DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(getApplicationContext());
DateFormat timeFormat = DateFormat.getTimeInstance();

String currentTimeString =  dateFormat.format(timestamp) + " - " +            
       timeFormat.format(timestamp);

Why not use a hybrid solution

android.text.format.DateFormat.getDateFormat(this).format(new Date(timestamp));

and

java.text.format.DateFormat.getDateTimeInstance().format(new Date(timestamp))

For getDateTimeInstance save it as a string and extract the seconds, and then just append it to the getDateFormat

A bit of a hack but this seems to work:

Date date = ...;
String dateStr = DateFormat.getLongDateFormat(context).format(date);
String timeStr;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
  String timeFormat = DateFormat.getBestDateTimePattern(Locale.getDefault(), DateFormat.is24HourFormat(context) ? "Hms" : "hmsa");
  timeStr = new SimpleDateFormat(timeFormat, Locale.getDefault()).format(date);
}
else {
  String timeFormat = DateFormat_getTimeFormatString(context).replace("mm", "mm:ss");
  timeStr = new SimpleDateFormat(timeFormat, Locale.getDefault()).format(date);
}
return dateStr + " " + timeStr;

It relies on a hidden function in `DateFormat˙ that we can use via reflection:

private static String DateFormat_getTimeFormatString(Context context) {
  try {
    final Method method = DateFormat.class.getDeclaredMethod("getTimeFormatString");
    method.setAccessible(true);
    return (String) method.invoke(context);
  }
  catch (Exception ignored) {
    return "HH:mm";
  }
}

From API 18 onwards you can use the new getBestDateTimePattern() function that only needs a list of what data you want to display and it returns the best matching pattern.

If you look into the is24HourFormat() source code, it's a bit insane (it checks whether there is an uppercase H in the user preferred pattern string), so it might be worth to store its value instead of calling it repeatedly if you need to display many dates.

Of course, you can decide what format you need for the date by calling other instances or using DateUtils.formatDateTime().

One warning, though. Make sure that you use an activity context to ask for is24HourFormat(). Application contexts will not do.

tl;dr

ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )
             .format( DateTimeFormatter.ofPattern( "dd/MM/uuuu HH:mm:ss" ) )

23/01/2017 12:34:56

java.time

Add the ThreeTenABP project (see below) to access the modern date-time classes of the java.time framework. The old legacy date-time classes are notoriously troublesome, confusing, and flawed.

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" );  // Or ZoneId.systemDefault()
ZonedDateTime zdt = ZonedDateTime.now( z );

Use DateTimeFormatter to generate a string representing the value of that ZonedDateTime object. Specify a custom formatting pattern, or call the toLocalized… methods to automatically localize to the user’s human language and cultural norms.

DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu HH:mm:ss" ) ; 
String output = zdt.format( f );

23/01/2017 12:34:56


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?


Joda-Time

Update: The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

Joda-Time easily formats date-time values in a Locale-sensitive manner. Look at the DateTimeFormat class.

DateTimeFormatter formatter = DateTimeFormat.forStyle( "SM" );
DateTime dateTime = DateTime.now();
String output = formatter.print( dateTime );

Play around with that two letter sequence to get the format you desire. The first character is the date style, and the second character is the time style. Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full. A date or time may be ommitted by specifying a style character '-'.

Locale

A Locale controls the formatting according to cultural norms and controls the language used to translate name of day, name of month, and so on.

If you do not specify a Locale, the JVM’s current default Locale is silently applied. As this default can change at an moment before or even during runtime, I suggest you specify the desired/expected locale. If you do indeed want the current default, I suggest specifying that as well with a call to Locale.getDefault so as to make your code self-documenting.

formatter = formatter.withLocale( Locale.CANADA_FRENCH );
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top