我有一个定义为TIMESTAMP WITH TIME ZONE
的字段。
要保存的值以"09-23-2019 10:03:11 pm"
区域中的US/Hawaii
开头。
这就是我要保存到DB(所有日期信息加区域)
数据库以UTC格式存储时间信息。
截至目前,日期存储在数据库中,因此它看起来像这样:
DAYS
---------------------------------------------------------------------------
23-SEP-19 10.03.11.000000 PM -05:00
23-SEP-19 10.03.11.000000 PM -05:00
在处理过程中,它会运行以下代码:
dateStr: the date (as seen above)
ZoneLoc: 'US/Hawaii'
public Calendar convDateStrWithZoneTOCalendar(String dateStr,
String ZoneLoc) throws Exception {
// convert the string sent in from user (which uses AM/PM) to one that uses military time (24HR)
// it
String formattedDate = null;
DateFormat readFormat = new SimpleDateFormat(this.getPattern());
DateFormat writeFormat = new SimpleDateFormat("MM-dd-yyyy'T'HH:mm:ss'Z'");
writeFormat.setTimeZone(TimeZone.getTimeZone(ZoneLoc));
Date date = null;
date = readFormat.parse(dateStr);
formattedDate = writeFormat.format(date);
// see if you can parse the date needed WITH the TimeZone
Date d;
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone(ZoneLoc));
d = sdf.parse(formattedDate);
Calendar cal = Calendar.getInstance();
cal.setTime(d);
system.out.println(" ZONELOC VALUE " + ZoneLoc);
system.out.println(" RETURNED VALUE " + cal );
return cal;
}
返回的日历信息是:
ZONELOC价值是美国/夏威夷
返回值是 java.util.GregorianCalendar中[时间= 1577678591000,areFieldsSet =真,areAllFieldsSet =真,宽大=真,区= sun.util.calendar.ZoneInfo [ID = “美国/芝加哥”,偏移= -21600000,dstSavings = 3600000,useDaylight =真,过渡= 235,lastRule = java.util.SimpleTimeZone中[ID =美国/芝加哥,偏移= -21600000,dstSavings = 3600000,useDaylight =真,startYear = 0,STARTMODE = 3,startMonth = 2,朝九特派= 8, startDayOfWeek = 1,开始时间= 7200000,startTimeMode = 0,endMode = 3,endMonth = 10,endday指定= 1,一个endDayOfWeek = 1,结束时间= 7200000,endTimeMode = 0]],Firstdayofweek可= 1,minimalDaysInFirstWeek = 1,ERA = 1, YEAR = 2019,月= 11,WEEK_OF_YEAR = 1,WEEK_OF_MONTH = 5,DAY_OF_MONTH = 29,DAY_OF_YEAR = 363,DAY_OF_WEEK = 1,DAY_OF_WEEK_IN_MONTH = 5,AM_PM = 1,HOUR = 10,HOUR_OF_DAY = 22,MINUTE = 3,SECOND = 11,微差= 0,ZONE_OFFSET = -21600000,DST_OFFSET = 0]
看起来美国/夏威夷没有被设置为返回值。
我该怎么做才能确保设置好?
之后,我可以将它放在数据库中,看看设置是否“坚持”而不是恢复为America/Chicago
更新 @Patrick H - 感谢输入。我使用您指定的模式进行了更改,并且能够保存数据。它现在看起来像这样:
2017-08-02 13:38:49 TRACE ohtype.descriptor.sql.BasicBinder - 绑定参数[26] as [TIMESTAMP] - [java.util.GregorianCalendar [time = 1569294191000,areFieldsSet = true,areAllFieldsSet =真,宽松=真,区= sun.util.calendar.ZoneInfo [ID = “美国/芝加哥”,偏移= -21600000,dstSavings = 3600000,useDaylight =真,过渡= 235,lastRule = java.util.SimpleTimeZone中[ID =美国/芝加哥,偏移= -21600000,dstSavings = 3600000,useDaylight =真,startYear = 0,STARTMODE = 3,startMonth = 2,朝九特派= 8,startDayOfWeek = 1,开始时间= 7200000,startTimeMode = 0,endMode = 3, endMonth = 10,endday指定= 1,一个endDayOfWeek = 1,结束时间= 7200000,endTimeMode = 0]],Firstdayofweek可= 1,minimalDaysInFirstWeek = 1,ERA = 1,YEAR = 2019,MONTH = 8,WEEK_OF_YEAR = 39,WEEK_OF_MONTH = 4, DAY_OF_MONTH = 23,DAY_OF_YEAR = 266,DAY_OF_WEEK = 2,DAY_OF_WEEK_IN_MONTH = 4,AM_PM = 1,HOUR = 10,HOUR_OF_DAY = 22,MINUTE = 3,SECOND = 11,微差= 0,ZONE_OFFSET = -21600000,DST_OFFSET = 3600000]]
数据库中的数据如下所示:
23-SEP-19 10.03.11.000000 PM -05:00
即使指定America/Chicago
,区域仍为US/Hawaii
。如何让US/Hawaii
坚持而不是恢复到America/Chicago
?
答案 0 :(得分:2)
根据这个输出:
java.util.GregorianCalendar[time=1569294191000,...
上面的时间值(表示自unix时期(1970-01-01T00:00Z
)以来1569294191000毫秒)相当于芝加哥中的09-23-2019 10:03 PM
。那是因为readFormat
正在使用系统的默认时区(可能是America/Chicago
,只需检查TimeZone.getDefault()
的值)。
要解析输入09-23-2019 10:03:11 pm
并将其视为夏威夷的当地时间,您只需将相应的时区设置为SimpleDateFormat
实例(在本例中为readFormat
,因为它需要知道输入日期在什么时区 - 因为你没有设置任何,它使用系统的默认值)。您也不需要其他格式化程序(writeFormat
和sdf
),只能使用一个格式化程序来获取相应的日期:
SimpleDateFormat parser = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss a");
// the input is in Hawaii timezone
parser.setTimeZone(TimeZone.getTimeZone("US/Hawaii"));
Date date = parser.parse("09-23-2019 10:03:11 pm");
上面的date
相当于夏威夷的10:03 PM。 实际上,日期本身只包含unix纪元(date.getTime()
返回1569312191000)和has no format nor any timezone information 的毫秒数。
然后,您可以将其设置为Calendar
个实例(不要忘记设置日历的时区):
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("US/Hawaii"));
cal.setTime(date);
自从我使用带有时区类型的oracle时间戳以来已经有一段时间了,但我认为这将是enough to save the correct values。日历的价值是:
java.util.GregorianCalendar中[时间= 1569312191000,areFieldsSet =真,areAllFieldsSet =真,宽大=真,区= sun.util.calendar.ZoneInfo [ID = <强> “US /夏威夷”下,偏移量= -36000000,dstSavings = 0,useDaylight =假,过渡= 7,lastRule =空],Firstdayofweek可= 1,minimalDaysInFirstWeek = 1,ERA = 1,YEAR = 2019,MONTH = 8,WEEK_OF_YEAR = 39,WEEK_OF_MONTH = 4, DAY_OF_MONTH = 23,DAY_OF_YEAR = 266,DAY_OF_WEEK = 2,DAY_OF_WEEK_IN_MONTH = 4,的 AM_PM = 1,HOUR = 10,HOUR_OF_DAY = 22,MINUTE = 3,SECOND = 11 下,微差= 0,ZONE_OFFSET = - 36000000,DST_OFFSET = 0]
旧类(Date
,Calendar
和SimpleDateFormat
)有lots of problems和design issues,它们将被新API取代。< / p>
其中一个主要问题是使用不同的时区是多么困难和困惑。
如果您使用的是 Java 8 ,请考虑使用new java.time API。它更容易,less bugged and less error-prone than the old APIs。
如果您使用的是 Java&lt; = 7 ,则可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。对于 Android ,有ThreeTenABP(更多关于如何使用它here)。
以下代码适用于两者。
唯一的区别是包名称(在Java 8中为java.time
,在ThreeTen Backport(或Android的ThreeTenABP)中为org.threeten.bp
),但类和方法名称是相同的
要解析输入09-23-2019 10:03:11 pm
,您可以使用DateTimeFormatter
并将其解析为LocalDateTime
- 输入没有时区信息,因此我们只考虑日期和时间,然后我们可以将它转换为时区。
// parse the input
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// parse AM/PM and am/pm
.parseCaseInsensitive()
// input pattern
.appendPattern("MM-dd-yyyy hh:mm:ss a")
// use English locale for am/pm symbols
.toFormatter(Locale.ENGLISH);
LocalDateTime dt = LocalDateTime.parse("09-23-2019 10:03:11 pm", fmt);
// convert to Hawaii timezone
ZonedDateTime hawaiiDate = dt.atZone(ZoneId.of("US/Hawaii"));
最新的JDBC驱动程序支持新的API(但我猜测只适用于Java 8),但如果您仍然需要使用Calendar
,则可以轻松地将ZonedDateTime
转换为它:
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("US/Hawaii"));
calendar.setTimeInMillis(hawaiiDate.toInstant().toEpochMilli());
在Java 8中,您也可以这样做:
Calendar calendar = GregorianCalendar.from(hawaiiDate);
如果您需要与旧的Calendar
和Date
API进行互操作,则可以在内部使用新API进行计算,并在需要时从/转换为API。
答案 1 :(得分:1)
根据SimpleDateFormat,我认为你的格式化字符串是错误的。您还可以在返回的值中看到月份和日期是错误的。 MONTH=11,DAY_OF_MONTH=29
这是你现在拥有的:
23-SEP-19 10.03.11.000000 PM -05:00
我认为格式化字符串应为:'dd-MMM-yy hh.mm.ss.SSSSSS a Z'
看起来时区问题可能是因为里面有一个冒号。 SimpleDateFormat的文档表明它需要采用这种格式而不是RFC 822时区:-0500
您可能会发现更容易使用常规时区组件。