日期计算器
一、 系统描述
完成日期计算器的编写。
系统功能:
1.输入日期,完成星期几的计算,并输出星期值
2.输入日期加天数,完成日期加/减天数的计算,并输出新日期
3.输入日期以及一个年月日,完成日期加/减该年月日的计算,并输出新 日期。
二、 实验要求和目的
1、理解并掌握图形界面的设计。
2、理解整体算法的思路,即actionPerformed()的算法。
3、输入日期,完成星期几的计算,并输出星期值(注意日期输入的合法性)
4、输入日期加天数,完成日期加天数的计算,并输出新日期(注意日期的合法输入,以及注意日期不能加日期,既第二次输入的只能是天数)
5、输入日期以及一个年月日,完成日期加/减该年月日的计算,并输出新日期。 6、进一步学习JAVA的编程思想。
三、 实验条件
编译器: Eclipse Java Oxygen
Java环境: JDK 1.8.0
操作系统: Windows 10 家庭中文版
四、思路分析以及详细设计
布局管理程序(比如FlowLayout(流式布局)、BorderLayout(区域边框布局)、GridLayout(顺序依次排列))进行布局设计,同时限定了窗体的大小,防止用户无意改变窗口大小;
详细设计:
先利用FlowLayout设置用户界面上的屏幕组件的格式布局,然后使用BorderLayout将布局分为五个区域:北、南、东、西、中。利用GridLayout将中区域分割4*3,间隔10,10,用来存放数字按钮以及C和CE。东区域分割4*1,间隔10,10,用来存放+、-、/、=,北区域存放文本框。且将setResizable设为false表示生成的窗体大小是由程序员决定的,用户不可以自由改变该窗体的大小。
2. 封装了一个创建数字按钮的方法getNumberButton(int n),可以快速地进行增加数字按钮的操作;
详细设计:
利用JButton库创建按钮,并设计函数为其添加事件。
"yyyy年MM月dd日是 E"的形式编写了toStringDate()方法,其中包括日期输入错误的异常处理。
详细设计:
先利用SimpleDateForma和parset把String:"yyyy/MM/dd" 类型转换成Date类型,再利用SimpleDateFormat的format,将Date类型转换为"yyyy年MM月dd日是 E"的形式。
为了区分日期加的天数还是年月日,编写了isNumber和isDate俩个函数去进行格式判断。
详细设计:
利用String的matches进行格式判断即可,数字格式:"\\d{0,}",年月日格式:"\\d{0,4}/([0-9]|1[0-2]|)/([0-9]|[1-2][0-9]|3[0-1])"即XXXX/XX/XX、/xx/xx或//xx.
编写了getDate函数。
详细设计:
逐个读取字符串于一个缓冲区内,当读取到‘/’或是结束时,则读出缓冲区,将其转为整形依次赋值给year,month,day,并清空缓冲区。若读出的缓冲区为空,赋值即为0。
6. 鼠标使用,定义了一个ButtonActionListener类使用了ActionListener接口然后覆写了actionPerformed,去实现按键监听对应的功能。
详细设计:
判断按钮若为"=",使用了bolean布尔型属性来标记是否为第一次出现等号即转化为 "yyyy年MM月dd日是 E"的形式,若不是第一次即执行加减功能。若为"+"、"-"则保存之前的时间,等到下一个等号计算。若为“CE”则清空文本框并复原bolean,若为“C”则删除文本框中的一个字符。
了一个KeyboardListener类使用了KeyListener接口然后覆写了keyReleased,去实现按键监听对应的功能。
详细设计:
判断键盘输入若为enter,使用了bolean布尔型属性来标记是否为第一次出现等号即转化为 "yyyy年MM月dd日是 E"的形式,若不是第一次即执行加减功能。若为"+"、"-"则保存之前的时间,等到下一个等号计算。若为Backspace则删除文本框中的一个字符。
8.为实现+-操作、使用了isSymbol布尔型属性来标记当前状态,同时使用一个Calendar属性保存当前日期,在每步操作时都对其进行改变,最后=时输出。
详细设计:
定义一个全局变量isSymbol,遇到“+”时变为true,“-”时为false,然后遇到等号时去读取isSymbol判断执行加还是减。
- 主要代码清单
package ll;
import java.awt.BorderLayout; //布置容器的边框布局,五个区域:北、南、东、西、中
import java.awt.Dimension; //封装了一个构件的高度和宽度
import java.awt.FlowLayout; //流式布局,组件按照加入的先后顺序按照设置的对齐方式从左向右排列,一行排满到下一行开始继续排列
import java.awt.GridLayout; //切割出来的版面就如同表格一般整齐,加入的组件会按顺序由左至右、由上至下摆放
import java.awt.event.ActionEvent; //按钮事件处理的一个接口
import java.awt.event.ActionListener; //用于接收按钮操作事件的侦听器接口
import java.awt.event.KeyEvent; //键盘事件处理的一个接口
import java.awt.event.KeyListener; //用于接收键盘操作事件的侦听器接口
import java.text.ParseException; //异常类
import java.text.SimpleDateFormat; //日期-时间格式化
import java.util.Calendar; //日期处理库
import java.util.Date; //日期库
import javax.swing.JButton; //按钮
import javax.swing.JFrame; //屏幕上window的对象,能够最大化、最小化、关闭。
import javax.swing.JPanel; //面板容器类
import javax.swing.JTextField; //轻量级组件,编辑单行文本
@SuppressWarnings("serial") //声明long类型的静态final serialVersionUID字段
public class Calculator extends JFrame{
JTextField textFile; //文本框
Date date;
String fullString; //全局(第一个时间)
String before,after;
Calendar calendar;// 全局变量用来保存时间
int year = 0,month = 0,day = 0; // 全局变量用来保存年月日
//bolean:确保第一次都是将日期转换成yyyy年MM月dd日是 E形式
//isSymbol:判断符号是+还是-,true表示+
//键盘监听事件keyLitener
boolean bolean=true, isSymbol=true;
int m;
public Calculator() {
JFrame jframe = new JFrame("日期计算机");
jframe.setLayout(new FlowLayout()); //设置用户界面上的屏幕组件的格式布局
jframe.setBounds(500, 150, 750, 350); //在屏幕左上角的焦点为0坐标,向右移动500个像素,向下移动150个像素,对话框宽为750个像素,高为350个像素。
textFile = new JTextField(50); //文本框长度为50列
JPanel jpanel = new JPanel();
jpanel.setLayout(new BorderLayout()); //布置容器的边框布局,五个区域:北、南、东、西、中
JPanel jpanel_center = new JPanel(); //中区域
jpanel_center.setLayout(new GridLayout(4, 3, 10, 10)); //中区域分割4*3,间隔10,10
JPanel jpanel_north = new JPanel(); //北区域
JPanel jpanel_east = new JPanel(); //东区域
jpanel_east.setLayout(new GridLayout(4, 1, 10, 10));
//北区域文本框
jpanel_north.add(textFile);
jpanel.add(jpanel_north, BorderLayout.NORTH);
//中区域数字
for (int i = 1; i < 10; i++) {
jpanel_center.add(getNumberButton(i));
}
jpanel_center.add(getNumberButton("CE"));
jpanel_center.add(getNumberButton(0));
jpanel_center.add(getNumberButton("C"));
jpanel.add(jpanel_center, BorderLayout.CENTER);
//东区域符号
jpanel_east.add(getNumberButton("+"));
jpanel_east.add(getNumberButton("-"));
jpanel_east.add(getNumberButton("/"));
jpanel_east.add(getNumberButton("="));
jpanel.add(jpanel_east, BorderLayout.EAST);
jframe.add(jpanel);
//文本框加入键盘监听
KeyboardListener kl = new KeyboardListener ();
textFile.addKeyListener(kl);
jframe.setResizable(false); //设置此窗体是否可由用户调整大小,false时,表示生成的窗体大小是由程序员决定的,用户不可以自由改变该窗体的大小
jframe.setVisible(true); //数据模型已经构造好了,允许JVM可以根据数据模型执行paint方法开始画图并显示到屏幕上
}
private JButton getNumberButton(int n) {
JButton an = new JButton("" + n);
//设置按钮的大小
Dimension preferredSize = new Dimension(70, 55);
an.setPreferredSize(preferredSize);
ButtonActionListener bl = new ButtonActionListener ();
an.addActionListener(bl);
return an;
}
private JButton getNumberButton(String n) {
JButton an = new JButton("" + n);
//设置按钮的大小
Dimension preferredSize = new Dimension(70, 55);
an.setPreferredSize(preferredSize);
//监听
ButtonActionListener bl = new ButtonActionListener ();
an.addActionListener(bl);
return an;
}
public String toStringDate() {
// 首先把String:"yyyy/MM/dd" 类型转换成Date类型
SimpleDateFormat f = new SimpleDateFormat("yyyy/MM/dd");
String time= textFile.getText();
try {
date =f.parse(time);
} catch (ParseException e) {
textFile.setText("toStringDate异常");
}
String strDateFormat = "yyyy年MM月dd日是 E";
SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
return sdf.format(date);
}
public boolean isNumber(String s){
//判断是否为数字
String r="\\d{0,}";
return s.matches(r);
}
public boolean isDate(String s){
//判断是否为年月日
String r="\\d{0,4}/([0-9]|1[0-2]|)/([0-9]|[1-2][0-9]|3[0-1])"; //格式XXXX/XX/XX、/xx/xx、//xx.
return s.matches(r);
}
public void getDate(String s) {
//获取字符上的年月日
int j = 1;
String s1 = "";
for(int i=0; i<s.length(); i++) {
if(s.charAt(i)>=48 && s.charAt(i)<=57) {
s1 += s.charAt(i);
}
if(s.charAt(i) == '/' || i == s.length()-1 ) {
System.out.println(s1);
if(s1 == "") s1 = "0"; //无数字即为0
if(j == 1) {
j++;
year = Integer.parseInt(s1);
s1 = "";
}
else if(j == 2) {
j++;
month = Integer.parseInt(s1);
s1 = "";
}
else if(j == 3) {
j++;
day = Integer.parseInt(s1);
s1 = "";
}
}
}
}
public Calendar toDate(String s) throws Exception {
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
Date d = df.parse(s); //解析一个日期时间字符串,并返回 1970/1/1 午夜距离该日期时间的毫秒数
Calendar cal = Calendar.getInstance(); //单例模式创建Calendar类的实例,在主函数开始时调用,返回一个实例化对象,此对象是static的.
cal.setTime(d); //以毫秒设置 Date 对象,据 GMT 时间 1970 年 1 月 1 日午夜之间的毫秒数
return cal;
}
class ButtonActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getActionCommand().equals("=")) {
after=fullString;
if(bolean){ //第一个=必须转换日期格式
System.out.println("第一次发现等号,执行转换成日期格式");
after=textFile.getText();
System.out.println(after);
String regex="\\d{1,4}/([1-9]|1[0-2])/([1-9]|[1-2][0-9]|3[0-1])"; //格式XXXX/XX/XX,前面为空即是0,每个至少一位
System.out.println(after.matches(regex));
if(after.matches(regex)){ //格式判断
try {
textFile.setText(toStringDate());
} catch (Exception e1) {
textFile.setText("toStringDate()抛出异常!!!");
}
}
else{
textFile.setText("输入与日期格式不匹配");
}
bolean=false;
}
else { //非第一个=,日期计算
if(isNumber(after)) {
m = Integer.parseInt(after); //数字部分,天数操作
if(isSymbol){ //这里还要判断是+还是-,对日进行操作
calendar.add(Calendar.DATE, m);
}else{
calendar.add(Calendar.DATE, -m);
}
SimpleDateFormat sj=new SimpleDateFormat("yyyy/MM/dd");
fullString=sj.format(calendar.getTime());
System.out.println(fullString);
try { //
date =sj.parse(fullString);
String strDateFormat = "yyyy年MM月dd日是 E";
SimpleDateFormat s = new SimpleDateFormat(strDateFormat);
textFile.setText(s.format(date));
} catch (ParseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
else if(isDate(after)) { //天数操作,年月日
getDate(after);
if(isSymbol){ //这里还要判断是+还是-,对日进行操作
calendar.add(Calendar.DATE, day);
calendar.add(Calendar.MONTH, month);
calendar.add(Calendar.YEAR, year);
}
else{
calendar.add(Calendar.DATE, -day);
calendar.add(Calendar.MONTH, -month);
calendar.add(Calendar.YEAR, -year);
}
SimpleDateFormat sj=new SimpleDateFormat("yyyy/MM/dd");
fullString=sj.format(calendar.getTime());
System.out.println(fullString);
try { //
date =sj.parse(fullString);
String strDateFormat = "yyyy年MM月dd日是 E";
SimpleDateFormat s = new SimpleDateFormat(strDateFormat);
textFile.setText(s.format(date));
} catch (ParseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
else {
textFile.setText("输入格式不正确");
}
}
}
else if(e.getActionCommand().equals("+")) {
isSymbol=true;
before = fullString; //用来存储加号前面的数据
try {
calendar=toDate(before);
} catch (Exception e1) {
// TODO Auto-generated catch block
textFile.setText("toDate错误");
}
System.out.println("实现+");
textFile.setText("");
}
else if(e.getActionCommand().equals("-")) {
isSymbol=false;
before = fullString; //用来存储加号前面的数据
try {
calendar=toDate(before);
} catch (Exception e1) {
// TODO Auto-generated catch block
textFile.setText("toDate错误");
}
System.out.println("实现-");
textFile.setText("");
}
else if(e.getActionCommand().equals("CE")) {
System.out.println("实现CE");
textFile.setText("");
bolean=true;
}
else if(e.getActionCommand().equals("C")) {
System.out.println("实现C");
fullString = fullString.substring(0, fullString.length()-1);
textFile.setText(fullString);
}
else{
textFile.setText(textFile.getText()+e.getActionCommand());
fullString=textFile.getText(); //边按边存入全局变量
}
}
}
class KeyboardListener implements KeyListener{
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if(e.getKeyChar() == KeyEvent.VK_ENTER) {
after=fullString;
if(bolean){ //第一个=必须转换日期格式
System.out.println("第一次发现等号,执行转换成日期格式");
after=textFile.getText();
System.out.println(after);
String regex="\\d{1,4}/([1-9]|1[0-2])/([1-9]|[1-2][0-9]|3[0-1])"; //格式XXXX/XX/XX,前面为空即是0,每个至少一位
System.out.println(after.matches(regex));
if(after.matches(regex)){ //格式判断
try {
textFile.setText(toStringDate());
} catch (Exception e1) {
textFile.setText("toStringDate()抛出异常!!!");
}
}
else{
textFile.setText("输入与日期格式不匹配");
}
bolean=false;
}
else { //非第一个=,日期计算
if(isNumber(after)) {
m = Integer.parseInt(after); //数字部分,天数操作
if(isSymbol){ //这里还要判断是+还是-,对日进行操作
calendar.add(Calendar.DATE, m);
}else{
calendar.add(Calendar.DATE, -m);
}
SimpleDateFormat sj=new SimpleDateFormat("yyyy/MM/dd");
fullString=sj.format(calendar.getTime());
System.out.println(fullString);
try { //
date =sj.parse(fullString);
String strDateFormat = "yyyy年MM月dd日是 E";
SimpleDateFormat s = new SimpleDateFormat(strDateFormat);
textFile.setText(s.format(date));
} catch (ParseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
else if(isDate(after)) { //天数操作,年月日
getDate(after);
if(isSymbol){ //这里还要判断是+还是-,对日进行操作
calendar.add(Calendar.DATE, day);
calendar.add(Calendar.MONTH, month);
calendar.add(Calendar.YEAR, year);
}
else{
calendar.add(Calendar.DATE, -day);
calendar.add(Calendar.MONTH, -month);
calendar.add(Calendar.YEAR, -year);
}
SimpleDateFormat sj=new SimpleDateFormat("yyyy/MM/dd");
fullString=sj.format(calendar.getTime());
System.out.println(fullString);
try { //
date =sj.parse(fullString);
String strDateFormat = "yyyy年MM月dd日是 E";
SimpleDateFormat s = new SimpleDateFormat(strDateFormat);
textFile.setText(s.format(date));
} catch (ParseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
else {
textFile.setText("输入格式不正确");
}
}
}
else if(e.getKeyChar() == '+') {
isSymbol=true;
before = fullString; //用来存储加号前面的数据
try {
calendar=toDate(before);
} catch (Exception e1) {
// TODO Auto-generated catch block
textFile.setText("toDate错误");
}
System.out.println("实现+");
textFile.setText("");
}
else if(e.getKeyChar() == '-') {
isSymbol=false;
before = fullString; //用来存储加号前面的数据
try {
calendar=toDate(before);
} catch (Exception e1) {
// TODO Auto-generated catch block
textFile.setText("toDate错误");
}
System.out.println("实现-");
textFile.setText("");
}
else if(e.getKeyChar() == KeyEvent.VK_BACK_SPACE) {
System.out.println("实现C");
fullString=textFile.getText();
}
else{
textFile.setText(textFile.getText());
fullString=textFile.getText(); //边按边存入全局变量
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Calculator();
}
}
- 调试与运行情况
加20天
减30天
加上两年两月两日
七、 实验小结
系统设计的功能基本都完成了实现,该系统尽可能的模拟现实的情况,使得系统设计更加贴近于现实情况。但也存在一些问题,例如如果经过减操作之后的日期是公元前的日期,结果依然会显示为公元年份。使用时也需要按一定要求输入,需要先输入一个日期,按下等于号后,才能进行加减,并且不能连加,连减。还有键盘输入时,一定要注意必须是英文输入法。