|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. 项目概述和目标
天数计算系统是一个常见的实用工具,广泛应用于项目管理、财务计算、日程安排等领域。本项目的目标是开发一个功能全面的天数计算系统,能够处理从简单的日期差计算到复杂的工作日计算等各种需求。
项目主要功能包括:
• 基本日期操作(创建、格式化、解析)
• 简单天数差计算
• 高级时间处理(时间段计算、日期加减)
• 工作日计算(排除周末和节假日)
• 自定义工作日规则
通过本项目的开发,读者将全面掌握Java中日期时间处理的各个方面,并学会如何在Eclipse环境中构建一个完整的应用程序。
2. 环境搭建(Eclipse配置)
2.1 安装Eclipse IDE
1. 访问Eclipse官方网站(https://www.eclipse.org/)
2. 下载适合你操作系统的Eclipse IDE for Enterprise Java Developers版本
3. 安装Eclipse(解压下载的文件即可)
2.2 配置Java开发环境
1. 确保已安装JDK(推荐JDK 8或更高版本)
2. 在Eclipse中配置JDK:打开Eclipse,选择”Window” > “Preferences”在左侧树形菜单中,展开”Java” > “Installed JREs”点击”Add”按钮,选择已安装的JDK目录
3. 打开Eclipse,选择”Window” > “Preferences”
4. 在左侧树形菜单中,展开”Java” > “Installed JREs”
5. 点击”Add”按钮,选择已安装的JDK目录
• 打开Eclipse,选择”Window” > “Preferences”
• 在左侧树形菜单中,展开”Java” > “Installed JREs”
• 点击”Add”按钮,选择已安装的JDK目录
2.3 创建新项目
1. 在Eclipse中,选择”File” > “New” > “Java Project”
2. 输入项目名称,例如”DayCalculator”
3. 选择合适的JRE版本
4. 点击”Finish”完成项目创建
2.4 项目结构设计
在项目中创建以下包结构:
• com.daycalculator.core:核心计算逻辑
• com.daycalculator.ui:用户界面
• com.daycalculator.util:工具类
• com.daycalculator.exception:自定义异常
• com.daycalculator.test:测试类
3. 基础日期操作
3.1 Java日期API介绍
Java提供了多种日期时间API,主要包括:
• java.util.Date:早期的日期类
• java.util.Calendar:提供更丰富的日期操作
• java.text.SimpleDateFormat:日期格式化和解析
• Java 8引入的java.time包:现代日期时间API
3.2 日期创建和格式化
让我们创建一个基础日期操作类,演示日期的创建和格式化:
- package com.daycalculator.core;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Calendar;
- public class BasicDateOperations {
-
- // 获取当前日期
- public static Date getCurrentDate() {
- return new Date();
- }
-
- // 使用Calendar创建特定日期
- public static Date createSpecificDate(int year, int month, int day) {
- Calendar calendar = Calendar.getInstance();
- calendar.set(year, month - 1, day); // 月份从0开始
- return calendar.getTime();
- }
-
- // 格式化日期
- public static String formatDate(Date date, String pattern) {
- SimpleDateFormat sdf = new SimpleDateFormat(pattern);
- return sdf.format(date);
- }
-
- // 默认格式化日期 (yyyy-MM-dd)
- public static String formatDefaultDate(Date date) {
- return formatDate(date, "yyyy-MM-dd");
- }
- }
复制代码
3.3 日期解析和验证
接下来,我们添加日期解析和验证的方法:
- package com.daycalculator.core;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- public class DateParser {
-
- // 解析日期字符串
- public static Date parseDate(String dateStr, String pattern) throws ParseException {
- SimpleDateFormat sdf = new SimpleDateFormat(pattern);
- sdf.setLenient(false); // 严格解析,不允许非法日期
- return sdf.parse(dateStr);
- }
-
- // 使用默认格式解析日期 (yyyy-MM-dd)
- public static Date parseDefaultDate(String dateStr) throws ParseException {
- return parseDate(dateStr, "yyyy-MM-dd");
- }
-
- // 验证日期字符串是否有效
- public static boolean isValidDate(String dateStr, String pattern) {
- try {
- parseDate(dateStr, pattern);
- return true;
- } catch (ParseException e) {
- return false;
- }
- }
-
- // 使用默认格式验证日期字符串
- public static boolean isValidDefaultDate(String dateStr) {
- return isValidDate(dateStr, "yyyy-MM-dd");
- }
- }
复制代码
4. 简单天数差计算
4.1 基本天数差计算方法
计算两个日期之间的天数差是一个常见需求。我们可以使用以下几种方法:
1. 使用Date类和毫秒差计算
2. 使用Calendar类
3. 使用Java 8的java.time包
4.2 考虑时区因素
在计算日期差时,时区是一个重要因素。我们需要确保两个日期在相同的时区下进行比较。
4.3 代码实现
让我们创建一个天数差计算类:
- package com.daycalculator.core;
- import java.util.Calendar;
- import java.util.Date;
- import java.util.TimeZone;
- public class DateDifferenceCalculator {
-
- // 使用Date类计算天数差(不考虑时间部分)
- public static int calculateDaysBetween(Date startDate, Date endDate) {
- // 重置时间部分为00:00:00
- Date start = resetTime(startDate);
- Date end = resetTime(endDate);
-
- // 计算毫秒差
- long diff = end.getTime() - start.getTime();
-
- // 转换为天数
- return (int) (diff / (24 * 60 * 60 * 1000));
- }
-
- // 重置日期的时间部分为00:00:00
- private static Date resetTime(Date date) {
- Calendar calendar = Calendar.getInstance();
- calendar.setTime(date);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- return calendar.getTime();
- }
-
- // 使用Calendar类计算天数差
- public static int calculateDaysBetweenWithCalendar(Date startDate, Date endDate) {
- Calendar startCalendar = Calendar.getInstance();
- startCalendar.setTime(startDate);
-
- Calendar endCalendar = Calendar.getInstance();
- endCalendar.setTime(endDate);
-
- // 重置时间部分
- resetTimeCalendar(startCalendar);
- resetTimeCalendar(endCalendar);
-
- // 计算完整的天数差
- int days = 0;
- while (startCalendar.before(endCalendar)) {
- startCalendar.add(Calendar.DAY_OF_MONTH, 1);
- days++;
- }
-
- return days;
- }
-
- // 重置Calendar的时间部分
- private static void resetTimeCalendar(Calendar calendar) {
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- }
-
- // 考虑时区的天数差计算
- public static int calculateDaysBetweenWithTimeZone(Date startDate, Date endDate, TimeZone timeZone) {
- Calendar startCalendar = Calendar.getInstance(timeZone);
- startCalendar.setTime(startDate);
-
- Calendar endCalendar = Calendar.getInstance(timeZone);
- endCalendar.setTime(endDate);
-
- // 重置时间部分
- resetTimeCalendar(startCalendar);
- resetTimeCalendar(endCalendar);
-
- // 计算完整的天数差
- int days = 0;
- while (startCalendar.before(endCalendar)) {
- startCalendar.add(Calendar.DAY_OF_MONTH, 1);
- days++;
- }
-
- return days;
- }
- }
复制代码
5. 高级时间处理
5.1 Java 8日期时间API
Java 8引入了全新的日期时间API,位于java.time包中,提供了更直观、更强大的日期时间处理能力。主要类包括:
• LocalDate:表示日期(年月日)
• LocalTime:表示时间(时分秒)
• LocalDateTime:表示日期时间
• ZonedDateTime:带时区的日期时间
• Period:表示日期间隔
• Duration:表示时间间隔
5.2 时间段计算
使用Java 8的API,我们可以更方便地计算时间段:
- package com.daycalculator.core;
- import java.time.LocalDate;
- import java.time.Period;
- import java.time.temporal.ChronoUnit;
- public class AdvancedDateOperations {
-
- // 使用Java 8 API计算两个日期之间的天数差
- public static long calculateDaysBetweenJava8(LocalDate startDate, LocalDate endDate) {
- return ChronoUnit.DAYS.between(startDate, endDate);
- }
-
- // 计算两个日期之间的月数差
- public static long calculateMonthsBetweenJava8(LocalDate startDate, LocalDate endDate) {
- return ChronoUnit.MONTHS.between(startDate, endDate);
- }
-
- // 计算两个日期之间的年数差
- public static long calculateYearsBetweenJava8(LocalDate startDate, LocalDate endDate) {
- return ChronoUnit.YEARS.between(startDate, endDate);
- }
-
- // 使用Period计算日期间隔
- public static Period calculatePeriodBetween(LocalDate startDate, LocalDate endDate) {
- return Period.between(startDate, endDate);
- }
-
- // 获取Period中的年、月、日
- public static String getPeriodDetails(Period period) {
- return String.format("%d 年, %d 月, %d 日",
- period.getYears(), period.getMonths(), period.getDays());
- }
- }
复制代码
5.3 日期加减操作
- package com.daycalculator.core;
- import java.time.LocalDate;
- import java.time.temporal.ChronoUnit;
- public class DateArithmeticOperations {
-
- // 日期加天数
- public static LocalDate addDays(LocalDate date, long days) {
- return date.plusDays(days);
- }
-
- // 日期减天数
- public static LocalDate subtractDays(LocalDate date, long days) {
- return date.minusDays(days);
- }
-
- // 日期加月数
- public static LocalDate addMonths(LocalDate date, long months) {
- return date.plusMonths(months);
- }
-
- // 日期减月数
- public static LocalDate subtractMonths(LocalDate date, long months) {
- return date.minusMonths(months);
- }
-
- // 日期加年数
- public static LocalDate addYears(LocalDate date, long years) {
- return date.plusYears(years);
- }
-
- // 日期减年数
- public static LocalDate subtractYears(LocalDate date, long years) {
- return date.minusYears(years);
- }
-
- // 使用ChronoUnit进行日期加减
- public static LocalDate addUnits(LocalDate date, long amount, ChronoUnit unit) {
- return date.plus(amount, unit);
- }
-
- public static LocalDate subtractUnits(LocalDate date, long amount, ChronoUnit unit) {
- return date.minus(amount, unit);
- }
- }
复制代码
6. 工作日计算
6.1 工作日概念和规则
工作日通常指周一到周五,不包括周末(周六和周日)。在某些情况下,还需要排除法定节假日。
6.2 排除周末的计算
- package com.daycalculator.core;
- import java.time.DayOfWeek;
- import java.time.LocalDate;
- import java.time.temporal.ChronoUnit;
- import java.util.List;
- import java.util.stream.Collectors;
- import java.util.stream.Stream;
- public class WorkdayCalculator {
-
- // 计算两个日期之间的工作日数(排除周末)
- public static long calculateWorkdaysBetween(LocalDate startDate, LocalDate endDate) {
- long daysBetween = ChronoUnit.DAYS.between(startDate, endDate) + 1; // 包含开始日期
-
- // 计算完整的周数和剩余天数
- long fullWeeks = daysBetween / 7;
- long remainingDays = daysBetween % 7;
-
- // 计算周末天数
- long weekendDays = fullWeeks * 2;
-
- // 计算剩余天数中的周末天数
- LocalDate currentDate = startDate.plusDays(fullWeeks * 7);
- for (int i = 0; i < remainingDays; i++) {
- if (isWeekend(currentDate)) {
- weekendDays++;
- }
- currentDate = currentDate.plusDays(1);
- }
-
- return daysBetween - weekendDays;
- }
-
- // 判断是否是周末
- private static boolean isWeekend(LocalDate date) {
- DayOfWeek day = date.getDayOfWeek();
- return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY;
- }
-
- // 获取两个日期之间的所有工作日
- public static List<LocalDate> getWorkdaysBetween(LocalDate startDate, LocalDate endDate) {
- return Stream.iterate(startDate, date -> date.isBefore(endDate) || date.isEqual(endDate), date -> date.plusDays(1))
- .filter(date -> !isWeekend(date))
- .collect(Collectors.toList());
- }
- }
复制代码
6.3 排除节假日的计算
- package com.daycalculator.core;
- import java.time.LocalDate;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
- import java.util.stream.Collectors;
- import java.util.stream.Stream;
- public class HolidayWorkdayCalculator extends WorkdayCalculator {
-
- private Set<LocalDate> holidays = new HashSet<>();
-
- // 添加节假日
- public void addHoliday(LocalDate date) {
- holidays.add(date);
- }
-
- // 批量添加节假日
- public void addHolidays(List<LocalDate> holidayList) {
- holidays.addAll(holidayList);
- }
-
- // 移除节假日
- public void removeHoliday(LocalDate date) {
- holidays.remove(date);
- }
-
- // 判断是否是节假日
- public boolean isHoliday(LocalDate date) {
- return holidays.contains(date);
- }
-
- // 判断是否是工作日(不是周末也不是节假日)
- public boolean isWorkday(LocalDate date) {
- return !isWeekend(date) && !isHoliday(date);
- }
-
- // 计算两个日期之间的工作日数(排除周末和节假日)
- public long calculateWorkdaysBetweenWithHolidays(LocalDate startDate, LocalDate endDate) {
- return Stream.iterate(startDate, date -> date.isBefore(endDate) || date.isEqual(endDate), date -> date.plusDays(1))
- .filter(date -> isWorkday(date))
- .count();
- }
-
- // 获取两个日期之间的所有工作日(排除周末和节假日)
- public List<LocalDate> getWorkdaysBetweenWithHolidays(LocalDate startDate, LocalDate endDate) {
- return Stream.iterate(startDate, date -> date.isBefore(endDate) || date.isEqual(endDate), date -> date.plusDays(1))
- .filter(date -> isWorkday(date))
- .collect(Collectors.toList());
- }
-
- // 计算从开始日期开始的第n个工作日
- public LocalDate getNthWorkday(LocalDate startDate, int n) {
- LocalDate currentDate = startDate;
- int workdayCount = 0;
-
- while (workdayCount < n) {
- currentDate = currentDate.plusDays(1);
- if (isWorkday(currentDate)) {
- workdayCount++;
- }
- }
-
- return currentDate;
- }
- }
复制代码
6.4 自定义工作日规则
7. 完整项目实现
7.1 项目结构设计
现在,让我们整合所有组件,构建一个完整的天数计算系统。
项目结构如下:
- DayCalculator/
- ├── src/
- │ ├── com/
- │ │ └── daycalculator/
- │ │ ├── core/
- │ │ │ ├── BasicDateOperations.java
- │ │ │ ├── DateParser.java
- │ │ │ ├── DateDifferenceCalculator.java
- │ │ │ ├── AdvancedDateOperations.java
- │ │ │ ├── DateArithmeticOperations.java
- │ │ │ ├── WorkdayCalculator.java
- │ │ │ ├── HolidayWorkdayCalculator.java
- │ │ │ └── CustomWorkdayCalculator.java
- │ │ ├── ui/
- │ │ │ ├── MainFrame.java
- │ │ │ ├── DateDifferencePanel.java
- │ │ │ ├── WorkdayCalculatorPanel.java
- │ │ │ └── HolidayManagerPanel.java
- │ │ ├── util/
- │ │ │ ├── DateUtils.java
- │ │ │ └── HolidayLoader.java
- │ │ ├── exception/
- │ │ │ └── DateParseException.java
- │ │ └── test/
- │ │ └── DateCalculatorTest.java
- │ └── resources/
- │ └── holidays.properties
- └── lib/
复制代码
7.2 核心类设计
让我们创建一个核心服务类,整合所有功能:
- package com.daycalculator.core;
- import java.time.LocalDate;
- import java.time.ZoneId;
- import java.util.Date;
- import java.util.List;
- import java.util.TimeZone;
- import java.util.HashSet;
- import java.util.Set;
- public class DayCalculatorService {
-
- private CustomWorkdayCalculator workdayCalculator;
-
- public DayCalculatorService() {
- this.workdayCalculator = new CustomWorkdayCalculator();
- }
-
- // 基本日期操作
-
- public Date getCurrentDate() {
- return BasicDateOperations.getCurrentDate();
- }
-
- public Date createSpecificDate(int year, int month, int day) {
- return BasicDateOperations.createSpecificDate(year, month, day);
- }
-
- public String formatDate(Date date, String pattern) {
- return BasicDateOperations.formatDate(date, pattern);
- }
-
- public String formatDefaultDate(Date date) {
- return BasicDateOperations.formatDefaultDate(date);
- }
-
- public Date parseDate(String dateStr, String pattern) throws Exception {
- try {
- return DateParser.parseDate(dateStr, pattern);
- } catch (Exception e) {
- throw new Exception("日期解析错误: " + e.getMessage(), e);
- }
- }
-
- public Date parseDefaultDate(String dateStr) throws Exception {
- try {
- return DateParser.parseDefaultDate(dateStr);
- } catch (Exception e) {
- throw new Exception("日期解析错误: " + e.getMessage(), e);
- }
- }
-
- public boolean isValidDate(String dateStr, String pattern) {
- return DateParser.isValidDate(dateStr, pattern);
- }
-
- public boolean isValidDefaultDate(String dateStr) {
- return DateParser.isValidDefaultDate(dateStr);
- }
-
- // 日期差计算
-
- public int calculateDaysBetween(Date startDate, Date endDate) {
- return DateDifferenceCalculator.calculateDaysBetween(startDate, endDate);
- }
-
- public int calculateDaysBetweenWithCalendar(Date startDate, Date endDate) {
- return DateDifferenceCalculator.calculateDaysBetweenWithCalendar(startDate, endDate);
- }
-
- public int calculateDaysBetweenWithTimeZone(Date startDate, Date endDate, String timeZoneId) {
- return DateDifferenceCalculator.calculateDaysBetweenWithTimeZone(
- startDate, endDate, TimeZone.getTimeZone(timeZoneId));
- }
-
- // Java 8 日期操作
-
- public LocalDate convertToLocalDate(Date date) {
- return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
- }
-
- public Date convertToDate(LocalDate localDate) {
- return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
- }
-
- public long calculateDaysBetweenJava8(LocalDate startDate, LocalDate endDate) {
- return AdvancedDateOperations.calculateDaysBetweenJava8(startDate, endDate);
- }
-
- public long calculateMonthsBetweenJava8(LocalDate startDate, LocalDate endDate) {
- return AdvancedDateOperations.calculateMonthsBetweenJava8(startDate, endDate);
- }
-
- public long calculateYearsBetweenJava8(LocalDate startDate, LocalDate endDate) {
- return AdvancedDateOperations.calculateYearsBetweenJava8(startDate, endDate);
- }
-
- public String calculatePeriodBetween(LocalDate startDate, LocalDate endDate) {
- return AdvancedDateOperations.getPeriodDetails(
- AdvancedDateOperations.calculatePeriodBetween(startDate, endDate));
- }
-
- // 日期算术运算
-
- public LocalDate addDays(LocalDate date, long days) {
- return DateArithmeticOperations.addDays(date, days);
- }
-
- public LocalDate subtractDays(LocalDate date, long days) {
- return DateArithmeticOperations.subtractDays(date, days);
- }
-
- public LocalDate addMonths(LocalDate date, long months) {
- return DateArithmeticOperations.addMonths(date, months);
- }
-
- public LocalDate subtractMonths(LocalDate date, long months) {
- return DateArithmeticOperations.subtractMonths(date, months);
- }
-
- public LocalDate addYears(LocalDate date, long years) {
- return DateArithmeticOperations.addYears(date, years);
- }
-
- public LocalDate subtractYears(LocalDate date, long years) {
- return DateArithmeticOperations.subtractYears(date, years);
- }
-
- // 工作日计算
-
- public void setWeekendDays(List<java.time.DayOfWeek> days) {
- workdayCalculator.setWeekendDays(new HashSet<>(days));
- }
-
- public void addHoliday(LocalDate date) {
- workdayCalculator.addHoliday(date);
- }
-
- public void addHolidays(List<LocalDate> holidayList) {
- workdayCalculator.addHolidays(holidayList);
- }
-
- public void removeHoliday(LocalDate date) {
- workdayCalculator.removeHoliday(date);
- }
-
- public void addWorkdayOnWeekend(LocalDate date) {
- workdayCalculator.addWorkdayOnWeekend(date);
- }
-
- public void removeWorkdayOnWeekend(LocalDate date) {
- workdayCalculator.removeWorkdayOnWeekend(date);
- }
-
- public long calculateWorkdaysBetween(LocalDate startDate, LocalDate endDate) {
- return workdayCalculator.calculateWorkdaysBetween(startDate, endDate);
- }
-
- public List<LocalDate> getWorkdaysBetween(LocalDate startDate, LocalDate endDate) {
- return workdayCalculator.getWorkdaysBetween(startDate, endDate);
- }
-
- public LocalDate getNthWorkday(LocalDate startDate, int n) {
- return workdayCalculator.getNthWorkday(startDate, n);
- }
- }
复制代码
7.3 用户界面设计
让我们创建一个简单的Swing用户界面:
- package com.daycalculator.ui;
- import javax.swing.*;
- import java.awt.*;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.time.LocalDate;
- import java.time.format.DateTimeFormatter;
- import java.time.format.DateTimeParseException;
- import java.util.Arrays;
- import java.util.HashSet;
- import java.util.Set;
- import java.util.TimeZone;
- public class MainFrame extends JFrame {
-
- private DayCalculatorService service;
- private JTabbedPane tabbedPane;
-
- public MainFrame() {
- service = new DayCalculatorService();
- initUI();
- }
-
- private void initUI() {
- setTitle("天数计算系统");
- setSize(800, 600);
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- setLocationRelativeTo(null);
-
- tabbedPane = new JTabbedPane();
-
- // 添加各个功能面板
- tabbedPane.addTab("日期差计算", new DateDifferencePanel(service));
- tabbedPane.addTab("工作日计算", new WorkdayCalculatorPanel(service));
- tabbedPane.addTab("节假日管理", new HolidayManagerPanel(service));
-
- add(tabbedPane, BorderLayout.CENTER);
- }
-
- public static void main(String[] args) {
- SwingUtilities.invokeLater(() -> {
- MainFrame frame = new MainFrame();
- frame.setVisible(true);
- });
- }
- }
- class DateDifferencePanel extends JPanel {
-
- private DayCalculatorService service;
- private JTextField startDateField;
- private JTextField endDateField;
- private JButton calculateButton;
- private JTextArea resultArea;
-
- public DateDifferencePanel(DayCalculatorService service) {
- this.service = service;
- initUI();
- }
-
- private void initUI() {
- setLayout(new BorderLayout(10, 10));
- setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
-
- // 输入面板
- JPanel inputPanel = new JPanel(new GridLayout(2, 2, 5, 5));
-
- inputPanel.add(new JLabel("开始日期 (yyyy-MM-dd):"));
- startDateField = new JTextField();
- inputPanel.add(startDateField);
-
- inputPanel.add(new JLabel("结束日期 (yyyy-MM-dd):"));
- endDateField = new JTextField();
- inputPanel.add(endDateField);
-
- // 按钮面板
- JPanel buttonPanel = new JPanel();
- calculateButton = new JButton("计算");
- calculateButton.addActionListener(new CalculateButtonListener());
- buttonPanel.add(calculateButton);
-
- // 结果面板
- resultArea = new JTextArea(10, 30);
- resultArea.setEditable(false);
- JScrollPane scrollPane = new JScrollPane(resultArea);
-
- // 添加组件
- add(inputPanel, BorderLayout.NORTH);
- add(buttonPanel, BorderLayout.CENTER);
- add(scrollPane, BorderLayout.SOUTH);
- }
-
- private class CalculateButtonListener implements ActionListener {
- @Override
- public void actionPerformed(ActionEvent e) {
- try {
- String startDateStr = startDateField.getText();
- String endDateStr = endDateField.getText();
-
- if (startDateStr.isEmpty() || endDateStr.isEmpty()) {
- JOptionPane.showMessageDialog(DateDifferencePanel.this,
- "请输入开始日期和结束日期", "错误", JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- LocalDate startDate = LocalDate.parse(startDateStr, formatter);
- LocalDate endDate = LocalDate.parse(endDateStr, formatter);
-
- // 计算各种日期差
- long daysBetween = service.calculateDaysBetweenJava8(startDate, endDate);
- long monthsBetween = service.calculateMonthsBetweenJava8(startDate, endDate);
- long yearsBetween = service.calculateYearsBetweenJava8(startDate, endDate);
- String periodBetween = service.calculatePeriodBetween(startDate, endDate);
-
- // 显示结果
- StringBuilder result = new StringBuilder();
- result.append("日期差计算结果:\n\n");
- result.append("天数差: ").append(daysBetween).append(" 天\n");
- result.append("月数差: ").append(monthsBetween).append(" 个月\n");
- result.append("年数差: ").append(yearsBetween).append(" 年\n");
- result.append("详细间隔: ").append(periodBetween).append("\n");
-
- resultArea.setText(result.toString());
-
- } catch (DateTimeParseException ex) {
- JOptionPane.showMessageDialog(DateDifferencePanel.this,
- "日期格式错误,请使用 yyyy-MM-dd 格式", "错误", JOptionPane.ERROR_MESSAGE);
- } catch (Exception ex) {
- JOptionPane.showMessageDialog(DateDifferencePanel.this,
- "计算错误: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
- }
- }
- }
- }
- class WorkdayCalculatorPanel extends JPanel {
-
- private DayCalculatorService service;
- private JTextField startDateField;
- private JTextField endDateField;
- private JButton calculateButton;
- private JTextArea resultArea;
- private JComboBox<String> weekendRuleCombo;
- private JCheckBox excludeHolidaysCheck;
-
- public WorkdayCalculatorPanel(DayCalculatorService service) {
- this.service = service;
- initUI();
- }
-
- private void initUI() {
- setLayout(new BorderLayout(10, 10));
- setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
-
- // 输入面板
- JPanel inputPanel = new JPanel(new GridLayout(4, 2, 5, 5));
-
- inputPanel.add(new JLabel("开始日期 (yyyy-MM-dd):"));
- startDateField = new JTextField();
- inputPanel.add(startDateField);
-
- inputPanel.add(new JLabel("结束日期 (yyyy-MM-dd):"));
- endDateField = new JTextField();
- inputPanel.add(endDateField);
-
- inputPanel.add(new JLabel("周末规则:"));
- weekendRuleCombo = new JComboBox<>(new String[]{
- "周六周日为周末", "仅周日为周末", "周五周六为周末", "自定义周末"
- });
- inputPanel.add(weekendRuleCombo);
-
- excludeHolidaysCheck = new JCheckBox("排除节假日");
- inputPanel.add(excludeHolidaysCheck);
- inputPanel.add(new JLabel()); // 占位
-
- // 按钮面板
- JPanel buttonPanel = new JPanel();
- calculateButton = new JButton("计算工作日");
- calculateButton.addActionListener(new CalculateButtonListener());
- buttonPanel.add(calculateButton);
-
- // 结果面板
- resultArea = new JTextArea(10, 30);
- resultArea.setEditable(false);
- JScrollPane scrollPane = new JScrollPane(resultArea);
-
- // 添加组件
- add(inputPanel, BorderLayout.NORTH);
- add(buttonPanel, BorderLayout.CENTER);
- add(scrollPane, BorderLayout.SOUTH);
- }
-
- private class CalculateButtonListener implements ActionListener {
- @Override
- public void actionPerformed(ActionEvent e) {
- try {
- String startDateStr = startDateField.getText();
- String endDateStr = endDateField.getText();
-
- if (startDateStr.isEmpty() || endDateStr.isEmpty()) {
- JOptionPane.showMessageDialog(WorkdayCalculatorPanel.this,
- "请输入开始日期和结束日期", "错误", JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- LocalDate startDate = LocalDate.parse(startDateStr, formatter);
- LocalDate endDate = LocalDate.parse(endDateStr, formatter);
-
- // 设置周末规则
- int weekendRuleIndex = weekendRuleCombo.getSelectedIndex();
- switch (weekendRuleIndex) {
- case 0: // 周六周日为周末
- service.setWeekendDays(Arrays.asList(
- java.time.DayOfWeek.SATURDAY,
- java.time.DayOfWeek.SUNDAY));
- break;
- case 1: // 仅周日为周末
- service.setWeekendDays(Arrays.asList(
- java.time.DayOfWeek.SUNDAY));
- break;
- case 2: // 周五周六为周末
- service.setWeekendDays(Arrays.asList(
- java.time.DayOfWeek.FRIDAY,
- java.time.DayOfWeek.SATURDAY));
- break;
- case 3: // 自定义周末
- // 这里可以添加更复杂的自定义周末设置界面
- JOptionPane.showMessageDialog(WorkdayCalculatorPanel.this,
- "自定义周末功能待实现", "提示", JOptionPane.INFORMATION_MESSAGE);
- return;
- }
-
- // 如果需要排除节假日,添加一些示例节假日
- if (excludeHolidaysCheck.isSelected()) {
- // 这里应该从数据库或配置文件加载节假日
- // 这里仅添加几个示例节假日
- service.addHoliday(LocalDate.of(2023, 1, 1)); // 元旦
- service.addHoliday(LocalDate.of(2023, 1, 21)); // 春节
- service.addHoliday(LocalDate.of(2023, 1, 22)); // 春节
- service.addHoliday(LocalDate.of(2023, 10, 1)); // 国庆节
- }
-
- // 计算工作日
- long workdays = service.calculateWorkdaysBetween(startDate, endDate);
-
- // 显示结果
- StringBuilder result = new StringBuilder();
- result.append("工作日计算结果:\n\n");
- result.append("工作日数量: ").append(workdays).append(" 天\n");
-
- // 获取所有工作日列表
- java.util.List<LocalDate> workdayList = service.getWorkdaysBetween(startDate, endDate);
- result.append("\n工作日列表 (前10个):\n");
- for (int i = 0; i < Math.min(10, workdayList.size()); i++) {
- result.append(workdayList.get(i).toString()).append("\n");
- }
- if (workdayList.size() > 10) {
- result.append("... 共 ").append(workdayList.size()).append(" 个工作日\n");
- }
-
- resultArea.setText(result.toString());
-
- } catch (DateTimeParseException ex) {
- JOptionPane.showMessageDialog(WorkdayCalculatorPanel.this,
- "日期格式错误,请使用 yyyy-MM-dd 格式", "错误", JOptionPane.ERROR_MESSAGE);
- } catch (Exception ex) {
- JOptionPane.showMessageDialog(WorkdayCalculatorPanel.this,
- "计算错误: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
- }
- }
- }
- }
- class HolidayManagerPanel extends JPanel {
-
- private DayCalculatorService service;
- private JTextField holidayField;
- private JButton addButton;
- private JButton removeButton;
- private JTextArea holidayListArea;
- private JButton loadSampleHolidaysButton;
-
- public HolidayManagerPanel(DayCalculatorService service) {
- this.service = service;
- initUI();
- }
-
- private void initUI() {
- setLayout(new BorderLayout(10, 10));
- setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
-
- // 输入面板
- JPanel inputPanel = new JPanel(new GridLayout(1, 2, 5, 5));
-
- inputPanel.add(new JLabel("节假日日期 (yyyy-MM-dd):"));
- holidayField = new JTextField();
- inputPanel.add(holidayField);
-
- // 按钮面板
- JPanel buttonPanel = new JPanel(new GridLayout(1, 3, 5, 5));
- addButton = new JButton("添加节假日");
- addButton.addActionListener(new AddButtonListener());
- removeButton = new JButton("移除节假日");
- removeButton.addActionListener(new RemoveButtonListener());
- loadSampleHolidaysButton = new JButton("加载示例节假日");
- loadSampleHolidaysButton.addActionListener(new LoadSampleHolidaysListener());
- buttonPanel.add(addButton);
- buttonPanel.add(removeButton);
- buttonPanel.add(loadSampleHolidaysButton);
-
- // 节假日列表面板
- holidayListArea = new JTextArea(10, 30);
- holidayListArea.setEditable(false);
- JScrollPane scrollPane = new JScrollPane(holidayListArea);
-
- // 添加组件
- add(inputPanel, BorderLayout.NORTH);
- add(buttonPanel, BorderLayout.CENTER);
- add(scrollPane, BorderLayout.SOUTH);
- }
-
- private void updateHolidayList() {
- // 由于我们的服务类没有提供获取节假日列表的方法,这里暂时显示示例文本
- holidayListArea.setText("节假日列表功能待实现\n\n" +
- "已添加的节假日将影响工作日计算结果。\n" +
- "使用'加载示例节假日'按钮添加一些示例节假日。");
- }
-
- private class AddButtonListener implements ActionListener {
- @Override
- public void actionPerformed(ActionEvent e) {
- try {
- String holidayStr = holidayField.getText();
-
- if (holidayStr.isEmpty()) {
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "请输入节假日日期", "错误", JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- LocalDate holidayDate = LocalDate.parse(holidayStr, formatter);
-
- // 添加节假日
- service.addHoliday(holidayDate);
-
- // 清空输入框
- holidayField.setText("");
-
- // 更新节假日列表
- updateHolidayList();
-
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "节假日添加成功", "成功", JOptionPane.INFORMATION_MESSAGE);
-
- } catch (DateTimeParseException ex) {
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "日期格式错误,请使用 yyyy-MM-dd 格式", "错误", JOptionPane.ERROR_MESSAGE);
- } catch (Exception ex) {
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "添加失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
- }
- }
- }
-
- private class RemoveButtonListener implements ActionListener {
- @Override
- public void actionPerformed(ActionEvent e) {
- try {
- String holidayStr = holidayField.getText();
-
- if (holidayStr.isEmpty()) {
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "请输入要移除的节假日日期", "错误", JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- LocalDate holidayDate = LocalDate.parse(holidayStr, formatter);
-
- // 移除节假日
- service.removeHoliday(holidayDate);
-
- // 清空输入框
- holidayField.setText("");
-
- // 更新节假日列表
- updateHolidayList();
-
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "节假日移除成功", "成功", JOptionPane.INFORMATION_MESSAGE);
-
- } catch (DateTimeParseException ex) {
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "日期格式错误,请使用 yyyy-MM-dd 格式", "错误", JOptionPane.ERROR_MESSAGE);
- } catch (Exception ex) {
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "移除失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
- }
- }
- }
-
- private class LoadSampleHolidaysListener implements ActionListener {
- @Override
- public void actionPerformed(ActionEvent e) {
- try {
- // 添加一些示例节假日
- service.addHoliday(LocalDate.of(2023, 1, 1)); // 元旦
- service.addHoliday(LocalDate.of(2023, 1, 21)); // 春节
- service.addHoliday(LocalDate.of(2023, 1, 22)); // 春节
- service.addHoliday(LocalDate.of(2023, 1, 23)); // 春节
- service.addHoliday(LocalDate.of(2023, 1, 24)); // 春节
- service.addHoliday(LocalDate.of(2023, 1, 25)); // 春节
- service.addHoliday(LocalDate.of(2023, 1, 26)); // 春节
- service.addHoliday(LocalDate.of(2023, 1, 27)); // 春节
- service.addHoliday(LocalDate.of(2023, 4, 5)); // 清明节
- service.addHoliday(LocalDate.of(2023, 5, 1)); // 劳动节
- service.addHoliday(LocalDate.of(2023, 6, 22)); // 端午节
- service.addHoliday(LocalDate.of(2023, 6, 23)); // 端午节
- service.addHoliday(LocalDate.of(2023, 6, 24)); // 端午节
- service.addHoliday(LocalDate.of(2023, 9, 29)); // 中秋节
- service.addHoliday(LocalDate.of(2023, 9, 30)); // 中秋节
- service.addHoliday(LocalDate.of(2023, 10, 1)); // 国庆节
- service.addHoliday(LocalDate.of(2023, 10, 2)); // 国庆节
- service.addHoliday(LocalDate.of(2023, 10, 3)); // 国庆节
- service.addHoliday(LocalDate.of(2023, 10, 4)); // 国庆节
- service.addHoliday(LocalDate.of(2023, 10, 5)); // 国庆节
- service.addHoliday(LocalDate.of(2023, 10, 6)); // 国庆节
-
- // 更新节假日列表
- updateHolidayList();
-
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "示例节假日加载成功", "成功", JOptionPane.INFORMATION_MESSAGE);
-
- } catch (Exception ex) {
- JOptionPane.showMessageDialog(HolidayManagerPanel.this,
- "加载失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
- }
- }
- }
- }
复制代码
7.4 测试用例
让我们创建一些测试用例来验证我们的系统:
- package com.daycalculator.test;
- import com.daycalculator.core.DayCalculatorService;
- import org.junit.jupiter.api.Test;
- import static org.junit.jupiter.api.Assertions.*;
- import java.time.LocalDate;
- import java.time.Month;
- import java.util.Arrays;
- import java.util.List;
- public class DateCalculatorTest {
-
- private DayCalculatorService service = new DayCalculatorService();
-
- @Test
- public void testCalculateDaysBetween() {
- LocalDate startDate = LocalDate.of(2023, Month.JANUARY, 1);
- LocalDate endDate = LocalDate.of(2023, Month.JANUARY, 10);
-
- long days = service.calculateDaysBetweenJava8(startDate, endDate);
- assertEquals(9, days); // 不包括开始日期
- }
-
- @Test
- public void testCalculateWorkdaysBetween() {
- LocalDate startDate = LocalDate.of(2023, Month.JANUARY, 2); // 周一
- LocalDate endDate = LocalDate.of(2023, Month.JANUARY, 8); // 周日
-
- long workdays = service.calculateWorkdaysBetween(startDate, endDate);
- assertEquals(5, workdays); // 周一到周五
- }
-
- @Test
- public void testCalculateWorkdaysWithHolidays() {
- LocalDate startDate = LocalDate.of(2023, Month.JANUARY, 2); // 周一
- LocalDate endDate = LocalDate.of(2023, Month.JANUARY, 8); // 周日
-
- // 添加一个节假日(周三)
- service.addHoliday(LocalDate.of(2023, Month.JANUARY, 4));
-
- long workdays = service.calculateWorkdaysBetween(startDate, endDate);
- assertEquals(4, workdays); // 周一、周二、周四、周五
- }
-
- @Test
- public void testDateArithmetic() {
- LocalDate date = LocalDate.of(2023, Month.JANUARY, 1);
-
- LocalDate plus10Days = service.addDays(date, 10);
- assertEquals(LocalDate.of(2023, Month.JANUARY, 11), plus10Days);
-
- LocalDate minus2Months = service.subtractMonths(date, 2);
- assertEquals(LocalDate.of(2022, Month.NOVEMBER, 1), minus2Months);
-
- LocalDate plus1Year = service.addYears(date, 1);
- assertEquals(LocalDate.of(2024, Month.JANUARY, 1), plus1Year);
- }
-
- @Test
- public void testGetNthWorkday() {
- LocalDate startDate = LocalDate.of(2023, Month.JANUARY, 1); // 周日
-
- // 第5个工作日应该是周五
- LocalDate nthWorkday = service.getNthWorkday(startDate, 5);
- assertEquals(LocalDate.of(2023, Month.JANUARY, 6), nthWorkday);
- }
-
- @Test
- public void testCustomWeekend() {
- // 设置周末只有周日
- service.setWeekendDays(Arrays.asList(java.time.DayOfWeek.SUNDAY));
-
- LocalDate startDate = LocalDate.of(2023, Month.JANUARY, 2); // 周一
- LocalDate endDate = LocalDate.of(2023, Month.JANUARY, 8); // 周日
-
- long workdays = service.calculateWorkdaysBetween(startDate, endDate);
- assertEquals(6, workdays); // 周一到周六
- }
- }
复制代码
8. 扩展功能和优化
8.1 性能优化
对于大量日期计算,我们可以进行一些性能优化:
- package com.daycalculator.core;
- import java.time.LocalDate;
- import java.util.List;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.ConcurrentMap;
- public class OptimizedWorkdayCalculator extends CustomWorkdayCalculator {
-
- // 缓存计算结果
- private ConcurrentMap<String, Long> workdayCache = new ConcurrentHashMap<>();
- private ConcurrentMap<String, List<LocalDate>> workdayListCache = new ConcurrentHashMap<>();
-
- @Override
- public long calculateWorkdaysBetween(LocalDate startDate, LocalDate endDate) {
- String cacheKey = startDate.toString() + "_" + endDate.toString();
-
- // 检查缓存
- Long cachedResult = workdayCache.get(cacheKey);
- if (cachedResult != null) {
- return cachedResult;
- }
-
- // 计算结果
- long result = super.calculateWorkdaysBetween(startDate, endDate);
-
- // 存入缓存
- workdayCache.put(cacheKey, result);
-
- return result;
- }
-
- @Override
- public List<LocalDate> getWorkdaysBetween(LocalDate startDate, LocalDate endDate) {
- String cacheKey = startDate.toString() + "_" + endDate.toString();
-
- // 检查缓存
- List<LocalDate> cachedResult = workdayListCache.get(cacheKey);
- if (cachedResult != null) {
- return cachedResult;
- }
-
- // 计算结果
- List<LocalDate> result = super.getWorkdaysBetween(startDate, endDate);
-
- // 存入缓存
- workdayListCache.put(cacheKey, result);
-
- return result;
- }
-
- // 清除缓存
- public void clearCache() {
- workdayCache.clear();
- workdayListCache.clear();
- }
-
- // 清除特定日期范围的缓存
- public void clearCacheForRange(LocalDate startDate, LocalDate endDate) {
- String cacheKey = startDate.toString() + "_" + endDate.toString();
- workdayCache.remove(cacheKey);
- workdayListCache.remove(cacheKey);
- }
- }
复制代码
8.2 国际化支持
为了支持多语言,我们可以添加国际化功能:
- package com.daycalculator.util;
- import java.text.MessageFormat;
- import java.util.Locale;
- import java.util.ResourceBundle;
- public class I18nSupport {
-
- private static final String BUNDLE_NAME = "messages";
- private static ResourceBundle messages;
- private static Locale currentLocale = Locale.getDefault();
-
- static {
- loadMessages();
- }
-
- public static void setLocale(Locale locale) {
- currentLocale = locale;
- loadMessages();
- }
-
- private static void loadMessages() {
- try {
- messages = ResourceBundle.getBundle(BUNDLE_NAME, currentLocale);
- } catch (Exception e) {
- // 如果找不到指定语言的资源文件,使用默认语言
- messages = ResourceBundle.getBundle(BUNDLE_NAME, Locale.ENGLISH);
- }
- }
-
- public static String getMessage(String key) {
- try {
- return messages.getString(key);
- } catch (Exception e) {
- return key; // 如果找不到消息,返回键名
- }
- }
-
- public static String getMessage(String key, Object... params) {
- try {
- String pattern = messages.getString(key);
- return MessageFormat.format(pattern, params);
- } catch (Exception e) {
- return key; // 如果找不到消息,返回键名
- }
- }
- }
复制代码
创建资源文件:
• messages_en.properties(英文)
• messages_zh.properties(中文)
8.3 异常处理
创建自定义异常类:
- package com.daycalculator.exception;
- public class DateParseException extends Exception {
-
- public DateParseException() {
- super();
- }
-
- public DateParseException(String message) {
- super(message);
- }
-
- public DateParseException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public DateParseException(Throwable cause) {
- super(cause);
- }
- }
复制代码
9. 总结和展望
9.1 项目总结
本项目实现了一个功能全面的天数计算系统,包括:
• 基础日期操作(创建、格式化、解析)
• 简单天数差计算
• 高级时间处理(时间段计算、日期加减)
• 工作日计算(排除周末和节假日)
• 自定义工作日规则
• 用户界面
• 性能优化
• 国际化支持
通过这个项目,我们学习了:
• Java日期时间API的使用
• 从传统的Date和Calendar到现代的java.time包
• 如何设计灵活的工作日计算系统
• 如何构建一个完整的Java应用程序
• 性能优化技巧
• 国际化支持
9.2 未来展望
未来可以考虑的扩展方向:
1. Web界面:将系统转换为Web应用,使用Spring Boot等框架
2. 移动应用:开发Android或iOS版本
3. 云服务:将计算功能部署为云服务,提供API接口
4. 更多日期计算功能:如农历支持、工作班次计算等
5. 数据库集成:持久化节假日和工作日规则
6. 更多性能优化:如并行计算、更高效的缓存策略等
通过这个项目,我们不仅学习了Java日期时间处理的各种技术,还了解了如何构建一个完整的应用程序。希望这个项目能够帮助读者在实际工作中处理各种日期计算需求。
版权声明
1、转载或引用本网站内容(使用Eclipse开发天数计算系统从基础日期操作到高级时间处理从简单天数差到复杂工作日计算的完整项目实战)须注明原网址及作者(威震华夏关云长),并标明本网站网址(https://www.pixtech.cc/)。
2、对于不当转载或引用本网站内容而引起的民事纷争、行政处理或其他损失,本网站不承担责任。
3、对不遵守本声明或其他违法、恶意使用本网站内容者,本网站保留追究其法律责任的权利。
本文地址: https://www.pixtech.cc/thread-36902-1-1.html
|
|