当前位置:首页 > 技术 > 正文内容

Java日期处理:Date与Calendar实战指南

访客 技术 2026年9月2日 1

概述

在Java开发中,日期处理是极为常见的操作。Date和Calendar作为早期JDK提供的日期类,虽然在现代开发中已被java.time包取代,但在维护遗留系统或理解底层原理时仍具有重要价值。本文将系统讲解这两个类的核心用法与相互转换机制。

核心类解析

Date类

Date类用于表示某个特定的时刻,精度可达毫秒级别。在JDK 1.1之前的版本中,Date类承担了日期解析和格式化的双重职能。然而,由于其API设计不利于国际化支持,从JDK 1.1开始,官方推荐使用Calendar类进行日期字段的转换操作,同时将Date类中相关的方法标记为已废弃。

Calendar类

Calendar是一个抽象基类,它在时间戳与日历字段(如年、月、日、时、分、秒等)之间提供了一套完整的转换机制。该类还支持对日历字段进行各种算术运算,例如计算指定日期若干天后的日期。Calendar内部采用毫秒值表示时间点,该值表示自1970年1月1日00:00:00 GMT(格里高利历元)以来的毫秒数。 简而言之,Date主要用于存储日期数据,而Calendar则专注于日期相关的计算与操作。

常用操作详解

获取当前时间

以下代码展示了如何获取系统的当前时间:
// 使用Date类获取当前时间
Date currentDate = new Date();
System.out.println(currentDate.toString());

// 使用Calendar类获取当前时间
Calendar currentCalendar = Calendar.getInstance();
System.out.println(currentCalendar.toString());
从输出结果可以清晰看出两者的本质区别:Date输出的是易于阅读的日期字符串,而Calendar则提供了完整的时间字段信息。

创建指定日期

在实际开发中,经常需要根据字符串创建特定日期对象:
// 定义日期格式解析器
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date specifiedDate = null;

try {
    // 将字符串解析为Date对象
    specifiedDate = dateFormatter.parse("2020-05-20 14:30:00");
} catch (ParseException e) {
    e.printStackTrace();
}

// 将Date转换为Calendar进行操作
Calendar calendarFromDate = Calendar.getInstance();
calendarFromDate.setTime(specifiedDate);
需要特别注意的是,格式字符串中的月份和分钟必须使用小写的"mm",而小时部分的大小写不影响解析结果。

类型相互转换

Date与Calendar之间的转换是日常开发中的高频操作:
// 初始化Date对象
Date originalDate = new Date();

// Date转Calendar
Calendar calendarInstance = Calendar.getInstance();
calendarInstance.setTime(originalDate);

// Calendar转Date
Date convertedDate = calendarInstance.getTime();
转换的核心在于Calendar类提供的setTime()和getTime()方法。setTime()接收Date类型参数并填充Calendar的各个字段,getTime()则从Calendar字段组装并返回Date对象。值得注意的是,Date类本身并未提供直接转换到Calendar的方法。

日期格式化输出

将Date对象格式化为指定格式的字符串:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String formattedString = formatter.format(new Date());

日期算术运算

使用Calendar的add()方法可以对日期进行加减操作:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

// 获取当前时间并输出
Date today = new Date();
System.out.println(sdf.format(today));

// 创建Calendar并进行月份加一操作
Calendar calendar = Calendar.getInstance();
calendar.setTime(today);
calendar.add(Calendar.MONTH, 1);
Date nextMonth = calendar.getTime();

System.out.println(sdf.format(nextMonth));
add()方法的官方定义为:根据日历规则,为指定的日历字段添加或减去指定的时间量。 参数说明:
  • field:指定要操作的时间字段,支持的常量包括YEAR(年)、MONTH(月)、DAY_OF_MONTH(日)、HOUR(小时)、MINUTE(分钟)、SECOND(秒)等
  • amount:正数表示在当前时间基础上增加,负数表示减少
Calendar类提供了丰富的字段常量,不仅限于时间单位,还包括WEEK_OF_YEAR、DAY_OF_YEAR等周期性字段。

获取当天结束时刻

以下工具方法可用于获取当天23:59:59.000这一精确时刻:
public static Date obtainDayEndTime() {
    Calendar cal = new GregorianCalendar();
    cal.set(Calendar.HOUR_OF_DAY, 23);
    cal.set(Calendar.MINUTE, 59);
    cal.set(Calendar.SECOND, 59);
    cal.set(Calendar.MILLISECOND, 0);
    return cal.getTime();
}

日期工具类实现

以下是一个实用的日期处理工具类,封装了常见的日期操作需求:
package com.example.core.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

public class DateTimeUtil {

    public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String COMPACT_FORMAT = "yyyyMMdd";
    public static final String TIME_ONLY = "HH:mm";
    
    private static final Logger log = LoggerFactory.getLogger(DateTimeUtil.class);
    
    /**
     * 时间偏移计算
     * @param baseDate 基础时间
     * @param direction true为正向偏移,false为反向
     * @param unit 时间单位
     * @param delta 偏移量
     * @return 偏移后的时间
     */
    public static Date shiftTime(Date baseDate, boolean direction, TimeUnit unit, int delta) {
        if (baseDate == null || delta == 0) {
            return baseDate;
        }
        
        Calendar cal = Calendar.getInstance();
        cal.setTime(baseDate);
        cal.add(unit.getField(), direction ? delta : -delta);
        return cal.getTime();
    }
    
    /**
     * 日期单位枚举
     */
    public enum TimeUnit {
        YEAR(Calendar.YEAR),
        MONTH(Calendar.MONTH),
        DAY(Calendar.DAY_OF_YEAR),
        HOUR(Calendar.HOUR_OF_DAY),
        MINUTE(Calendar.MINUTE),
        SECOND(Calendar.SECOND);
        
        private final int field;
        
        TimeUnit(int field) {
            this.field = field;
        }
        
        public int getField() {
            return field;
        }
    }
    
    /**
     * 将Date格式化为指定格式的字符串
     */
    public static String toString(Date target, String pattern) {
        if (target == null) {
            return null;
        }
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            return sdf.format(target);
        } catch (Exception e) {
            log.error("日期格式化失败: {}", target, e);
            return null;
        }
    }
    
    /**
     * 将字符串解析为Date对象
     */
    public static Date fromString(String source, String pattern) {
        if (source == null || source.trim().isEmpty()) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        try {
            return sdf.parse(source.trim());
        } catch (Exception e) {
            log.warn("日期解析异常, source={}, pattern={}", source, pattern, e);
            return null;
        }
    }
    
    /**
     * 获取指定时间在当天的指定时分秒时刻
     */
    public static Date getSpecificTimeOfDay(Date base, int hour, int minute, int second) {
        if (base == null) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(base);
        cal.set(Calendar.HOUR_OF_DAY, hour);
        cal.set(Calendar.MINUTE, minute);
        cal.set(Calendar.SECOND, second);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }
    
    /**
     * 获取日期中的小时数(24小时制)
     */
    public static int extractHour(Date target) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(target);
        return cal.get(Calendar.HOUR_OF_DAY);
    }
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。