SimpleDateFormat在某些*日期*上的小时字符串。怎么样?为什么?

时间:2015-03-19 21:54:53

标签: java android calendar simpledateformat gregorian-calendar

我正在使用SimpleDateFormat将我的GregorianCalendar个实例变成漂亮的字符串。我有一个奇怪的问题,在某些日期 时间字符串被一个关闭,但并非总是如此。

通常情况下,如果我的GregorianCalendar的值为HOUR_OF_DAY=0,MINUTE=11,那么我会收到类似“1:11 AM”的字符串。但随后我更改了YEARMONTHDAY,现在HOUR_OF_DAY=0,MINUTE=11为我提供了字符串“ 2:11 AM ”。

这是我的时间字符串格式化功能(日期时间是GregorianCalendar):

public String toTimeString() {
    Log.i(TAG, "Datetime: "+ datetime.toString() + "Formatted as: " + SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(datetime.getTime()));
    return SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(datetime.getTime());
}

这给了我这些日志:

起初工作良好:

Datetime: java.util.GregorianCalendar[time=1426637512725,areFieldsSet=true,lenient=true,zone=GMT,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=2,WEEK_OF_YEAR=12,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=77,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=3,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=11,SECOND=52,MILLISECOND=725,ZONE_OFFSET=0,DST_OFFSET=0]
Formatted as: 1:11 AM

然后我将月份从3月改为4月(年份和日期也一样),现在改为:

Datetime: java.util.GregorianCalendar[time=1429315912725,areFieldsSet=true,lenient=true,zone=GMT,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=3,WEEK_OF_YEAR=16,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=108,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=3,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=11,SECOND=52,MILLISECOND=725,ZONE_OFFSET=0,DST_OFFSET=0]
Formatted as: 2:11 AM

这怎么可能?

这种一分一秒的行为似乎只发生在未来几个月,而不是本月或最后一个月。 2015年和2016年都很好,但2017年也是问题所在。实际日期似乎在未来一周或更长时间内触发它(无论年份如何)。

我可以在不同的UI小部件中更改年,月和日期,将时间字符串格式从正常切换为逐个和后退。我使用TimePicker控制小时,该TimePicker.OnTimeChangedListener()调用GregorianCalendar上的字符串格式化程序。我不认为它是相关的,因为当月和日设置为某些值时它可以工作,但这是代码以防万一。如您所见,有一些逐个处理,因为HOUR_OF_DAYTimePicker存储为0-23值,private void setUpTimePicker() { timePicker.setCurrentHour(schedule.getHourOfDay() + 1); timePicker.setCurrentMinute(schedule.getMinute()); timePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() { public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { schedule.setHourOfDay(hourOfDay - 1); schedule.setMinute(minute); timeText.setText(schedule.toTimeString()); } }); } 使用值1-24匹配小部件实际显示。我认为我做得对,但很高兴在这里发现问题是错误的。

GregorianCalandar

格式化为字符串的时间总是正确的(0 =凌晨1点,17 = 5点等等,一天中的每个小时)或总是错误的(0 =凌晨2点,17点= 6点,等一天中的每一小时)基于其他日历字段。 一致性使我认为这不是一个线程问题

是否像所有其他类似问题一样时区问题?我怎么也想不到。我的public static TimeZone TIMEZONE = TimeZone.getTimeZone("GMT"); private static GregorianCalendar makeCalendar(){ GregorianCalendar now = (GregorianCalendar) GregorianCalendar.getInstance(TIMEZONE); now.setFirstDayOfWeek(Calendar.SUNDAY); return now; } 实例始终使用此函数创建:

SimpleDateFormat

任何想法都会非常感激!我已经筋疲力尽了我能想到的一切,包括在它上面睡了几天。 提前感谢任何回答的人!!

更新

Jon找到了问题,但由于代码更改隐藏在评论中,我也会将其复制到此处。

问题是GregorianCalendar正在使用系统时区,而不是我假设的给定return SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(datetime.getTime()); 的时区。我通过改变这一行来解决这个问题:

SimpleDateFormat sdf = new SimpleDateFormat("hh:mm a", LOCALE);
        sdf.setTimeZone(TIMEZONE);
        return sdf.format(datetime.getTime());

到此:

+1

然后我从-1中移除了TimePicker和{{1}},它确实有效!

2 个答案:

答案 0 :(得分:2)

您正在设置您正在使用的日历的时区,但这不是您传入SimpleDateFormat的内容 - 您&#39 ;在此处格式化时,只需使用默认的系统时区:

SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(datetime.getTime())

我建议:

  • 理想情况下,从Java 8转到Joda Time或java.timejava.util糟糕。我意识到你正在Android上工作,所以java.time已经出局,但Joda Time是你的选择吗?
  • 将日期选择器排除在等式之外:努力想出一个简短但完整的程序来证明问题 - 只是一个控制台应用程序(比如说)试图在每个月的第一天凌晨5点开始格式化到2019年底。(为简单起见,尝试在Android之外重现它。)
  • 无论何时格式化或解析(或基本上做任何事情),都要准确计算出您感兴趣的时区,并明确指定。 (我建议使用UTC而不是GMT表示"没有偏离UTC"否则有些人会认为你的意思是英国时区。)

答案 1 :(得分:0)

尝试使用此方法

Date date = new Date();    
System.out.println(new SimpleDateFormat("yyyy.MM.dd  HH:mm").format(date));