我想将给定的日历实例的时间戳设置为星期一(星期一),而是返回一个看似完全不相关的时间戳 - 除非我在此之前访问任何日历的字段。我在下面添加了一个示例,另请参阅Ideone中的这个可运行示例。
这是预期的行为吗?这背后的逻辑是什么?是的,我听说过Joda Time。
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
class MyTest {
private static Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"), Locale.FRANCE);
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
public static void main(String[] args) {
// Set to any date.
calendar.set(2013, 10, 3);
System.out.println(dateFormat.format(calendar.getTime()));
// Set to another day.
calendar.set(2014, 0, 15);
// --- THE WTF STARTS HERE ---
// Uncommenting the line below returns the correct date in the end.
// calendar.getTime();
// Set to monday of current week.
calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
// Expected outdate is 20140113
System.out.println(dateFormat.format(calendar.getTime()));
}
}
答案 0 :(得分:5)
文档中的Field Manipulation章节清楚地解释了它。它虽然有点怪异。
http://docs.oracle.com/javase/6/docs/api/java/util/Calendar.html
示例:考虑最初设置为1999年8月31日的GregorianCalendar。调用 set(Calendar.MONTH,Calendar.SEPTEMBER)将日期设置为1999年9月31日。 这是一个临时内部代表,如果是,则解决到1999年10月1日 然后调用getTime()。但是,调用之前设置(Calendar.DAY_OF_MONTH,30) 调用getTime()将日期设置为1999年9月30日,因为没有重新计算 在set()之后。
修改强>
来自同一文档的日历字段解析部分
If there is any conflict in calendar field values, Calendar gives priorities to
calendar fields that have been set more recently. The following are the default
combinations of the calendar fields. The most recent combination, as determined
by the most recently set single field, will be used.
For the date fields:
YEAR + MONTH + DAY_OF_MONTH
YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
YEAR + DAY_OF_YEAR
YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
我认为MONTH和DAY_OF_WEEK之间的差异就是这个。如果在最后一个语句中设置MONTH,则它与YEAR + MONTH + DAY_OF_MONTH匹配并覆盖所有这些语句。如果您设置DAY_OF_WEEK,则它与YEAR + DAY_OF_WEEK + WEEK_OF_YEAR匹配,因此不会覆盖月份值。或类似的东西。说实话,我看起来越多,看起来就越破碎。它完全没有意义。最好继续使用JodaTime
答案 1 :(得分:1)
您可以清除日历,因为它会保留值吗?
Kaya's answer about why this would be relevant
E.g:
calendar.clear();
// Set to another day.
calendar.set(2014, 0, 14);
返回:
20140113 ,而不是 20131223 ,因为你正在获得。
来自文档:
void java.util.Calendar.set(int year,int month,int date)
设置日历字段YEAR,MONTH和DAY_OF_MONTH的值。 保留其他日历字段的先前值。如果不是这样的话 希望,先打电话给clear()。
答案 2 :(得分:0)
LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) // Get the current date for a particular region.
.with(
TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) // Move to an earlier date that is Monday, or stay on same date if it already *is* Monday.
) // Return `LocalDate` object.
Calendar
类现在是遗留的,取而代之的是 java.time 类,特别是ZonedDateTime
。所以问题现在没有实际意义了。
遗留类的问题之一是,每周开始的定义因区域设置而异,以及年,月和星期几的疯狂编号方案。相比之下,java.time默认情况下始终将星期一视为每周的开始,按照ISO 8601标准运行到星期日。而java.time使用理智的编号:
2018
是2018年,1900年没有乱数。LocalDate
LocalDate
类表示没有时间且没有时区的仅限日期的值。
时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜后的几分钟是新的一天,而Montréal Québec中仍然是“昨天”。
如果未指定时区,则JVM会隐式应用其当前的默认时区。该默认值可能随时更改,因此您的结果可能会有所不同。最好明确指定您期望/预期的时区作为参数。
以continent/region
的格式指定proper time zone name,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
如果要使用JVM的当前默认时区,请求它并作为参数传递。如果省略,则隐式应用JVM的当前默认值。最好是明确的,因为默认情况下可以在运行时期间由JVM中任何应用程序的任何线程中的任何代码随时更改。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
或指定日期。您可以将月份设置为一个数字,1月至12月的数字为1-12。
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
或者,更好的是,使用预定义的Month
枚举对象,一年中的每个月一个。提示:在整个代码库中使用这些Month
对象,而不仅仅是整数,以使代码更具自我记录功能,确保有效值,并提供type-safety。
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
要从一个日期移到另一个日期,请使用TemporalAdjuster
课程中的TemporalAdjusters
实施。使用DayOfWeek
枚举指定所需的星期几。
LocalDate previousOrSameMonday = today.with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) ) ;
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。