从日期转换为日历时,日期比实际日期少一天

时间:2015-06-24 17:14:12

标签: java date calendar

我从前端传递日期,即IST(印度时区的日期)。在java代码中,我使用以下代码将日期转换为日历(这发生在美国PST时区的服务器中)。

Calendar cal = Calendar.getInstance();
int offset = date.getTimezoneOffset();
logger.info("Calendar Instance - " + cal);
cal.setTime(date);
logger.info("Calendar Instance after setting date - " + cal);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
logger.info("Calendar Instance after setting zeros - " + cal);
return cal;

所以当我看到最后一个日志时,那个月的日子会比我传递的日子少一天。如果我通过22/06/2015 IST,它将转移到2015年6月21日。所以在处理完毕后,它会在另一个UI页面的数据列表中显示21/06/2015。

2 个答案:

答案 0 :(得分:5)

这是因为服务器端的JVM和客户端的JVM默认使用不同的时区Java TimeZone

  

通常,您使用getDefault获取TimeZone,这会创建一个   TimeZone基于程序运行的时区。对于   例如,对于在日本运行的程序,getDefault会创建一个TimeZone   基于日本标准时间的对象。

我们可以看到,服务器上的Pacific Time Zone UTC-8:00 ,客户端上的Indian Standard Time UTC + 05:30 。它们相差 13.30 ,印度日期X转换为美国为X-13.30,可能会在服务器端为某些X产生前一天。

根据您如何影响/修改服务器和客户端应用程序,可能会有一些变通方法。例如,您可以在服务器端和客户端使用UTC + 00:00时区中的日期。如果您需要向用户显示日期,您可以在需要时将其转换为印度时区。

// Set default GMT+0:00 time zone
TimeZone timeZone;
timeZone = TimeZone.getTimeZone("GMT+0:00");
TimeZone.setDefault(timeZone);

您可以创建"清除"而不是简单地使用Calendar cal = Calendar.getInstance();。您稍后将用户设置日历,月份和年份的日历

public static Calendar createClearedCalendar() {
    Calendar cal = Calendar.getInstance();

    cal.setTimeZone(timeZone);

    cal.set(1970, 0, 1, 0, 0, 0);
    cal.set(Calendar.HOUR_OF_DAY, 0);

    cal.clear(Calendar.MILLISECOND);

    return cal;
}

顺便说一下,如果你在Java中操纵日期时间,你可以考虑Joda Time,它有更多扩展选项和optimized performance

答案 1 :(得分:0)

安东尼奥的

The Answer是正确的,应该被接受(点击大空的复选标记)。

本回答添加了一些想法和示例代码。

避免使用3个字母的时区代码

避免使用或甚至考虑这些3或4个字母代码,例如ISTPST。它们不是标准化的,它们不是唯一的,它们进一步混淆了Daylight Saving Time (DST)周围的问题。例如,IST表示“印度标准时间”,“爱尔兰标准时间”等等。

使用proper time zone names。其中大多数都处于“大陆”+“/”+“城市/地区”模式。城市/地区名称并非专门针对该城镇,而是作为一个易于识别的名称,尽可能广泛的区域,共享time zone rules和异常的过去,现在和未来规则(包括DST)。

使用UTC

通常,您应该使用UTC time zone进行所有业务逻辑,数据存储和数据交换。仅在用户需要时进行演示时调整到特定时区。

使用体面日期时间框架

旧的java.util.Date/.Calendar类是处理日期时间工作的大胆尝试,但最终它们失败了。众所周知,它们在设计和实施方面都很麻烦。避免他们。

第三方Joda-Time库是一种解决方案。它适用于许多Java版本以及Android版本。 Joda-Time启发了另一个解决方案,即Java 8及更高版本(java.time package)中的Tutorial

解决方案

该问题似乎的目标是获取java.util.Date对象,分配所需的时区,并生成java.util.Calendar对象。

幸运的是,java.time框架具有转换方法。请参阅this Tutorial page

示例代码如下,使用Java 8 Update 45中的java.time。

您可能需要导入,例如:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

让我们模拟传入java.util.Date。我们将基于“now”实例化一个Date。

Date inputDate = new Date( );  // Simulate getting a java.util.Date object.

然后我们使用proper time zone names定义所需的时区。让我们一起玩蒙特利尔,以及问题中提到的太平洋美国和印度时区。

ZoneId zoneLosAngeles = ZoneId.of( "America/Los_Angeles" );
ZoneId zoneMontréal = ZoneId.of( "America/Montreal" );
ZoneId zoneKolkata = ZoneId.of( "Asia/Kolkata" );

然后我们将其转换为Instant,即时间轴上的一个点,而不考虑时区。

Instant instant = inputDate.toInstant( );

然后我们分配各种时区来创建ZonedDateTime个实例。了解我们如何以两种方式实例化ZonedDateTime:[a]来自Instant,或[b]来自另一个ZonedDateTime来自withZoneSameInstant方法。两种方式如下所示。

请注意,java.time(和Joda-Time)使用immutable objects,这是一种设计模式,我们根据旧实例创建新实例,而不是改变(“改变”)旧实例。 Thread-safety是主要的好处之一。

ZonedDateTime zdtLosAngeles = ZonedDateTime.ofInstant( instant, zoneLosAngeles );
ZonedDateTime zdtMontréal = ZonedDateTime.ofInstant( instant, zoneMontréal );
ZonedDateTime zdtKolkata = ZonedDateTime.ofInstant( instant, zoneKolkata );
ZonedDateTime zdtUtc = zdtKolkata.withZoneSameInstant( ZoneOffset.UTC );

最后,我们将其中一个转换为GregorianCalendar对象,该对象是java.util.Calendar的子类。

GregorianCalendar calendarKolkata = GregorianCalendar.from( zdtKolkata );

转储到控制台。

System.out.println( "inputDate: " + inputDate );
System.out.println( "zdtLosAngeles: " + zdtLosAngeles );
System.out.println( "zdtMontréal: " + zdtMontréal );
System.out.println( "zdtKolkata: " + zdtKolkata );
System.out.println( "zdtUtc: " + zdtUtc );
System.out.println( "calendarKolkata: " + calendarKolkata );

跑步时。

inputDate: Wed Jun 24 15:12:12 PDT 2015
zdtLosAngeles: 2015-06-24T15:12:12.153-07:00[America/Los_Angeles]
zdtMontréal: 2015-06-24T18:12:12.153-04:00[America/Montreal]
zdtKolkata: 2015-06-25T03:42:12.153+05:30[Asia/Kolkata]
zdtUtc: 2015-06-24T22:12:12.153Z
calendarKolkata: java.util.GregorianCalendar[time=1435183932153,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Kolkata",offset=19800000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2015,MONTH=5,WEEK_OF_YEAR=26,WEEK_OF_MONTH=4,DAY_OF_MONTH=25,DAY_OF_YEAR=176,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=4,AM_PM=0,HOUR=3,HOUR_OF_DAY=3,MINUTE=42,SECOND=12,MILLISECOND=153,ZONE_OFFSET=19800000,DST_OFFSET=0]