Java 日期和时间

日期和时间是一个比较复杂的概念,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 编程的逻辑》马俊昌

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • 我们先来看一些基本概念,然后再介绍 Java 的日期和时间 API。关于日期和时间,有一些基本概念,包括时区、时刻...
    acc8226阅读 477评论 1 1
  • Java API中关于日期和时间,有三个主要的类。 ❑ Date:表示时刻,即绝对时间,与年月日无关。 ❑ Cal...
    何佳阳阅读 234评论 0 1
  • 前言   计算机中日期时间是一个很大的概念,现有的系统基本都是利用从1970.1.1 00:00:00 到当前时间...
    小le罗阅读 463评论 0 0
  • java.util 包提供了Date类来封装当前的日期时间。Date类提供两个构造函数来实例化Date对象。 第二...
    南风_001阅读 522评论 0 0
  • Java日期和时间总结 时区:全球一共有24个时区,英国格林尼治是0时区,北京是东八区, 也就是说格林尼治凌晨1点...
    编程人生阅读 205评论 0 1