【Java常用类】1-8 Date, SimpleDateFormat 和 Calendar

§1-8 Date, SimpleDateFormatCalendar

1-8.1 JDK 7 前的时间相关类

在 JDK 7 以前,Java API 中所提供的时间相关类如下所示

简要描述
Date 时间
SimpleDateFormat 格式化时间
Calendar 日历

其中,DateCalendar 都位于 java.util 包下,而 SimpleDateFormat 继承自抽象类 DateFormat,后者也继承自抽象类 Format,全部位于 java.text 包下。

有关时间的小知识

  1. 全世界的时间,拥有一个统一的计算标准;
  2. 1884 年,将格林尼治时间(GMT, Greenwich Mean Time)认为是世界标准时间;
  3. GMT 的计算核心:将一天划分为 24 小时,太阳直射时为正午 12 时;
  4. 位于本初子午线东侧的地区为东部地区,时间基于 GMT 相加;位于子午线西侧的地区为西部地区,时间基于 GMT 相减;
  5. 共有 24 个时区,其中包含 23 个整时区和 2 个半时区,中国位于东八区,即 GMT+8;
  6. 由于地球自转速度不均,当前时刻与实际时刻存在误差,后来诞生了协调世界时(UTC, Coordinated Universal Time),基于铯原子的振动频率计算得出,成为目前的世界标准时间;
  7. 常用的时间单位换算关系:\(1 \text{s} = 10^3 \text{ms}, 1 \text{ms} = 10^3 \mathrm{\mu s}, 1 \mathrm{\mu s} = 10^3 \text{ns}\)

1-8.2 Date

Date 类用于表示某个具体的时刻,精确到毫秒。

构造方法

构造方法 描述
Date() 无参构造,返回实例化对象时刻的 Date 对象
Date(long date) 基于所给时间创建一个 Date 对象

注意

  1. Date(long date) 所接受的参数是指自 1970.1.1 00:00:00 GMT 起所经历的毫秒数;
  2. 传入年、月、日(时、分、秒)的构造方法自 JDK 1.1 起被弃用,相关方法已由 Calendar 类中的静态方法 set(year + 1900, month, date, hrs, min, sec) ,或 GregorianCalendar(year + 1900, month, date, hrs, min, sec) 取代;
  3. 传入日期字符串的构造方法自 JDK 1.1 起被弃用,代之以 DateFormat.parse(String s)
  4. 所有与年月日时分秒参数相关的方法都已被弃用,代之以 Calendar 中的方法,详见官方文档;
  5. 所有与格式化有关的方法(包含解析方法和日期字符串构造时间对象的方法)都已被弃用,代之以 DateFormat 中的方法,详见官方文档;

常用方法

方法 描述
boolean after(Date when) 测试该日期是否晚于指定日期
boolean before(Date when) 测试该日期是否早于指定日期
int compareTo(Date anotherDate) 比较两个日期的时间顺序
void setTime(long time) 设置该对象中的时间
long getTime() 返回时间

注意

  • 上表所述的 ”时间“,指的都是自 1970.1.1 00:00:00 GMT 起所经历的毫秒数;
  • 该类重写了 toString() 方法,打印出来的日期字符串格式为 dow mon dd hh:mm:ss zzz yyyy,即 星期 月份 日期 时:分:秒 时区 年份

案例演示

要求:

  • 打印自时间原点开始一年后的时间;
  • 定义两个任意 Date 对象,比较二者孰前孰后;

演示

import java.util.Date;
import java.util.Random;

public class DateTest {
    public static void main(String[] args) {
        //打印自时间原点一年后的时间
        Date d1 = new Date(31_536_000_000L);
        System.out.println("自时间原点一年后的时间:" + d1);

        //比较两个任意时间的先后顺序
        Random rd = new Random(System.currentTimeMillis());

        d1.setTime(Math.abs(rd.nextInt()));
        Date d2 = new Date(Math.abs(rd.nextInt()));
        System.out.println("新的时间 1:" + d1);
        System.out.println("新的时间 2:" + d2);
        System.out.print("二者先后:");
        
        if (d1.equals(d2)) {
            System.out.println("二者相同。");
        } else if (d1.before(d2)) {
            System.out.println("d2 在后,d1 在前。");
        } else {
            System.out.println("d1 在后,d2 在前。");
        }
    }
}

运行得到

自时间原点一年后的时间:Fri Jan 01 08:00:00 CST 1971
新的时间 1:Fri Jan 16 10:01:34 CST 1970
新的时间 2:Sun Jan 25 07:45:18 CST 1970
二者先后:d2 在后,d1 在前。

1-8.3 SimpleDateFormat

SimpleDateFormat 类是一个具体类,用于针对不同地区格式化和解析日期。格式化,即从日期到文字;解析,即从文字到日期。

因此,SimpleDateFormat 主要工作就是格式化(把时间变为所需格式)和解析(把文字所述时间变为 Date 对象)。

构造方法

构造方法 描述
SimpleDateFormat() 使用默认的模式和日期格式标识,为默认地区创建对象
SimpleDateFormat(String pattern) 使用指定的模式,默认的日期格式标识,为默认地区创建对象
SimpleDateFormat(String pattern, Locale locale) 使用指定的模式,默认的日期格式标识,为指定地区创建对象

常用方法

方法 描述
Date parse(String text, ParsePosition pos)
Date parse(String text)
从字符串中的指定索引位置解析文本,产生一个新的 Date 对象
StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos)
StringBuffer format(Date date)
格式化指定的日期/时间,将结果追加到指定的 StringBuffer 对象中
void applyPattern(String pattern) 应用指定的新模式字符串,格式化日期

注意

  1. 若成功解析文本,pos 将更新到最后一个使用的字符后(解析并不一定使用至末尾的所有字符),并返回解析的对象,更新后的 pos 可用于指示下次调用该方法时的起始位置;
  2. 若解析文本失败,pos 索引不变,且将位于发生错误的字符索引处,并返回 null,抛出 ParseException(声明于超类);
  3. 解析文本时,所使用的模式必须与所给字符串模式相同;
  4. 所有的日期/时间模式将在下文列出,其中常用的为 y, M, d, H, m, s

日期/时间模式

字母 日期或时间组成成分 表示 示例
G 纪元 文本 AD
y 年份 年份 1996; 96
Y Week year 年份 2009; 09
M 年中月份(上下文敏感) 月份 July; Jul; 07
L 年中月份(独立形式) 月份 July; JUl; 07
w 年中周 数字 27
W 月中周 数字 2
D 年中日 数字 189
d 月中日 数字 10
F 月中星期 数字 2
E 星期名字 文本 Tuesday; Tue
u 星期数字编号 数字 1
a Am/pm 标志 文本 PM
H 一天中小时(0~23) 数字 0
k 一天中小时(1-24) 数字 24
K am/pm 小时(0~11) 数字 0
h am/pm 小时(1-12) 数字 12
m 小时中的分钟 数字 30
s 分钟中的秒数 数字 55
S 毫秒 数字 978
z 时区 一般时区 Pacific Standard Time; PST; GMT-08:00
Z 时区 RFC 822 时区 -0800
X 时区 ISO 8601 时区 -08; -0800; -08:00

注意

  • 在字符串中,无引号括起来的字母 A-Za-z 会解释为字符串中表示不同日期和时间的组成成分;
  • 使用单引号可避免被解析,"''" 表示一个单引号,其余所有字符都不会被解析,而是会被复制到输出字符串中;
  • 模式字母通常会重复使用,它们的个数决定了具体的表示;

案例演示

要求:

  • 给定一个年月日 2000-11-11,用字符串表示该数据,转换为 2000年11月11日
  • 秒杀活动自 2023.11.11 00:00:00 至 2023.11.11 00:10:00 开始,小明下单付款时间为 2023.11.11 00:01:00,小红下单付款时间为 2023.11.11 00:11:00。在代码层面判断二人是否成功参加活动。

演示

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Practices {
    public static void main(String[] args) throws ParseException {
        //案例:
        //1. 转换 2000-11-11 :xxxx年-xx月xx日
        String str1 = "2000-11-11";
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
        Date parse1 = sdf1.parse(str1);
        sdf1.applyPattern("yyyy年MM月dd日");
        String str2 = sdf1.format(parse1);
        System.out.println(str2);
    }
}

运行,得到

2000年11月11日
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Practices {
    public static void main(String[] args) throws ParseException {
        //2. 秒杀活动
        //秒杀时间为 2023.11.11 00:00:00 ~ 00:10:00
        //小明下单付款时间 2023.11.11 00:01:00
        //小红下单付款时间 2023.11.11 00:11:00
        //用代码说明二位是否成功参加
        SimpleDateFormat sfd = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        String startStr = "2023.11.11 00:00:00";
        String endStr = "2023.11.11 00:10:00";
        String xiaoming = "2023.11.11 00:01:00";
        String xiaohong = "2023.11.11 00:11:00";

        //一种没有可读性的写法:偷懒
        long startTime = sfd.parse(startStr).getTime();
        long period = sfd.parse(endStr).getTime() - startTime;
        long xiaomingTime = sfd.parse(xiaoming).getTime() - startTime;
        long xiaohongTime = sfd.parse(xiaohong).getTime() - startTime;

        System.out.println(((xiaomingTime <= period) ? "小明成功参加" : "小明未能参加"));
        System.out.println(((xiaohongTime <= period) ? "小红成功参加" : "小红未能参加"));
    }
}

运行得到

小明成功参加
小红未能参加

1-8.4 Calendar

Calendar 是一个抽象类,其内部提供一些具体时间与日历字段(年、月、日等)相互转换、操作日历字段的方法。

Calendar 代表了系统当前时间的日历对象,可以单独修改、获取时间中的年、月、日等字段。

该类的构造方法不可以被直接调用,要想获取实例,只能使用其静态方法。

静态方法

静态方法 描述
Calendar getInstance() 使用默认时区和地区获取一个日历实例
Locale[] getAvailableLocales() 返回地区数组,包含 getInstance() 方法所能支持的所有本地化实例

常用方法

方法 描述
final Date getTime() 返回一个 Date 对象,表示该 Calendar 的时间值(与时间原点的偏移量)
final void setTime(Date date) 使用指定 Date 日期设置时间
long getTimeInMillis() 返回该日历的毫秒时间值
void setTimeInMillis(long millis) 设置该日历的时间
int get(int field) 返回指定日历字段的值
void set(int field, int value) 使用指定值设置日历中字段的值
void add(int field, int amount) 基于日历规则,在指定字段上加或减指定数量
final Instant toInstant() 将对象转换为 Instant 对象

注意

  1. getInstance() 方法会根据系统的不同时区创建不同的日历,一般而言,创建的是格里高利日历(Gregorian Calendar);

  2. Calendar 会把时间当中的纪元、年、月、日、时、分、秒、星期等信息放在一个数组中存储;

    • 可以通过向控制台打印该实例查看该实例中所存储的信息;
    • 月份字段的范围为 \([0,11]\),以此表示一月到十二月;
    • 一般而言,一周的第一天为周日;星期字段的表示范围为 \([1,7]\),分别从一周的第一天开始至最后一天;可用 int getFirstDayOfWeek() 查看,在美国,一周第一天为周日,在法国为周一;
  3. 使用 get()set() 方法时,field 字段对应的实际上是数组的索引,若越界,则会抛出异常 ArrayIndexOutOfBoundsException,索引所对应内容为:

    索引 对应公有静态常量 字段
    0 ERA 纪元
    1 YEAR
    2 MONH
    3 WEEK_OF_YEAR 一年中的周
    4 WEEK_OF_MONTH 一月中的周
    5 DAY_OF_MONTH, DATE 一月中的日
    6 DAY_OF_YEAR 一年中的日
    7 DAY_OF_WEEK 一周第几天
    8 DAY_OF_WEEK_IN_MONTH 本月第几个周几
    9 AM_PM AM / PM
    10 HOUR 时(12小时制)
    11 HOUR_OF_DAY 时(24小时制)
    12 MINUTE
    13 SECOND
    14 MILLISECOND 毫秒
    15 ZONE_OFFSET 与 GMT 的毫秒偏移量
    16 DST_OFFSET 夏令时
  4. set()add() 方法传入新值时,若数据发生溢出(例如 13 月、2 月 30 日、星期循环等),则会自动往后(前)顺延;

posted @ 2023-07-22 01:12  Zebt  阅读(111)  评论(0)    收藏  举报