在使用java日历类时一开始总是出现一点小错误后来发现原因在于设置月份错误
因为Calendar类设置月份时需要将你想要计算的月份减去1因为它的月份的范围是0到11
没注意到这一点我以为是其它错误就去看了它的源码,虽然最后发现是这样一个小错误
但是借机小小研究了一下源码也是不亏吧
Calendar类是一个抽象类不能被实例化所以Calendar.java里提供了一个getInstance方法
我们来看一下这个方法
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(),
Locale.getDefault(Locale.Category.FORMAT));
}
这里符号没有错是逗号运算符,其含义是逗号隔开的各个表达式都会被计算,但是只返回最后一个表达式的值。
这两个方法都很见闻名义第一个就是返回一个创建好的calendar实例(当然不能是calendar本身)第二个是与从java虚拟机
中获取指定类别默认值的方法我想大概就是获取一些默认配置(我点进去看了一下这个format是一些关于用户的配置)
当然我们这里最主要的是createCalendar方法,点进去看一下
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
这里重点是这块
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
cal是一个Calendar的对象句柄
上面那段代码就表示了这里是实例化是一个子类对象赋给父类的过程(即多态的一种体现)
buddhist是佛教的应该就是佛教的日历,japane应该是代表日本的历法(不知道为什么要给日本的历法单独写一个子类
我中华大农历呢,,)GregorianCalendar是公历
所以我们发现到这一步Calendar的实例化基本是完成了有的小伙伴可能觉得这个Calendar类这么方便那它是怎么计算日期或者
其他属性的呢我们接着往下看
如果你想要设置你Calendar对象的日期,这里你可以使用Calendar类自带的set方法
public final void set(int year, int month, int date)
{
set(YEAR, year);
set(MONTH, month);
set(DATE, date);
}
当然set方法不止这一种Calendar类中重载了多个set方法方便大家使用
这里设置时记住month的值要减去1,设置完了我们看他是怎么计算的,Calendar类获取某一个属性时用的是get方法
比如获取该天是星期几
Calendar calendar = Calendar.getInstance();
calendar.get(Calendar.DAY_OF_WEEK);
这里的Calendar.DAY_OF_WEEK是Calendar类一个不可被修改的常量,点进去get方法
public int get(int field)
{
complete();
return internalGet(field);
}
return internalGet(field)是返回所要获取的域的值就如该天是星期几,complete方法就是一个计算日期及相应属性的方法
点进去看到一个updatetime方法再点进去,看到一个computeTime方法这个就是实际计算日期的方法
再点进去
protected abstract void computeTime();
发现这是一个抽象类,所以其实Calendar不提供实际计算日期的方法它是提供一个抽象方法供子类重写
这里我贴一下公历子类的computeTime方法
protected void computeTime() {
// In non-lenient mode, perform brief checking of calendar
// fields which have been set externally. Through this
// checking, the field values are stored in originalFields[]
// to see if any of them are normalized later.
if (!isLenient()) {
if (originalFields == null) {
originalFields = new int[FIELD_COUNT];
}
for (int field = 0; field < FIELD_COUNT; field++) {
int value = internalGet(field);
if (isExternallySet(field)) {
// Quick validation for any out of range values
if (value < getMinimum(field) || value > getMaximum(field)) {
throw new IllegalArgumentException(getFieldName(field));
}
}
originalFields[field] = value;
}
}
// Let the super class determine which calendar fields to be
// used to calculate the time.
int fieldMask = selectFields();
// The year defaults to the epoch start. We don't check
// fieldMask for YEAR because YEAR is a mandatory field to
// determine the date.
int year = isSet(YEAR) ? internalGet(YEAR) : EPOCH_YEAR;
int era = internalGetEra();
if (era == BCE) {
year = 1 - year;
} else if (era != CE) {
// Even in lenient mode we disallow ERA values other than CE & BCE.
// (The same normalization rule as add()/roll() could be
// applied here in lenient mode. But this checking is kept
// unchanged for compatibility as of 1.5.)
throw new IllegalArgumentException("Invalid era");
}
// If year is 0 or negative, we need to set the ERA value later.
if (year <= 0 && !isSet(ERA)) {
fieldMask |= ERA_MASK;
setFieldsComputed(ERA_MASK);
}
// Calculate the time of day. We rely on the convention that
// an UNSET field has 0.
long timeOfDay = 0;
if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
timeOfDay += (long) internalGet(HOUR_OF_DAY);
} else {
timeOfDay += internalGet(HOUR);
// The default value of AM_PM is 0 which designates AM.
if (isFieldSet(fieldMask, AM_PM)) {
timeOfDay += 12 * internalGet(AM_PM);
}
}
timeOfDay *= 60;
timeOfDay += internalGet(MINUTE);
timeOfDay *= 60;
timeOfDay += internalGet(SECOND);
timeOfDay *= 1000;
timeOfDay += internalGet(MILLISECOND);
// Convert the time of day to the number of days and the
// millisecond offset from midnight.
long fixedDate = timeOfDay / ONE_DAY;
timeOfDay %= ONE_DAY;
while (timeOfDay < 0) {
timeOfDay += ONE_DAY;
--fixedDate;
}
// Calculate the fixed date since January 1, 1 (Gregorian).
calculateFixedDate: {
long gfd, jfd;
if (year > gregorianCutoverYear && year > gregorianCutoverYearJulian) {
gfd = fixedDate + getFixedDate(gcal, year, fieldMask);
if (gfd >= gregorianCutoverDate) {
fixedDate = gfd;
break calculateFixedDate;
}
jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
} else if (year < gregorianCutoverYear && year < gregorianCutoverYearJulian) {
jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
if (jfd < gregorianCutoverDate) {
fixedDate = jfd;
break calculateFixedDate;
}
gfd = jfd;
} else {
jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
gfd = fixedDate + getFixedDate(gcal, year, fieldMask);
}
// Now we have to determine which calendar date it is.
// If the date is relative from the beginning of the year
// in the Julian calendar, then use jfd;
if (isFieldSet(fieldMask, DAY_OF_YEAR) || isFieldSet(fieldMask, WEEK_OF_YEAR)) {
if (gregorianCutoverYear == gregorianCutoverYearJulian) {
fixedDate = jfd;
break calculateFixedDate;
} else if (year == gregorianCutoverYear) {
fixedDate = gfd;
break calculateFixedDate;
}
}
if (gfd >= gregorianCutoverDate) {
if (jfd >= gregorianCutoverDate) {
fixedDate = gfd;
} else {
// The date is in an "overlapping" period. No way
// to disambiguate it. Determine it using the
// previous date calculation.
if (calsys == gcal || calsys == null) {
fixedDate = gfd;
} else {
fixedDate = jfd;
}
}
} else {
if (jfd < gregorianCutoverDate) {
fixedDate = jfd;
} else {
// The date is in a "missing" period.
if (!isLenient()) {
throw new IllegalArgumentException("the specified date doesn't exist");
}
// Take the Julian date for compatibility, which
// will produce a Gregorian date.
fixedDate = jfd;
}
}
}
// millis represents local wall-clock time in milliseconds.
long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
// Compute the time zone offset and DST offset. There are two potential
// ambiguities here. We'll assume a 2:00 am (wall time) switchover time
// for discussion purposes here.
// 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
// can be in standard or in DST depending. However, 2:00 am is an invalid
// representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
// We assume standard time.
// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
// can be in standard or DST. Both are valid representations (the rep
// jumps from 1:59:59 DST to 1:00:00 Std).
// Again, we assume standard time.
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
TimeZone zone = getZone();
if (zoneOffsets == null) {
zoneOffsets = new int[2];
}
int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
if (zone instanceof ZoneInfo) {
((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
} else {
int gmtOffset = isFieldSet(fieldMask, ZONE_OFFSET) ?
internalGet(ZONE_OFFSET) : zone.getRawOffset();
zone.getOffsets(millis - gmtOffset, zoneOffsets);
}
}
if (tzMask != 0) {
if (isFieldSet(tzMask, ZONE_OFFSET)) {
zoneOffsets[0] = internalGet(ZONE_OFFSET);
}
if (isFieldSet(tzMask, DST_OFFSET)) {
zoneOffsets[1] = internalGet(DST_OFFSET);
}
}
// Adjust the time zone offset values to get the UTC time.
millis -= zoneOffsets[0] + zoneOffsets[1];
// Set this calendar's time in milliseconds
time = millis;
int mask = computeFields(fieldMask | getSetStateFields(), tzMask);
if (!isLenient()) {
for (int field = 0; field < FIELD_COUNT; field++) {
if (!isExternallySet(field)) {
continue;
}
if (originalFields[field] != internalGet(field)) {
String s = originalFields[field] + " -> " + internalGet(field);
// Restore the original field values
System.arraycopy(originalFields, 0, fields, 0, fields.length);
throw new IllegalArgumentException(getFieldName(field) + ": " + s);
}
}
}
setFieldsNormalized(mask);
}
至此我们Calendar类的解析到此结束希望对大家有所帮助
最后附上一个小知识点你使用Calendar类计算某天是星期几时要注意它返回的1是指星期天,2是指星期一
往后推7是指星期六,所以你要小小的改变一下取出来的值才能取到正确的某天是星期几