我有要求
为实现这一目标,我已在下面的代码段中编码,但是没有产生所需的结果。它存储的值 10/11/2017 4:05 ,然而,当向美国呈现时,即获得价值/刷新页面时,它又增加了5个小时。删除了异常和其他不必要的代码以简化:
public class DatetoString implements Serializable
{
private final DateFormat dateFormatter = createDateFormatter();
// Sets Date to model
public void setTypedValue(final Object val)
{
final String dateValue;
String dateTimeFormat = BooleanUtils.isFalse(getUSDateFormatConfig()) ? "dd/MM/yyyy HH:mm" : "MM/dd/yyyy HH:mm";
DateFormat df = new SimpleDateFormat(dateTimeFormat);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
Date singleDate = (Date) df.parse(val.toString());
dateValue = dateFormatter.format(singleDate);
model.setValue(dateValue.toString());
// Other code..
}
// Retrieves date from model
public Object getTypedValue()
{
final Object result;
String dateValue = model.iterator().next().getValue();
String dateTimeFormat = BooleanUtils.isFalse(getUSDateFormatConfig()) ? "dd/MM/yyyy HH:mm" : "MM/dd/yyyy HH:mm";
DateFormat df = new SimpleDateFormat(dateTimeFormat);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
Date singleDate = (Date) df.parse(dateValue);
result = dateFormatter.format(singleDate);
return result;
}
private DateFormat createDateFormatter()
{
final DateFormat result = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
result.setTimeZone(TimeZone.getTimeZone("GMT"));
return result;
}
}
答案 0 :(得分:5)
你正在使用可怕的旧日期时间类,这些类很麻烦,容易混淆,而且设计很差。它们现在已经遗留下来,取而代之的是java.time类。避免使用Date
,Calendar
,SimpleDateFormat
等。
CST
你的意思是中央标准时间还是中国标准时间?
以continent/region
的格式指定proper time zone name,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Chicago" );
如果时区对您的工作至关重要,您必须确认其输入所针对的区域。有一些方法可以猜测区域或检测默认值,但重要的是,使区域成为数据输入的一部分以及日期和时间。您可以从中选择present a list,或让他们输入字符串名称。
Locale
的同上(下文讨论)。你可以猜,但如果关键,请问。
以GMT时区保存并检索日期(日期应转换为字符串)。因此,如果用户保存日期10/10/2017 23:05,那么将保存为10/11/2017 4:05(例如,如果在CST时间保存为5小时,则保存在DB中)。
使用LocalDate
将用户输入解析为LocalTime
和DateTimeFormatter
。
在实际工作中,您可以添加try-catch来捕获错误用户输入引发的DateTimeParseException
。
DateTimeFormatter fDate = DateTimeFormatter.ofPattern( "MM/dd/uuuu" ) ;
LocalDate ld = LocalDate.parse( inputDate , f ) ;
DateTimeFormatter fTime = DateTimeFormatter.ISO_LOCAL_TIME ;
LocalTime lt = LocalTime.parse( inputTime , f ) ;
合并,并指定时区以获取ZonedDateTime
对象。
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
根据定义提取始终为UTC的Instant
来调整为UTC。同一时刻,时间轴上的相同点,但通过不同的挂钟镜头观看。
Instant instant = zdt.toInstant() ;
在TIMESTAMP WITH TIME ZONE
类型的列中保留您的数据库。另一种类型WITHOUT
忽略任何时区或从UTC信息偏移,并且绝对是不您想要跟踪实际时刻的内容。
myPreparedStatement.setObject( … , instant ) ;
从数据库中检索。
Instant instant = myResultSet.getObject( … , Instant.class ) ;
在检索和向UI显示日期时,CST用户应显示为10/10/2017 23:05。
调整到用户期望/期望的任何时区。
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ; // Or "America/Chicago" or "America/Winnipeg" etc.
ZonedDateTime zdt = instant.atZone( z ) ;
此外,需要验证一个功能,以了解日期是否需要以美国/非美国日期格式显示(dd / MM / YYYY与mm / DD / YYYY)。
同样,在生成表示该时刻的文本时,会自动使用用户期望/期望的Locale
进行本地化。
要进行本地化,请指定:
FormatStyle
确定字符串的长度或缩写。Locale
确定(a)翻译日期名称,月份名称等的人类语言,以及(b)决定缩写,大写,标点符号,分隔符等问题的文化规范示例:
Locale l = Locale.FRANCE ; // Or Locale.US etc.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG ).withLocale( l ) ;
String output = zdt.format( f ) ;
请注意Locale
和时区是正交的,不相关的和分开的。您可以在摩洛哥有一位讲法语的职员跟踪客户在印度的交货情况。因此,该时刻以UTC格式存储在加拿大服务器上运行的数据库中,在数据库和UTC中的其他组件之间进行交换,调整为印度时区以解决客户接收传递的角度,并本地化为法语供用户阅读摩洛哥。
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
答案 1 :(得分:2)
我全心全意地同意Basil Bourque’s thorough and very knowledgeable answer。您的格式是旧的,与使用旧的和过时的日期和时间类无关。使用现代代码会导致代码更自然地出现,并且更容易避免像您所问的那样的问题。还要使用格式region / city中的时区名称,并注意JVM的默认时区设置可能在运行时由同一JVM中运行的其他程序更改。
编辑:我不想通过从一开始就提供代码来破坏它,但现在你已经解决了你的问题,对于任何阅读的人来说,这里是:
private static final DateTimeFormatter storeFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss");
private static final DateTimeFormatter usDisplayFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm");
private static final DateTimeFormatter internationalDisplayFormatter
= DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
private ZoneId userTimeZone = ZoneId.of("America/Rosario");
/** Sets Date to model */
public void setTypedValue(final Object val)
{
DateTimeFormatter parseFormatter = isUSDateFormatConfig()
? usDisplayFormatter : internationalDisplayFormatter;
final String dateValue = LocalDateTime.parse(val.toString(), parseFormatter)
.atZone(userTimeZone)
.withZoneSameInstant(ZoneOffset.UTC)
.format(storeFormatter);
model.setValue(dateValue);
// Other code..
}
/** Retrieves date from model */
public Object getTypedValue()
{
String dateValue = model.iterator().next().getValue();
DateTimeFormatter displayFormatter = isUSDateFormatConfig()
? usDisplayFormatter : internationalDisplayFormatter;
final Object result = LocalDateTime.parse(dateValue, storeFormatter)
.atOffset(ZoneOffset.UTC)
.atZoneSameInstant(userTimeZone)
.format(displayFormatter);
return result;
}
我致电setTypedValue("10/29/2017 21:30")
,日期时间存储为10/30/2017 00:30:00
。我能够将其检索为美国的10/29/2017 21:30
和外部的29/10/2017 21:30
。
目前我已将用户的时区硬编码为America/Rosario
,以演示 region / city 格式的使用。您可以使用userTimeZone
代替ZoneId.systemDefault()
变量,但正如我所说,在同一JVM中运行的其他程序可能会在您的脚下改变。
如果您希望对用户界面进行现代化改造,可以使用DateTimeFormatter.ofLocalizedDateTime()
代替硬编码显示格式,如Basil Bourque所述。
在我看来,在问题的代码中,您在setTypedValue
和getTypedValue
中进行了类似的转换。你不应该做相反的转换吗?我认为在getTypedValue
中你应该使用dateFormatter
(最终实例变量)从GMT解析,然后使用格式化程序使用本地时区(不是GMT)进行格式化。
小点:
df.parse()
的返回值,因为已经声明该方法返回Date
。toString()
上致电dateValue
,因为它已被宣布为String
,因此呼叫将再次返回相同的String
。