关于java:SimpleDateFormat和基于语言环境的格式字符串

SimpleDateFormat and locale based format string

我正在尝试基于给定的语言环境以不同的方式在Java中格式化日期。 例如,我希望英国用户看到"2009年11月1日"(由"MMM d,yyyy"格式化)和挪威用户看到"1. nov.2009"("d.MMM.yyyy")。

如果我将语言环境添加到SimpleDateFormat构造函数中,那么月份部分可以正常工作,但其余部分呢?

我希望我可以将与locales配对的格式字符串添加到SimpleDateFormat,但我找不到任何方法来执行此操作。 是否可以或者我需要让我的代码检查语言环境并添加相应的格式字符串?


使用DateFormat.getDateInstance(int style,Locale locale)而不是使用SimpleDateFormat创建自己的模式。


1
2
SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE dd MMM yyyy", Locale.ENGLISH);
String formatted = dateFormat.format(the_date_you_want_here);


使用style + locale:DateFormat.getDateInstance(int style,Locale locale)

查看http://java.sun.com/j2se/1.5.0/docs/api/java/text/DateFormat.html

运行以下示例以查看差异:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

public class DateFormatDemoSO {
  public static void main(String args[]) {
    int style = DateFormat.MEDIUM;
    //Also try with style = DateFormat.FULL and DateFormat.SHORT
    Date date = new Date();
    DateFormat df;
    df = DateFormat.getDateInstance(style, Locale.UK);
    System.out.println("United Kingdom:" + df.format(date));
    df = DateFormat.getDateInstance(style, Locale.US);
    System.out.println("USA:" + df.format(date));  
    df = DateFormat.getDateInstance(style, Locale.FRANCE);
    System.out.println("France:" + df.format(date));
    df = DateFormat.getDateInstance(style, Locale.ITALY);
    System.out.println("Italy:" + df.format(date));
    df = DateFormat.getDateInstance(style, Locale.JAPAN);
    System.out.println("Japan:" + df.format(date));
  }
}

输出:

1
2
3
4
5
United Kingdom: 25-Sep-2017
USA: Sep 25, 2017
France: 25 sept. 2017
Italy: 25-set-2017
Japan: 2017/09/25


TL;博士

1
2
3
4
LocalDate.now().format(
    DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM )
                     .withLocale( new Locale("no" ,"NO" ) )
)

麻烦的java.util.DateSimpleDateFormat类现在已经遗留下来,取而代之的是java.time类。

LocalDate

LocalDate类表示没有时间且没有时区的仅日期值。

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,法国巴黎午夜过后几分钟是新的一天,而在魁北克蒙特利尔仍然是"昨天"。

1
2
ZoneId z = ZoneId.of("America/Montreal" );
LocalDate today = LocalDate.now( z );

DateTimeFormatter

使用DateTimeFormatter生成仅表示日期部分或时间部分的字符串。

DateTimeFormatter类可以自动本地化。

要进行本地化,请指定:

  • FormatStyle确定字符串应该多长或缩写。
  • Locale确定(a)用于翻译日期名称,月份名称等的人类语言,以及(b)决定缩写,大小写,标点符号等问题的文化规范。

例:

1
2
3
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.FULL ).withLocale( l );
String output = ld.format( f );

走向另一个方向,您可以解析本地化的字符串。

1
LocalDate ld = LocalDate.parse( input , f );

请注意,区域设置和时区是完全正交的问题。您可以用日语或以印地语提供的奥克兰新西兰时刻呈现蒙特利尔时刻。

另一个例子:将6 junio 2012(西班牙语)改为2012-06-06(标准ISO 8601格式)。默认情况下,java.time类使用ISO 8601格式来解析/生成字符串。

1
2
3
4
5
String input ="6 junio 2012";
Locale l = new Locale ("es" ,"ES" );
DateTimeFormatter f = DateTimeFormatter.ofPattern ("d MMMM uuuu" , l );
LocalDate ld = LocalDate.parse ( input , f );
String output = ld.toString();  // 2012-06-06.

细读格式

下面是一些示例代码,用于在多个语言环境中仔细阅读多种格式的结果,并自动进行本地化。

EnumSetSet的实现,在收集Enum对象时,针对低内存使用和快速执行速度进行了高度优化。因此,EnumSet.allOf( FormatStyle.class )为我们提供了要循环的所有四个FormatStyle枚举对象的集合。有关更多信息,请参阅枚举类型的Oracle教程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
LocalDate ld = LocalDate.of( 2018 , Month.JANUARY , 23 );

List < Locale > locales = new ArrayList <>( 3 );
locales.add( Locale.CANADA_FRENCH );
locales.add( new Locale("no" ,"NO" ) );
locales.add( Locale.US );

// Or use all locales (almost 800 of them, for about 120K text results).
// Locale[] locales = Locale.getAvailableLocales(); // All known locales. Almost 800 of them.

for ( Locale locale : locales )
{
    System.out.println("------|  LOCALE:" + locale +" —" + locale.getDisplayName() +"  |----------------------------------" + System.lineSeparator() );

    for ( FormatStyle style : EnumSet.allOf( FormatStyle.class ) )
    {
        DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( style ).withLocale( locale );
        String output = ld.format( f );
        System.out.println( output );
    }
    System.out.println("" );
}
System.out.println("? fin ?" + System.lineSeparator() );

输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
------|  LOCALE: fr_CA — French (Canada)  |----------------------------------

mardi 23 janvier 2018
23 janvier 2018
23 janv. 2018
18-01-23

------|  LOCALE: no_NO — Norwegian (Norway)  |----------------------------------

tirsdag 23. januar 2018
23. januar 2018
23. jan. 2018
23.01.2018

------|  LOCALE: en_US — English (United States)  |----------------------------------

Tuesday, January 23, 2018
January 23, 2018
Jan 23, 2018
1/23/18

? fin ?

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗留日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。

您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

  • Java SE 8,Java SE 9及更高版本

    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小功能和修复。
  • Java SE 6和Java SE 7

    • 许多java.time功能都被反向移植到Java 6&amp; 7在ThreeTen-Backport。
  • Android的

    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android,ThreeTenABP项目采用ThreeTen-Backport(如上所述)。请参见如何使用ThreeTenABP ....

ThreeTen-Extra项目使用其他类扩展了java.time。该项目是未来可能添加到java.time的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuarter等。


日期字符串的本地化:

基于redsonic的帖子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private String localizeDate(String inputdate, Locale locale) {

    Date date = new Date();
    SimpleDateFormat dateFormatCN = new SimpleDateFormat("dd-MMM-yyyy", locale);      
    SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");


    try {
        date = dateFormat.parse(inputdate);
    } catch (ParseException e) {
        log.warn("Input date was not correct. Can not localize it.");
        return inputdate;
    }
    return dateFormatCN.format(date);
}

String localizedDate = localizeDate("05-Sep-2013", new Locale("zh","CN"));

将像05-九月-2013


这将根据用户的当前区域设置显示日期:

要返回日期和时间:

1
2
3
4
5
6
import java.text.DateFormat;    
import java.util.Date;

Date date = new Date();
DateFormat df = DateFormat.getDateTimeInstance();
String myDate = df.format(date);

1969年12月31日下午7:00:02

要仅返回日期,请使用:

1
DateFormat.getDateInstance()

1969年12月31日


给定日期的Java 8样式

1
2
3
4
LocalDate today = LocalDate.of(1982, Month.AUGUST, 31);
System.out.println(today.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.ENGLISH)));
System.out.println(today.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.FRENCH)));
System.out.println(today.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.JAPANESE)));


1
String text = new SimpleDateFormat("E, MMM d, yyyy").format(date);

Java8

1
2
 import java.time.format.DateTimeFormatter;        
 myDate.format(DateTimeFormatter.ofPattern("dd-MMM-YYYY",new Locale("ar")))