Calendar类是一个抽象类,它提供了在特定时间点与一组日历字段(如YEAR,MONTH,DAY_OF_MONTH,HOUR等)之间进行转换的方法,以及用于操作日历字段的方法,例如获取下周的时间。 即时时间可以用毫秒值来表示,该值是从1970年1月1日00:00:00.000 GMT(格林威治时间)的偏移。
该类还提供了在包外部实现具体日历系统的附加字段和方法。这些字段和方法被定义为受保护的。
与其他语言环境敏感的类一样,Calendar提供了一个类方法getInstance,用于获取此类型的通用对象。
日历的getInstance方法返回一个Calendar对象,该对象的日历字段已用当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
日历对象用所有日历字段值可以生成实现某国语言和日历样式的日期时间格式(例如,Japanese-Gregorian,Japanese-Traditional)。日历定义了某些日历字段返回的值的范围及其含义。 例如,日历系统的第一个月对所有日历的值为MONTH == JANUARY。 其他值由具体的子类定义,如ERA。 有关详细信息,请参阅个别字段文档和子类文档。
日历字段值可以通过调用set方法来设置。 在日历中设置的任何字段值将不会被解释,直到它需要计算其时间值(从Epoch毫秒)或日历字段的值。 调用get,getTimeInMillis,getTime,add和roll会涉及这样的计算。
日历有两种解释日历字段的模式,lenient and non-lenient。当日历处于lenient模式时,它会接受比其生成的更广范围的日历字段值。 当日历重新计算日历字段值以便get()返回时,所有日历字段都将被标准化。 例如,宽松的GregorianCalendar将MONTH == JANUARY,DAY_OF_MONTH == 32解释为2月1日。
当日历处于non-lenient模式时,如果日历字段中存在任何不一致,则会引发异常。 例如,GregorianCalendar总是生成1和月份长度之间的DAY_OF_MONTH值。 如果任何超出范围的字段值已设置,则non-lenient的GregorianCalendar将在计算其时间或日历字段值时引发异常。
FirstWeek
日历使用两个参数定义特定于区域的特定七天:一周的第一天和第一周的最小日期(从1到7)。 这些数字是在构建日历时从地区资源数据中获取的。 它们也可以通过设置其值的方法明确指定。
设置或获取WEEK_OF_MONTH或WEEK_OF_YEAR字段时,日历必须确定月份或年份的第一周作为参考点。 一个月或一年的第一周被定义为从getFirstDayOfWeek()开始的最早的七天期间,并且该月份或年份至少包含getMinimalDaysInFirstWeek()天。 在第一周之前,编号为......,-1,0的周; 编号2,3,...的周数跟随它。请注意,get()返回的规范化编号可能不同。例如,特定的日历子类可以指定一年的第1周之前一周作为上一年的第n周。
Calendar Fields Resolution
从日历字段计算日期和时间时,可能没有足够的信息进行计算(例如只有年和月而不是一天中的某一天),或者可能存在不一致的信息(例如1996年7月15日星期二(格里高利 ) - 1996年7月15日实际上是一个星期一)。 日历将通过以下方式解析日历字段值以确定日期和时间。
如果日历字段值存在冲突,则日历会为最近设置的日历字段赋予优先级。 以下是日历字段的默认组合。 将使用由最近设置的单个字段确定的最近的组合。
对于日期字段:
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
对于一天的时间领域:
HOUR_OF_DAY AM_PM + HOUR
如果有任何日历字段的值未在所选字段组合中设置,日历将使用其默认值。 每个字段的默认值可能因具体日历系统而异。 例如,在GregorianCalendar中,字段的缺省值与Epoch的开始时间相同:即YEAR = 1970,MONTH = JANUARY,DAY_OF_MONTH = 1等。
在某些单数时间的解释中存在某些可能的含糊之处,可以通过以下方式解决:
1、23:59是当天的最后一分钟,00:00是第二天的第一分钟。 因此,于1999年12月31日23时59分<2000年1月1日00时00分<2000年1月1日00时01分。
2、尽管历史上不确切,但午夜也属于“上午”,而中午属于“下午”,所以在同一天中午12:00(午夜)<上午12:01和中午12:00(中午)<12 :下午1点
日期或时间格式字符串不是日历定义的一部分,因为这些字符串在运行时必须可由用户修改或覆盖。使用DateFormat格式化日期。
操作字段
日历字段可以使用三种方法更改:set(),add()和roll()。
set(f,value)将日历栏f更改为值。另外,它设置一个内部成员变量来指示日历字段f已被更改。虽然日历字段f会立即更改,但在下一次调用get(),getTime(),getTimeInMillis(),add()或roll()之前,不会重新计算日历的时间值(以毫秒为单位)。因此,多次调用set()不会触发多次不必要的计算。 由于使用set()更改日历字段,其他日历字段也可能会更改,具体取决于日历字段,日历字段值和日历系统。另外,在重新计算日历字段后,get(f)不一定会返回调用set方法所设置的值。 细节由具体的日历类决定。
例如:考虑最初设置为1999年8月31日的GregorianCalendar。调用集(Calendar.MONTH,Calendar.SEPTEMBER)将日期设置为1999年9月31日。这是一个临时的内部表示,如果getTime()被调用,则解析为1999年10月1日。但是,在调用getTime()之前set(Calendar.DAY_OF_MONTH,30)的调用会将日期设置为1999年9月30日,因为set()本身之后不会发生重新计算。
add(f,delta)增加字段f的增量。这相当于调用set(f,get(f)+ delta)进行两次调整:
添加规则1:调用之后的字段f的值减去调用之前的字段f的值是增量,以模块f中出现的任何溢出为模。 当字段值超出其范围时会发生溢出,因此,下一个较大的字段会递增或递减,并将字段值调整回其范围。
添加规则2:如果一个较小的字段预计是不变的,但由于场f变化或其他约束(例如时间区偏移变化)后其最小值或最大值发生变化,它不可能等于其先前值,则其值 调整为尽可能接近其预期值。 较小的字段代表较小的时间单位。 HOUR是比DAY_OF_MONTH小的字段。 没有对不期望不变的小字段进行调整。 日历系统确定哪些字段预期是不变的。
另外,与set()不同,add()强制立即重新计算日历的毫秒数和所有字段。
例如:考虑最初设置为1999年8月31日的GregorianCalendar。调用add(Calendar.MONTH,13)将日历设置为2000年9月30日。添加规则1将MONTH字段设置为9月,因为向8月添加13个月会给9月 下一年。 由于DAY_OF_MONTH在GregorianCalendar中的9月份不能为31,所以添加规则2将DAY_OF_MONTH设置为30,即最接近的可能值。 尽管这是一个较小的字段,但DAY_OF_WEEK未按规则2进行调整,因为在GregorianCalendar中月份发生更改时预计会发生更改。
roll(f,delta)将字段f添加delta而不更改较大的字段。这相当于通过以下调整调用add(f,delta):
roll规则。调整结束后,较大的字段保持不变。较大的字段代表较大的时间单位。 DAY_OF_MONTH是比HOUR更大的字段。
示例:请参阅GregorianCalendar.roll(int,int)。
用法模型。 为了激发add()和roll()的行为,请考虑一个用户界面组件,其中包含月份,日期和年份的增量和减量按钮以及基础的GregorianCalendar。 如果界面显示为1999年1月31日,并且用户按下月份增量按钮,那么它应该读取什么? 如果底层实现使用set(),它可能会在1999年3月3日读取。更好的结果是1999年2月28日。此外,如果用户再次按下月份增量按钮,则应该读取1999年3月31日,而不是3月28日 ,通过保存原始日期并使用add()或roll(),取决于是否应该影响较大的字段,用户界面可以像大多数用户直观地预期的那样行为。