0

0

Java中Calendar类常用方法解析

P粉602998670

P粉602998670

发布时间:2025-09-17 14:46:01

|

793人浏览过

|

来源于php中文网

原创

Java中Calendar类是处理日期时间的核心工具,通过getInstance()获取实例,set()/get()设置和获取字段,add()/roll()增减时间,getTime()/setTime()与Date转换,before()/after()/compareTo()比较时间。其与Date的关系为:Date表示时间点,Calendar是操作器,常见误区包括误用Date的废弃方法和Calendar的可变性导致的副作用。尽管Java 8推荐使用java.time(因不可变、线程安全、API直观等优势),但Calendar仍在遗留系统、旧API交互及Java 8以下版本中具有不可替代性。处理时区和夏令时时需注意:Calendar默认使用JVM时区,可通过setTimeZone()指定;能自动处理夏令时跳变,但“缺失”或“重复”时间可能引发问题,且依赖JVM时区数据库的更新。因此,在需要兼容旧环境或维护遗留代码时,掌握Calendar仍至关重要。

java中calendar类常用方法解析

Java中的

Calendar
类,在我看来,是处理日期和时间操作的一个核心工具,尤其是在
java.time
包出现之前,它几乎是所有复杂日期计算的基石。理解其常用方法,意味着你掌握了在Java中对时间进行增减、设置、获取以及比较的基础能力,尽管它有一些让人感到困惑的地方,但其核心功能依然强大且不可或缺。

解决方案

在使用

Calendar
类时,我们通常会通过
getInstance()
方法获取一个代表当前时间的
Calendar
实例,然后利用一系列方法对其进行操作。

1.

getInstance()
:获取Calendar实例 这是我们开始一切操作的起点。它会返回一个基于当前时间、默认时区和默认语言环境的
Calendar
对象。

import java.util.Calendar;

Calendar now = Calendar.getInstance();
System.out.println("当前时间: " + now.getTime());

2.

set()
:设置日期和时间字段
set()
方法非常灵活,可以设置年、月、日、小时、分钟、秒等各种字段。值得注意的是,月份是从0开始计数的(0代表1月,11代表12月),这常常是我在编码时需要特别留心的地方。

Calendar specificDate = Calendar.getInstance();
specificDate.set(2023, Calendar.DECEMBER, 25, 10, 30, 0); // 设置2023年12月25日10:30:00
System.out.println("特定日期: " + specificDate.getTime());

// 也可以单独设置某个字段
specificDate.set(Calendar.YEAR, 2024);
System.out.println("年份修改后: " + specificDate.getTime());

3.

get()
:获取日期和时间字段
set()
相对应,
get()
方法用于获取指定字段的值。你需要传入
Calendar
类中定义的常量,比如
Calendar.YEAR
Calendar.MONTH
等。

int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH) + 1; // 月份需要加1
int day = now.get(Calendar.DAY_OF_MONTH);
System.out.println("年: " + year + ", 月: " + month + ", 日: " + day);

4.

add()
:增加或减少时间
add()
方法是我最常用也觉得最方便的方法之一,它允许我们对某个时间字段进行增减操作,并且会自动处理日期溢出(例如,给月份加12会自动增加年份)。这在计算未来或过去的日期时非常实用。

立即学习Java免费学习笔记(深入)”;

now.add(Calendar.DAY_OF_MONTH, 7); // 增加7天
System.out.println("7天后: " + now.getTime());

now.add(Calendar.HOUR_OF_DAY, -3); // 减少3小时
System.out.println("3小时前: " + now.getTime());

5.

roll()
:滚动时间字段
roll()
方法与
add()
相似,但有一个关键区别:它在对某个字段进行增减时,不会影响比它大的字段。比如,对月份进行滚动,年份不会改变。这在某些特定场景下很有用,但用起来也容易混淆,需要特别注意。

Calendar tempCalendar = Calendar.getInstance();
tempCalendar.set(2023, Calendar.DECEMBER, 31);
System.out.println("原始日期: " + tempCalendar.getTime()); // 2023年12月31日

tempCalendar.roll(Calendar.MONTH, 1); // 滚动月份,年份不变
System.out.println("滚动月份后: " + tempCalendar.getTime()); // 2023年1月31日 (因为从12月滚动1个月变成了1月)

6.

getTime()
:将Calendar转换为Date对象
Calendar
对象本身并不直接代表一个时间点,它是一个抽象的概念。要获取一个具体的
java.util.Date
对象,就需要用到
getTime()
方法。

java.util.Date date = now.getTime();
System.out.println("转换为Date: " + date);

7.

setTime()
:将Date对象设置为Calendar的时间 反过来,如果你有一个
java.util.Date
对象,想要用
Calendar
进行操作,就需要
setTime()
方法。

java.util.Date anotherDate = new java.util.Date(); // 获取当前Date
Calendar anotherCalendar = Calendar.getInstance();
anotherCalendar.setTime(anotherDate);
System.out.println("从Date设置的Calendar: " + anotherCalendar.getTime());

8.

before()
,
after()
,
compareTo()
:比较Calendar实例
这些方法用于比较两个
Calendar
实例的时间顺序。
before()
after()
返回布尔值,
compareTo()
则返回一个整数,表示它们的相对顺序(-1表示早于,0表示相等,1表示晚于)。

Calendar future = Calendar.getInstance();
future.add(Calendar.DAY_OF_MONTH, 1);

System.out.println("当前时间是否在未来时间之前? " + now.before(future));
System.out.println("未来时间是否在当前时间之后? " + future.after(now));
System.out.println("比较结果 (now vs future): " + now.compareTo(future)); // 应该返回-1

Java中Calendar与Date类的关系及常见转换误区有哪些?

java.util.Date
java.util.Calendar
在Java早期是处理日期时间的核心,但它们的设计哲学和用途有所不同。
Date
类在我看来,更像是一个时间戳的封装,它内部存储的是自1970年1月1日00:00:00 GMT以来的毫秒数,代表一个精确到毫秒的瞬间。而
Calendar
则是一个更高级、更抽象的工具,它能将这个时间戳解析成我们熟悉的年、月、日、时、分、秒等字段,并且提供了对这些字段进行操作(如增减、设置)的功能。

它们之间的关系可以这样理解:

Date
是“时间点”,而
Calendar
是“时间点解析器和操作器”。

常见转换误区:

  1. Date的API误用: 很多人会尝试直接使用

    Date
    类中的
    getYear()
    getMonth()
    等方法来获取日期字段。但这些方法在JDK 1.1之后就已经被废弃了,因为它们存在时区问题,并且月份从0开始计数等设计缺陷。正确的做法是,将
    Date
    对象转换为
    Calendar
    对象,再通过
    Calendar
    get()
    方法获取各个字段。

    // 错误示范 (已废弃且有时区问题)
    // Date myDate = new Date();
    // System.out.println(myDate.getYear() + 1900); // 废弃方法
    
    // 正确做法
    java.util.Date myDate = new java.util.Date();
    Calendar calFromDate = Calendar.getInstance();
    calFromDate.setTime(myDate);
    System.out.println("年份: " + calFromDate.get(Calendar.YEAR));
  2. Calendar的Mutability(可变性): 这是我个人觉得

    Calendar
    最容易让人踩坑的地方。
    Calendar
    对象是可变的,这意味着当你对一个
    Calendar
    实例进行
    add()
    set()
    操作时,它会直接修改自身的状态。如果你不小心将同一个
    Calendar
    实例传递给多个地方,或者在一个循环中重复使用,可能会导致意想不到的结果。

    Calendar original = Calendar.getInstance();
    Calendar modified = original; // modified和original指向同一个对象
    
    modified.add(Calendar.DAY_OF_MONTH, 1);
    
    System.out.println("原始Calendar (被修改了): " + original.getTime()); // original也被修改了

    为了避免这种问题,当你需要基于一个

    Calendar
    对象进行操作但又不想改变原对象时,应该创建一个新的
    Calendar
    实例,或者克隆一份:

    Calendar originalCorrect = Calendar.getInstance();
    Calendar newCalendar = (Calendar) originalCorrect.clone(); // 克隆一份
    // 或者
    // Calendar newCalendar = Calendar.getInstance();
    // newCalendar.setTime(originalCorrect.getTime());
    
    newCalendar.add(Calendar.DAY_OF_MONTH, 1);
    System.out.println("原始Calendar (未被修改): " + originalCorrect.getTime());
    System.out.println("新Calendar (修改后): " + newCalendar.getTime());

为什么Java 8推荐使用java.time API,Calendar类还有哪些不可替代的场景?

Java 8引入的

java.time
包(通常称为JSR-310或新日期时间API)是对
Date
Calendar
类的一次彻底重构和现代化。在我看来,它的出现极大地简化了日期时间编程,解决了旧API中长期存在的痛点。

eMart 网店系统
eMart 网店系统

功能列表:底层程序与前台页面分离的效果,对页面的修改无需改动任何程序代码。完善的标签系统,支持自定义标签,公用标签,快捷标签,动态标签,静态标签等等,支持标签内的vbs语法,原则上运用这些标签可以制作出任何想要的页面效果。兼容原来的栏目系统,可以很方便的插入一个栏目或者一个栏目组到页面的任何位置。底层模版解析程序具有非常高的效率,稳定性和容错性,即使模版中有错误的标签也不会影响页面的显示。所有的标

下载

Calendar
类的主要缺点和
java.time
的优势:

  • 可变性问题:
    Calendar
    对象是可变的,容易导致线程不安全和意外的副作用。
    java.time
    中的所有核心类(如
    LocalDate
    ,
    LocalTime
    ,
    LocalDateTime
    ,
    ZonedDateTime
    等)都是不可变的,每次操作都会返回一个新的实例,这大大提高了代码的健壮性和可预测性。
  • API设计复杂且不直观:
    Calendar
    的月份从0开始,字段常量命名有时也让人困惑。
    java.time
    的API设计更加直观和语义化,例如
    Month.JANUARY
    代表1月,
    plusDays()
    minusHours()
    等方法一目了然。
  • 线程不安全:
    Calendar
    不是线程安全的,在多线程环境下需要额外的同步措施。
    java.time
    的不可变性使其天然就是线程安全的。
  • 时区处理复杂:
    Calendar
    处理时区时,常常需要手动设置
    TimeZone
    ,并且在夏令时切换时容易出错。
    java.time
    提供了
    ZonedDateTime
    等类,对时区和夏令时的处理更加清晰和健壮。
  • 缺乏类型安全:
    Calendar
    get()
    set()
    方法都使用
    int
    类型的字段常量,容易混淆,也缺乏编译时检查。
    java.time
    则有更丰富的类型系统,例如
    Duration
    用于表示时间段,
    Period
    用于表示日期段。

Calendar
类不可替代的场景(或仍有其价值的场景):

尽管

java.time
是现代Java开发的首选,但
Calendar
类并非一无是处,在某些特定场景下,它仍然有其用武之地:

  1. 遗留系统维护: 这是最主要的原因。在大量的现有Java项目中,
    Calendar
    类被广泛使用。重构这些代码以完全替换为
    java.time
    可能成本高昂且风险大。在这种情况下,理解并继续使用
    Calendar
    是必要的。
  2. 与旧API或第三方库交互: 某些第三方库或框架可能仍然期望或返回
    java.util.Date
    java.util.Calendar
    对象。在这种接口边界,你可能需要进行转换。
  3. 某些特定功能:
    Calendar
    在处理某些特定需求时,例如计算某个日期是当年的第几周,或者获取星期的第一天等,虽然
    java.time
    也能实现,但
    Calendar
    可能因为其历史积累,在某些特定文化或地区习惯的日期计算上表现出一些便利性。不过,这通常可以通过
    java.time
    结合
    TemporalAdjusters
    Locale
    更好地实现。
  4. Java版本限制: 如果你的项目运行在Java 8之前的版本(比如Java 6或7),那么
    java.time
    API是不可用的,
    Calendar
    仍然是唯一的内置选择。

总的来说,对于新项目或有机会重构的模块,我强烈建议拥抱

java.time
。但在面对遗留代码时,对
Calendar
的熟练掌握依然是一项宝贵的技能。

Calendar类在处理时区和夏令时时需要注意哪些细节?

Calendar
类在处理时区和夏令时方面,确实有一些需要留心的细节,否则很容易引入难以发现的bug。在我看来,它在这方面的设计不如
java.time
那么直观和健壮,但理解其工作原理是关键。

1. 时区设置:

Calendar
实例在创建时,默认会使用JVM的默认时区。如果你需要处理特定时区的日期时间,就必须显式地设置
TimeZone

import java.util.Calendar;
import java.util.TimeZone;

Calendar gmtCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
System.out.println("GMT时间: " + gmtCalendar.getTime());

Calendar nyCalendar = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
System.out.println("纽约时间: " + nyCalendar.getTime());

// 你也可以在创建后设置
Calendar localCalendar = Calendar.getInstance();
System.out.println("本地时间: " + localCalendar.getTime());
localCalendar.setTimeZone(TimeZone.getTimeZone("Europe/London"));
System.out.println("设置为伦敦时区后: " + localCalendar.getTime()); // 注意,getTime()返回的Date对象内部仍是UTC毫秒数,只是Calendar在格式化时会用伦敦时区规则

注意:

Calendar
内部存储的其实是自Epoch(1970-01-01 00:00:00 GMT)以来的毫秒数,这个毫秒数是与时区无关的。当你设置了
TimeZone
后,
Calendar
会根据这个时区规则来解释和计算这些毫秒数,从而得到年、月、日、时、分等字段。
getTime()
方法返回的
Date
对象,其内部仍然是这个UTC毫秒数。当你打印
Date
对象时,JVM通常会用默认时区来格式化它。这常常是初学者感到困惑的地方。

2. 夏令时(Daylight Saving Time, DST)的处理:

Calendar
类本身是能够自动处理夏令时的。当你设置一个日期时间,并且该日期时间落在夏令时生效或结束的区域和时间段内时,
Calendar
会自动调整时间。

例如,在许多地区,夏令时开始时,时间会向前跳一个小时(比如凌晨2点变成凌晨3点);夏令时结束时,时间会向后跳一个小时(比如凌晨2点变成凌晨1点)。

Calendar dstStart = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
// 设置一个夏令时开始前的时间 (假设纽约夏令时在3月第二个周日凌晨2点开始)
dstStart.set(2023, Calendar.MARCH, 12, 1, 30, 0); // 2023年3月12日1:30 AM
System.out.println("夏令时开始前: " + dstStart.getTime()); // 输出1:30 AM EST

dstStart.add(Calendar.HOUR_OF_DAY, 1); // 增加一个小时
System.out.println("增加一小时后 (跨越夏令时开始): " + dstStart.getTime()); // 可能会跳到3:30 AM EDT,而不是2:30 AM EST

这里需要注意的是,

add()
方法在跨越夏令时边界时,会根据时区规则进行“智能”调整。例如,如果你在夏令时开始前一小时,
add(Calendar.HOUR_OF_DAY, 1)
,结果可能会直接跳过夏令时开始时“丢失”的那一小时。

潜在问题和注意事项:

  • “缺失”或“重复”的时间: 在夏令时开始时,会有一个小时的时间段在钟表上是不存在的(例如,从1:59 AM直接跳到3:00 AM)。如果你试图设置或计算这个“缺失”的时间,
    Calendar
    可能会将其调整到夏令时生效后的时间。相反,在夏令时结束时,会有一个小时的时间段重复出现(例如,从1:00 AM到1:59 AM会发生两次)。
    Calendar
    在处理重复时间时,可能会根据其内部实现选择第一个或第二个实例。这在需要精确时间戳的场景下可能导致问题。
  • 跨时区计算: 当你在不同时区之间进行日期时间计算时,务必确保每个
    Calendar
    实例都设置了正确的
    TimeZone
    ,并且理解
    getTime()
    返回的
    Date
    对象是UTC毫秒数,它的字符串表示取决于JVM的默认时区或你使用的
    DateFormat
  • TimeZone数据库更新: 时区规则(包括夏令时规则)会不定期发生变化。JVM内部维护一个时区数据库。如果你的JVM时区数据库过旧,可能会导致夏令时计算错误。定期更新JVM或使用
    java.time
    (它通常依赖于更现代的TZDB)可以缓解这个问题。

在我看来,处理时区和夏令时是日期时间编程中最复杂的部分之一。

Calendar
虽然能处理,但其API设计有时会让人感到不够透明,需要开发者对时区和DST有较深的理解。这也是
java.time
在这方面做得更好的原因之一,它通过
ZonedDateTime
等类提供了更清晰、更明确的语义。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1502

2023.10.24

java中calendar类的用法
java中calendar类的用法

Java Video类是JavaFX库中的一个类,用于创建和操作视频对象。它提供了方法来加载、播放、暂停、停止和控制视频的音量、速度和循环等属性。想了解更多Java中类的相关内容,可以阅读本专题下面的文章。

310

2024.02.29

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

298

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1502

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

624

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

633

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

589

2024.04.29

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

相关下载

更多

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.9万人学习

Java 教程
Java 教程

共578课时 | 53.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号