Java 日期和时间

Java8对日期和时间API进行了重构,受到Joda-Time影响,引入了java.time包。文章介绍了时区、纪元时的概念,以及Date、TimeZone、Locale、Calendar和DateFormat等核心类的使用,包括时间的转换和比较。同时指出了SimpleDateFormat的线程安全问题,并提出了相应解决方案。
摘要由CSDN通过智能技术生成

日期和时间是一个比较复杂的概念,Java 8之前的设计有一些不足,业界有一个广泛使用的第三方类库 Joda-Time,Java 8受Joda-Time影响,重新设计了日期和时间API,新增了一个包java.time。虽然Java 8之前的API有一些不足,但依然是被大量使用的。

基本概念

  • 时区

全球一共有24个时区,英国格林尼治是0时区,北京是东八区,也就是说格林尼治凌晨1点,北京是早上9点。0时区的时间也称为GMT+0时间,GMT是格林尼治标准时间,北京的时间就是GMT+8:00。

  • 时刻和纪元时

所有计算机系统内部都用一个整数表示时刻,这个整数是距离格林尼治标准时间1970年1月1日0时0分0秒的毫秒数。

格林尼治标准时间1970年1月1日0时0分0秒也被称为EpochTime(纪元时)。这个整数表示的是一个时刻,与时区无关,世界各地都是同一个时刻,但对这个时刻的解读(如年月日时分秒)可能是不一样的。

对于1970年以前的时间,使用负数表示。

  • 年历

中国有公历和农历之分,公历和农历都是年历,不同的年历,一年有多少月,每月有多少天,甚至一天有多少小时,这些可能都是不一样的。

比如,公历有闰年,闰年2月是29天,而其他年份则是28天,其他月份,有的是30天,有的是31天。农历有闰月,比如闰7月,一年就会有两个7月,一共13个月。

Date

java.util.Date 表示时刻,内部是一个long类型的值,表示距离纪元时的毫秒数。

private transient long fastTime;
  • 构造方法
public Date(long date) {
	fastTime = date;
}

public Date() {
	this(System.currentTimeMillis());
}
  • 获取毫秒数
public long getTime()
  • 比较
public int compareTo(Date anotherDate)

public boolean before(Date when)

public boolean after(Date when)

TimeZone

java.util.TimeZone 表示时区,是一个抽象类。

  • 获取默认时区
TimeZone tz = TimeZone.getDefault();
System.out.println(tz.getID()); // Asia/Shanghai

java中有一个系统属性user.timezone,保存的就是默认时区。系统属性可以通过System.getProperty获得。

System.out.println(System.getProperty("user.timezone")); //  Asia/Shanghai

系统属性可以在Java启动的时候传入参数进行更改。

java -Duser.timezone=Asia/Shanghai xxx

Locale

java.util.Locale 表示国家(或地区)和语言,有两个主要参数:一个是国家(或地区);另一个是语言,每个参数都有一个代码,不过国家(或地区)并不是必需的。

比如,中国内地的代码是CN,中国台湾地区的代码是TW,美国的代码是US,中文语言的代码是zh,英文语言的代码是en。

  • 获取默认值
Locale locale =  Locale.getDefault();
System.out.println(locale.toString()); // zh_CN

Calendar

java.util.Calendar 表示与TimeZone和Locale相关的日历信息,可以进行各种相关的运算,是一个抽象类。

与Date类似,Calendar内部也有一个表示时刻的毫秒数,此外,Calendar内部还有一个数组,表示日历中各个字段的值。

Calendar会将表示时刻的毫秒数time,按照TimeZone和Locale对应的年历,计算各个日历字段的值,存放在fields数组中,Calendar.get 方法获取的就是fields数组中对应字段的值。

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
    protected long time;
    protected int[] fields;
    // ...
}

获取实例

Calendar是抽象类,不能直接创建对象,它提供了多个静态方法,可以获取Calendar实例。

最终调用的方法需要TimeZone和Locale的,如果没有,则会使用默认值。getInstance方法会根据TimeZone和Locale创建对应的Calendar子类对象,在中文系统中,子类一般是表示公历的GregorianCalendar。

TimeZone和Locale不同,具体的子类可能不同,但都是Calendar。这种隐藏对象创建细节的方式,是计算机程序中一种常见的设计模式,叫工厂模式,getInstance 就是一个工厂方法,它生产对象。

与new Date()类似,新创建的Calendar对象表示的也是当前时间, Calendar对象可以方便地获取年月日等日历信息。

public static Calendar getInstance() {
    return createCalendar(TimeZone.getDefault(),
         Locale.getDefault(Locale.Category.FORMAT));
}

public static Calendar getInstance(TimeZone var0) {
    return createCalendar(var0, Locale.getDefault(Category.FORMAT));
}

public static Calendar getInstance(Locale var0) {
    return createCalendar(TimeZone.getDefault(), var0);
}

public static Calendar getInstance(TimeZone var0, Locale var1) {
    return createCalendar(var0, var1);
}

常用静态变量

Calendar类中定义了一些静态变量,表示 fields 数组中存放的字段。

❑ Calendar.YEAR 表示年份

Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(Calendar.YEAR));

❑ Calendar.MONTH:表示月份,1月是0

System.out.println(calendar.get(Calendar.MONTH));

❑ Calendar.DAY_OF_MONTH:表示日,每月的第一天是1。

System.out.println(calendar.get(Calendar.DAY_OF_MONTH));

❑ Calendar.HOUR_OF_DAY:表示小时,为0~23。

System.out.println(calendar.get(Calendar.HOUR_OF_DAY));

❑ Calendar.MINUTE:表示分钟,为0~59。

System.out.println(calendar.get(Calendar.MINUTE));

❑ Calendar.SECOND:表示秒,为0~59。

System.out.println(calendar.get(Calendar.SECOND));

❑ Calendar.MILLISECOND:表示毫秒,为0~999。

System.out.println(calendar.get(Calendar.MILLISECOND));

❑ Calendar.DAY_OF_WEEK:表示星期几,周日是1,周一是2,周六是7。

System.out.println(calendar.get(Calendar.DAY_OF_WEEK));

设置时间

public final void setTime(Date var1) {
    this.setTimeInMillis(var1.getTime());
}

public void setTimeInMillis(long var1)
public void set(int field, int value)

public final void set(int year,int month,int day)

public final void set(int year,int month,int day,int hour,int minute,int second)

获取时间

public final Date getTime() {
    return new Date(this.getTimeInMillis());
}

public long getTimeInMillis()        

增减时间

❑ add

public void add(int field, int amount)

内部,根据字段设置或修改时间时,Calendar会更新fields数组对应字段的值,但一般不会立即更新其他相关字段或内部的毫秒数的值,不过在获取时间或字段值的时候, Calendar会重新计算并更新相关字段。

❑ roll

public void roll(int field, int amount)

与add方法的区别是,roll方法不影响时间范围更大的字段值。

Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 13);
calendar.set(Calendar.MINUTE, 59);

// 在分钟字段上执行roll方法不会改变小时的值。
calendar.add(Calendar.MINUTE, 3); 

DateFormat

DateFormat 类主要在Date和字符串表示之间进行相互转换。更多时候,使用它的子类 java.text.SimpleDateFormat。

SimpleDateFormat 相比 DateFormat,可以接受一个自定义的模式(pattern)作为参数,这个模式规定了Date的字符串形式。

pattern 中的英文字符a~z和A~Z表示特殊含义,其他字符原样输出:

  • yyyy:表示4位的年。
  • MM:表示月,用两位数表示。
  • dd:表示日,用两位数表示。
  • HH:表示24小时制的小时数,用两位数表示。
  • hh:表示12小时制的小时数,a表示的是上午还是下午。
  • mm:表示分钟,用两位数表示。
  • ss:表示秒,用两位数表示。
  • E:表示星期几。

Date 和字符串形式转换

  • format 格式化 Date 为字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(new Date()));
  • parse 解析字符串日期为 Date
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = "2020-12-12 00:00:00";
try {
	System.out.println(sdf.parse(str));
} catch (ParseException e) {
	e.printStackTrace();
}

局限性

SimpleDateFormat不是线程安全的,DateFormat内部使用了一个Calendar实例对象,多线程同时调用的时候,这个Calendar实例的状态可能会紊乱。

解决这个问题大概有以下方案:

  • 每次使用 DateFormat 都新建一个对象。
  • 使用线程同步。
  • 使用 Java8 的API

参考:《Java 编程的逻辑》马俊昌

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值