Calendar
- 获取当前时间
- SimpleDateFormat
- 获取年月日等
- 设置特定时间、时区
- 日期的计算(加减)
- 计算日期差
- 计算某年二月有几天
- Calendar常用方法合集
- 夏令时是什么
简介: Java中,Calendar是一个抽象类,所以这个类是不能通过new来直接实现的,并且调用getInstance后y一般返回的是Calendar的子类:GregorianCalendar。
附getInstance中private static Calendar createCalendar(TimeZone zone,Locale aLocale)部分代码
if (cal == null) {
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;
获取当前时间
Calendar calendar=Calendar.getInstance();
calendar.getTime();
调用抽象类中的静态方法getInstance来返回一个当前的calendar对象。Calendar对象较大较复杂,所以使用的是单例模式,使用单例模式的好处是每次都返回同一个对象,这样保证了每次返回都是同一个对象
源码
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
如何规范输出日期的格式
Date date=c.getTime();
SimpleDateFormat sdf2=new SimpleDateFormat("yyyy年MM月dd分hh时ss分mm秒");
String strTime=sdf2.format(date);
System.out.println(strTime);
yyyy:年 MM:月 dd:日 hh:1~12小时制(1-12) HH:24小时制(0-23) mm:分 ss:秒 S:毫秒 E:星期几 D:一年中的第几天 F:一月中的第几个星期(会把这个月总共过的天数除以7) w:一年中的第几个星期 W:一月中的第几星期(会根据实际情况来算) a:上下午标识 k:和HH差不多,表示一天24小时制(1-24)。 K:和hh差不多,表示一天12小时制(0-11)。 z:表示时区
解析Calendar生成方式:通过时区(TimeZone)和区域(Local)来生成时间,因为不同时区有时间差。并且Calendar对象是可变的,可以通过设置时区、区域、年月日来改变Calendar对象中存储的值。 举例:跨国业务中,美国的消费者下单时间和服务器中接收到订单的时间不一样,这里就需要使用到时区和地区的知识(想知道怎么做继续往下看)
获取年月日
Calendar calendar=Calendar.getInstance();
calendar.getTime();
System.out.println("年:"+calendar.get(calendar.YEAR));
System.out.println("一个月的第几周:"+calendar.get(calendar.WEEK_OF_MONTH));
System.out.println("一周的第几天:"+calendar.get(calendar.DAY_OF_WEEK));
System.out.println("早为0,晚为1(以中午12点为界限):"+calendar.get(Calendar.AM_PM));
System.out.println("一天的第几个小时:"+calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("秒:"+calendar.get(Calendar.SECOND));
System.out.println("注意:周日是第一天,序号为1:"+calendar.get(Calendar.SUNDAY));
field | 说明 |
---|
public final static int ERA = 0; | 公元前BC(0) 公元后(1) | public final static int YEAR = 1; | 年 | public final static int MONTH = 2; | 日期 | public final static int WEEK_OF_YEAR = 3; | 一年的第几周 | public final static int WEEK_OF_MONTH = 4; | 一个月的第几周 | public final static int DATE = 5; | 一个月的第几天 | public final static int DAY_OF_MONTH = 6; | 一个月的第几天 | public final static int DAY_OF_WEEK = 7; | 一周的第几天 | public final static int AM_PM = 9; | 特别注意:1是日 早上or下午AM:0 PM:1,以中午十二点为界 | public final static int HOUR = 10; | 小时 | public final static int HOUR_OF_DAY = 11; | 一天的第几个小时 | public final static int MINUTE = 12; | 分钟 | public final static int SECOND = 13; | 秒 | public final static int MILLISECOND = 14; | 毫秒 | public final static int ZONE_OFFSET = 15; | 指示与 GMT 的原始偏移量(以毫秒为单位) | public final static int DST_OFFSET = 16; | 指示以毫秒为单位的夏令时偏移量 | public final static int FIELD_COUNT = 17; | 字段数量 |
设置特定时间、时区
Calendar calendar=Calendar.getInstance();
calendar.getTime();
calendar.set(Calendar.YEAR,2022);
calendar.set(Calendar.MONTH,5);
calendar.set(Calendar.DAY_OF_MONTH,2);
System.out.println(calendar.getTime());
calendar.set(1990,3,5);
System.out.println(calendar.getTime());
时区有哪些
for (String timezone: TimeZone.getAvailableIDs()) {
System.out.println(timezone);
}
点击跳转至:世界时区
时区有什么用:不同时区的时间不一样,假如平台有海外业务,如果在存入数据库的时候没有考虑时区问题,即数据库只保存年月日,那么可能会发生2021.10.1 18:00:00服务器接收到了订单,并且立刻数据库中存入一笔时间2021.10.1 19:00:00的订单记录,这里订单时间相当于变成"一小时后的下单记录时间",所以我们要考虑转换时区来保证数据库时间存储的正确性。(虽然可以在数据库中加入timezone这个字段,但是可能会麻烦一些)
不同时区的时间转换
public class TimeZoneTransform {
private static String dateTransformBetweenTimeZone(Date sourceDate, DateFormat formatter,
TimeZone sourceTimeZone, TimeZone targetTimeZone) {
Long targetTime = sourceDate.getTime() - sourceTimeZone.getRawOffset() + targetTimeZone.getRawOffset();
return getTime(new Date(targetTime), formatter);
}
private static String getTime(Date date, DateFormat formatter) {
return formatter.format(date);
}
private static String getTimeZone() {
Calendar cal = Calendar.getInstance();
int timeZone = cal.getTimeZone().getOffset(System.currentTimeMillis()) / (3600000);
if (timeZone >= 0) {
return String.valueOf("+" + timeZone);
}
return String.valueOf(timeZone);
}
public static String getGMTTime(Date date, SimpleDateFormat formatter) {
TimeZone srcTimeZone = TimeZone.getTimeZone("GMT" + getTimeZone());
TimeZone destTimeZone = TimeZone.getTimeZone("GMT+8");
return dateTransformBetweenTimeZone(date, formatter, srcTimeZone, destTimeZone);
}
public static void main(String[] args) {
System.out.println(getGMTTime(new Date(System.currentTimeMillis()), new SimpleDateFormat()));
}
}
Set有延迟性 set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。 通俗的来说就是
public void set(int field, int value)
{
if (areFieldsSet && !areAllFieldsSet) {
computeFields();
}
internalSet(field, value);
isTimeSet = false;
areFieldsSet = false;
isSet[field] = true;
stamp[field] = nextStamp++;
if (nextStamp == Integer.MAX_VALUE) {
adjustStamp();
}
}
看到这里可能你会很懵,不知道它的延迟性可能会引发什么情况,下面来说一个情况,首先你给calendar设置了9月31号(不报错并且会存储,当调用上述几种方法后会计算然后变成十月一日),此时假如调用了上述几种方法后,设置为五日,就变成了十月五日;假如没调用那几种方法,再设置五日,就是九月五日。 简要来说:假如你阴差阳错把天数设置超过范围,并且你设置的月份是你认为正确的,此时假如你调用了get等方法,那么calendar会把日期计算成一个系统认为合法的日期(月份和天数可能都不是你想要的)。此时如果你只修改了日期,那么你月份仍然是错误的。
cal.setLenient(true);
cal.set(2021,8,31);
cal.set(Calendar.DATE,10);
System.out.println(cal.getTime());
日期的计算(加减)
Calendar calendar=Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.YEAR,10);
calendar.add(Calendar.MONTH,-2);
其他的字段操作自己探索就好了
计算日期差
public int getDaysBetween (Calendar d1, Calendar d2){
if (d1.after(d2)){
java.util.Calendar swap = d1;
d1 = d2;
d2 = swap;
}
int days = d2.get(Calendar.DAY_OF_YEAR) - d1.get(Calendar.DAY_OF_YEAR);
int y2 = d2.get(Calendar.YEAR);
if (d1.get(Calendar.YEAR) != y2){
d1 = (Calendar) d1.clone();
do{
days += d1.getActualMaximum(Calendar.DAY_OF_YEAR);
d1.add(Calendar.YEAR, 1);
} while (d1.get(Calendar.YEAR) != y2);
}
return days;
}
这里有个大佬讲的很全
获取某年二月有几天
虽然获取某年二月有几天很容易,但是这里仅是提供一个思路
calendar.set(Calendar.YEAR,2021);
calendar.set(Calendar.MONTH,1);
System.out.println(calendar.getTime());
System.out.println(calendar.getActualMaximum(Calendar.DATE));
下面来解释getActualMaximum方法:返回当前日期时,该字段的最大值 还有另一个长得很像的方法getMaximum:用于获取此Calendar的给定字段的最大值,例如使用getMaximum(Calendar.DATE),那么返回的是31,因为十二个月中DATE最大是31
常见方法
上面涉及到的不在写了
| |
---|
abstract void add(int field, int amount) | 根据日历的规则,为给定的日历字段添加或减去指定的时间量。 | boolean after(Object when) | 判断此 Calendar 表示的时间是否在指定 Object 表示的时间之后,返回判断结果。 | boolean before(Object when) | 判断此 Calendar 表示的时间是否在指定 Object 表示的时间之前,返回判断结果。 | void clear() | 将此 Calendar 的所日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义。 | void clear(int field) | 将此 Calendar 的给定日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义。 | int compareTo(Calendar anotherCalendar) | 比较两个 Calendar 对象表示的时间值(从历元至现在的毫秒偏移量)。 | protected void complete() | 填充日历字段中所有未设置的字段。 | boolean equals(Object obj) | 将此 Calendar 与指定 Object 比较。 | int get(int field) | 返回给定日历字段的值。 | int getActualMaximum(int field) | 给定此 Calendar 的时间值,返回指定日历字段可能拥有的最大值。 | int getActualMinimum(int field) | 给定此 Calendar 的时间值,返回指定日历字段可能拥有的最小值。 | static Locale[] getAvailableLocales() | 返回所有语言环境的数组,此类的 getInstance 方法可以为其返回本地化的实例。 | int getFirstDayOfWeek() | 获取一星期的第一天 | Date getTime() | 返回一个表示此 Calendar 时间值(从历元至现在的毫秒偏移量)的 Date 对象。 | long getTimeInMillis() | 返回此 Calendar 的时间值,以毫秒为单位。 | boolean isLenient() | 判断日期/时间的解释是否为宽松的。 | boolean isSet(int field) | 确定给定日历字段是否已经设置了一个值,其中包括因为调用 get 方法触发内部字段计算而导致已经设置该值的情况。 | abstract void roll(int field, boolean up) | 在给定的时间字段上添加或减去(上/下)单个时间单元,不更改更大的字段。 | void roll(int field, int amount) | 向指定日历字段添加指定(有符号的)时间量,不更改更大的字段。 | void setFirstDayOfWeek(int value) | 设置一星期的第一天是哪一天 | void setLenient(boolean lenient) | 指定日期/时间解释是否是宽松的。 | void setTime(Date date) | 使用给定的 Date 设置此 Calendar 的时间。 | void setTimeInMillis(long millis) | 用给定的 long 值设置此 Calendar 的当前时间值。 | void setTimeZone(TimeZone value) | 使用给定的时区值来设置时区。 |
宽松模式:是否允许字段的值超过范围,即设置HOUR=25就是明天的1点 注意:当 Calendar 处于 non-lenient 模式时,如果其日历字段中存在任何不一致性,它都会抛出一个异常。
calendar.setLenient(true);
calendar.set(Calendar.YEAR,2021);
calendar.set(Calendar.MONTH,13);
System.out.println(calendar.getTime());
roll和add的区别: 2021.9.12对MONTH字段-10,会是roll:2021.10.12, add:2020.10.12 roll:对字段MONTH回滚时,只回滚该字段,不更改其更大的字段。 add:对字段MONTH回滚时,超出范围时,会更新其更大的字段
在宽松模式下也可以通过add方法来计算两个日期之间的时间差
什么是夏令时
什么是夏令时?
通俗简单的讲:在夏天日光充裕的时候,人们为了充分利用日光时间人为的将时钟拨快了一个小时。 出发点和目的都很简单:夏天亮的早,白天时间长,调整为夏令时促使人们早睡早起,以充分利用光照资源,从而节约照明用电。 例如:晚上两点的时候,时间调到三点,那么这天你会少睡一小时,这样到了晚上,那么你会提前一小时睡觉,那么就省了电。 有关Calendar的夏令时部分请看这位大佬的博客 添加链接描述
|