将日历日期格式化为受时区影响的 dd-MM-yyyy

Format calendar date to dd-MM-yyyy affected by timezone

我在将日历对象转换为可读字符串时遇到了一个大问题。

实际上我使用 df.format(cal.getTime()) 来获取格式化字符串,这不是很好,因为我从 cal.getTime() 获取的 Date 对象是不受时区影响。

请收回任何评论,例如 "use joda time"...

我正在寻找直接从日历对象转换为字符串的解决方案。

这些是我的格式化程序:

  private DateFormat df = new SimpleDateFormat("HH:mm", Locale.GERMANY); // 13:42 Uhr
  private DateFormat df2 = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.GERMANY); // 14.04.2012

如果你们尝试一下这段代码,您会发现 Date 对象不受时区影响。

    //  this date is at wintertime
long milli = 1445945400000l;    //  CET  - central european time  

Calendar cal = Calendar.getInstance();

cal.setTimeInMillis(milli);
System.out.println("  ");
System.out.println("Kalender setTimeInMillis(" + milli + ");");
System.out.println("Daylight Time ? = " + cal.getTimeZone().useDaylightTime());
System.out.println("Date Time = " + cal.getTime());
System.out.println("Kalender Tag = " + cal.get(Calendar.DATE) + "  Monat = " + (cal.get(Calendar.MONTH) + 1) + "  Jahr = " + cal.get(Calendar.YEAR));
System.out.println("Kalender Uhrzeit = " + cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND));

cal.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
System.out.println("  ");
System.out.println("Kalender setTimeZone(TimeZone.getTimeZone(\"Europe/Berlin\");");
System.out.println("Daylight Time ? = " + cal.getTimeZone().useDaylightTime());
System.out.println("Date Time = " + cal.getTime());
System.out.println("Kalender Tag = " + cal.get(Calendar.DATE) + "  Monat = " + (cal.get(Calendar.MONTH) + 1) + "  Jahr = " + cal.get(Calendar.YEAR));
System.out.println("Kalender Uhrzeit = " + cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND));

    //  this date is at summertime
long milliS = 1445609700000l;   //  CEST - central european summertime

Calendar calS = Calendar.getInstance();

calS.setTimeInMillis(milliS);
System.out.println("  ");
System.out.println("Kalender setTimeInMillis(" + milliS + ");");
System.out.println("Daylight Time ? = " + calS.getTimeZone().useDaylightTime());
System.out.println("Date Time = " + calS.getTime());
System.out.println("Kalender Tag = " + calS.get(Calendar.DATE) + "  Monat = " + (calS.get(Calendar.MONTH) + 1) + "  Jahr = " + calS.get(Calendar.YEAR));
System.out.println("Kalender Uhrzeit = " + calS.get(Calendar.HOUR_OF_DAY) + ":" + calS.get(Calendar.MINUTE) + ":" + calS.get(Calendar.SECOND));

calS.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
System.out.println("  ");
System.out.println("Kalender setTimeZone(TimeZone.getTimeZone(\"Europe/Berlin\");");
System.out.println("Daylight Time ? = " + calS.getTimeZone().useDaylightTime());
System.out.println("Date Time = " + calS.getTime());
System.out.println("Kalender Tag = " + calS.get(Calendar.DATE) + "  Monat = " + (calS.get(Calendar.MONTH) + 1) + "  Jahr = " + calS.get(Calendar.YEAR));
System.out.println("Kalender Uhrzeit = " + calS.get(Calendar.HOUR_OF_DAY) + ":" + calS.get(Calendar.MINUTE) + ":" + calS.get(Calendar.SECOND));

输出

Kalender setTimeInMillis(1445945400000);  
Daylight Time ? = false  
Date Time** = Tue Oct 27 11:30:00 GMT 2015  
Kalender Tag = 27  Monat = 10  Jahr = 2015  
Kalender Uhrzeit = 11:30:0  

Kalender setTimeZone(TimeZone.getTimeZone("Europe/Berlin");  
Daylight Time ? = true  
Date Time = Tue Oct 27 11:30:00 GMT 2015  
Kalender Tag = 27  Monat = 10  Jahr = 2015  
Kalender Uhrzeit = 12:30:0  

Kalender setTimeInMillis(1445609700000);  
Daylight Time ? = false  
Date Time = Fri Oct 23 14:15:00 GMT 2015  
Kalender Tag = 23  Monat = 10  Jahr = 2015  
Kalender Uhrzeit = 14:15:0  

Kalender setTimeZone(TimeZone.getTimeZone("Europe/Berlin");  
Daylight Time ? = true  
Date Time = Fri Oct 23 14:15:00 GMT 2015  
Kalender Tag = 23  Monat = 10  Jahr = 2015  
Kalender Uhrzeit = 16:15:0

您需要设置格式化程序的时区,一个简单的例子:

SimpleDateFormat df = new SimpleDateFormat();

Calendar cal1 = Calendar.getInstance(TimeZone.getTimeZone("GMT+4:00"));

df.setTimeZone(cal1.getTimeZone());
System.out.println(df.format(cal1.getTime()));

Calendar cal2 = Calendar.getInstance(TimeZone.getTimeZone("GMT-3:00"));

df.setTimeZone(cal2.getTimeZone());
System.out.println(df.format(cal2.getTime()));

输出:

10/30/15 1:02 PM
10/30/15 6:02 AM

请注意 java.util.Date 对象本身不包含任何时区信息 - 您不能在 Date 对象上设置时区。 Date 对象唯一包含的是自 "epoch" - 1970 年 1 月 1 日 00:00:00 UTC 以来的毫秒数。

日期有自己的时区

令人困惑的是,java.util.Date 被分配了一个使用 JVM 当前默认时区的内部时区。您无法获取或设置该区域。 java.util.Calendar class 已添加到 Java 以处理时区和其他此类问题。

从纪元开始计数

long milli = 1445945400000l; // CET - central european time

通常这样的数字是 UTC(格林威治标准时间)的纪元计数,而不是任何特定时区。从该数字创建日期时间对象后,您可以分配要应用的时区。

提示:在整数文字上使用大写 L 以避免在许多字体中看起来像 1

java.time

这些 classes 现在已经被 Java 8 及更高版本中内置的 java.time 框架淘汰了。新的 classes 更明智且更易于使用。

时区

一个时区不止一个offset-from-UTC. A time zone is an offset plus a set of past, present, and future rules for handling anomalies such as Daylight Saving Time. Use a proper time zone such as Europe/Berlin。避免使用甚至考虑 3-4 个字母的代码,例如 CET & CEST;它们既不是标准化的也不是唯一的。

示例代码

首先我们得到一个 Instant,UTC 时间轴上的一个时刻,来自从纪元开始计数。

long milliSecondsFromEpochInUtc = 1445945400000L;
Instant instant = Instant.ofEpochMilli ( milliSecondsFromEpochInUtc );

然后我们分配所需的时区以获得ZonedDateTime

ZoneId zoneId = ZoneId.of ( "Europe/Berlin" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );

对于输出,格式化程序默认使用 ZonedDateTime 对象的指定时区。

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.MEDIUM ).withLocale ( Locale.GERMANY );
String output1 = zdt.format ( formatter );

如果指定,格式化程序可以选择应用不同的时区。

String output2 = zdt.format ( formatter.withZone ( ZoneId.of ( "America/Montreal" ) ) );

转储到控制台。

System.out.println ( "instant (UTC): " + instant );
System.out.println ( "zdt: " + zdt );
System.out.println ( "Berlin time zone: " + output1 );
System.out.println ( "Montréal time zone: " + output2 );

当运行。请注意,所有这些都代表时间轴上的同一时刻。他们的表达方式各不相同,但都表示同一时刻。

instant (UTC): 2015-10-27T11:30:00Z
zdt: 2015-10-27T12:30+01:00[Europe/Berlin]
Berlin time zone: 27.10.2015 12:30:00
Montréal time zone: 27.10.2015 07:30:00