Java
实用经验总结
--日期、数字
篇
1. 日期部分
对于像日期、时间和钱这样的对象来说,不同的国家、地区都有不同的显示格式。即便是同一地区,也可能存在差异。但是在不考虑国家化,时间格式相对固定的情形下,对于时间的处理还是相对比较简单的。在我最近所作的一个小程序里面,遇到了一些与日期有关的且不考虑国际化和复杂格式的问题。例如如何求两个日期所差的天数,所差的月数;将日期类转化为规定格式的字符串,将规定格式的日期字符串转成相应的日期类等等。下面我就以源码的形式逐一介绍上面提到的问题,需要注意的是这些代码都源于我做的一个名为DateUtil的类,其中独立的变量都是其中的成员变量,函数都是其成员函数:
1.1.成员变量简介:
要想对日期进行格式化首先就需要一个DateFormat类的实例,如果没有特殊需求的话,SimpleDateFormat类就是一个不错的选择,它可以将日期类格式化为在其构造函数中指定的日期格式。例如,如果我们想要将日期格式化为类似于2007-07-25这样的格式,我们就可以如下定义:
/**
以
yyyy
-
MM
-
dd
的形式显示格式
**/
SimpleDateFormat
sDateFormat
=
new
SimpleDateFormat(
"yyyy-MM-dd"
);
SimpleDateFormat
sFullFormat
=
new
SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss"
);
这里需要注意的是指定的日期格式一定要是
”yyyy-MM-dd”
,而不能是
”YYYY-MM-DD”
,否则的就不能正常显示。对于这个问题我没有深究,如果有对这个问题有研究的朋友欢迎留言。
下面的两个成员变量分别是日期分隔符字符串和字符串分隔器,专门用来解析字符串格式的日期。
/**
*
程序中主要的日期分隔符为
"
-
"
和
"/"
,且日期序列为
“
年
/
月
/
日
”
型,其内容缺一不可
*
例如
:
09/02/02
或
2009
-
02
-
02
**/
public
static
final
String
DATE_SEPARATOR
=
"-/"
;
/**
作日期分析之用
*/
static
StringTokenizer
sToken
;
1.2.取得两个日期之间所差天数的方法
鉴于java.util.Date类的绝大多数方法都不建议使用了,所以我也就不能够利用Date里面方便的getYear(),getMonth(),getDay()方法来计算日期差了—现在的JRE都可以自动升级,谁知道哪天SUN突然把Date这些API去掉了,那我就欲哭无泪了。不过话又说回来就算是能够使用这些方法,我们似乎也不太好算,因为每个月的日期数都不一样,如果单纯用两个日期的年月日信息来计算日期差还真是有些麻烦。基于以上两点原因,我在我的程序里采用了
GregorianCalendar做为日期计算的主力军。这个类有一些很实用的方法,如get(int),这个方法可以获得当前日期类的各项日期指标。比如我们有一个日期类名为gcDate,要获取它所在的年,月,日,至需要这么做:gcDate.get(
Calendar.
YEAR), gcDate.get(
Calendar.
MONTH)以及gcDate.get(
Calendar.
DATE)。不过由于使用这种方法获得的日期和月份都是日期类所指定的年份的,所以如果我们知道两个日期在同一年份的话才能使用gcDate1.get(
Calendar.
DATE)- gcDate2.get(
Calendar.
DATE)来获得日期差,否则就不能这么做。
所以如果想要获得不在同一年份的日期差的话就需要用到另一个有用的方法:
GregorianCalendar.add(int, int)
,这个方法可以让我们在日期类指定的日期指标(如年,月,日等)上加上一个数字,这个数字可以是正数也可以为负数。 其中第一个参数指定所要增加的指标,第二个参数指定增加的数量。例如我们调用gcDate.add(
Calendar.
DATE
,
1)的话,如果gcDate原来代表的时间为2007-07-24,那么调用之后就变成2007-07-25了。于是我们就可以这样计算日期差:让日期比较小的那个日期用add函数逐渐“逼近”那个较大的日期,直到两个日期完全相等为止。计数器中包含的数量即为两个日期的差值。
下面我给出了多个的计算日期差的方法,主要包含两个版本,一个版本参数为格式化字符串,另一个版本参数为GregorianCalendar。功能包括计算“今天与未来的某一天之间的日期差”和“给定两个日期的日期差”。主要的计算集中在最后一个daysBetween函数上,该函数接受两个GregorianCalendar类作为参数,并可以计算出两个日期之间的日期差,如果用户给出的较大的日期和较小的日期顺序颠倒的话,该函数会返回一个负数值。
/**
*
返回未来的某一天和今天所差的日期数
*
注意,这里要
clone
一个新的日期以免对原始日期类造成的修改。
*
而在
daysBetween(GregorianCalendar
pFormer,GregorianCalendar
pLatter)
就
*
直接处理而不进行
clone
动作,因为这里已经做了
:)
**/
public
static
int
daysBetween(GregorianCalendar pFurtherDay){
GregorianCalendar vToday =
new
GregorianCalendar();
GregorianCalendar vFurtherDay = (GregorianCalendar) pFurtherDay.clone();
return
daysBetween(vToday,vFurtherDay);
}
/**
上面函数的
String
版本
**/
public
static
int
daysBetween(String pFurtherDayStr){
GregorianCalendar vFurtherDay = DateUtil.parse2Cal(pFurtherDayStr);
GregorianCalendar vToday =
new
GregorianCalendar();
return
daysBetween(vToday,vFurtherDay);
}
/**
返回较晚的时间
(latter)
与较早的时间
(former)
所差的天数
**/
public
static
int
daysBetween(String pFormerStr,String pLatterStr){
GregorianCalendar pFormer = DateUtil.parse2Cal(pFormerStr);
GregorianCalendar pLatter = DateUtil.parse2Cal(pLatterStr);
return
daysBetween(pFormer,pLatter);
}
/**
返回较晚的时间
(latter)
与较早的时间
(former)
所差的天数
**/
public
static
int
daysBetween(GregorianCalendar pFormer,GregorianCalendar pLatter){
GregorianCalendar vFormer = pFormer,vLatter = pLatter;
boolean
vPositive =
true
;
if
( pFormer.before(pLatter) ){
vFormer = pFormer;
vLatter = pLatter;
}
else
{
vFormer = pLatter;
vLatter = pFormer;
vPositive =
false
;
}
vFormer.set(Calendar.
MILLISECOND
,0);
vFormer.set(Calendar.
SECOND
,0);
vFormer.set(Calendar.
MINUTE
,0);
vFormer.set(Calendar.
HOUR_OF_DAY
,0);
vLatter.set(Calendar.
MILLISECOND
,0);
vLatter.set(Calendar.
SECOND
,0);
vLatter.set(Calendar.
MINUTE
,0);
vLatter.set(Calendar.
HOUR_OF_DAY
,0);
int
vCounter = 0;
while
(vFormer.before(vLatter)){
vFormer.add(Calendar.
DATE
, 1);
vCounter++;
}
if
( vPositive)
return
vCounter;
else
return
-vCounter;
}
1.3.两个日期的月份差
获得两个日期的月份差的方法与获得日期差基本一致。但需要注意的是,计算月份差不能简单用before()来进行计算。还需要考虑到他们的年份及月份是否同时相等,只有在这种情况下,才能获得月份差的正确数值。下面同样给出了月份差的两个版本的多个函数,与日期差基本一致,这里就不再赘述。
/**
*
给定两个时间相差的月数
*/
//
本月和未来一个月的月份差
public
static
int
monthsBetween(GregorianCalendar pFurtherMonth){
GregorianCalendar vToday =
new
GregorianCalendar();
GregorianCalendar vFurtherMonth = (GregorianCalendar) pFurtherMonth.clone();
return
monthsBetween(vToday,vFurtherMonth);
}
/**
给定月分和本月的月份差
**/
public
static
int
monthsBetween(String pFurtherMonth){
GregorianCalendar vToday =
new
GregorianCalendar();
GregorianCalendar vFurtherMonth = DateUtil.parse2Cal(pFurtherMonth);
return
monthsBetween(vToday,vFurtherMonth);
}
/**
给定两个时间相差的月数
,String
版
**/
public
static
int
monthsBetween(String pFormerStr,String pLatterStr){
GregorianCalendar vFormer = DateUtil.parse2Cal(pFormerStr);
GregorianCalendar vLatter = DateUtil.parse2Cal(pLatterStr);
return
monthsBetween(vFormer,vLatter);
}
public
static
int
monthsBetween(GregorianCalendar pFormer,GregorianCalendar pLatter){
GregorianCalendar vFormer = pFormer,vLatter = pLatter;
boolean
vPositive =
true
;
if
( pFormer.before(pLatter) ){
vFormer = pFormer;
vLatter = pLatter;
}
else
{
vFormer = pLatter;
vLatter = pFormer;
vPositive =
false
;
}
int
vCounter = 0;
while
(vFormer.get(vFormer.
YEAR
) != vLatter.get(vLatter.
YEAR
) ||
vFormer.get(vFormer.
MONTH
) != vLatter.get(vLatter.
MONTH
)){
vFormer.add(Calendar.
MONTH
, 1);
vCounter++;
}
if
( vPositive)
return
vCounter;
else
return
-vCounter;
}
1.4.格式转换
将日期类转换成制定格式的字符串只需要调用DateFormat.format()即可。而反过来就比较麻烦,我们需要对字符串进行分析,找出其年,月,日的值分别为何,然后再用
GregorianCalendar(vYear,vMonth,vDayOfMonth)
构建一个新的日期类。
/**
将日期变为字符串格式
**/
public
static
String format(GregorianCalendar pCal){
return
sDateFormat
.format(pCal.getTime());
}
public
static
String format(Date pDate){
return
sDateFormat
.format(pDate);
}
public
static
String fullFormat(Date pDate){
return
sFullFormat
.format(pDate);
}
/**
将字符串格式的日期转换为
Calender
**/
public
static
GregorianCalendar parse2Cal(String pDateStr){
sToken
=
new
StringTokenizer(pDateStr,
DATE_SEPARATOR
);
int
vYear = Integer.parseInt(
sToken
.nextToken());
//GregorianCalendar
的月份是从
0
开始算起的,变态!!
int
vMonth = Integer.parseInt(
sToken
.nextToken())-1;
int
vDayOfMonth = Integer.parseInt(
sToken
.nextToken());
return
new
GregorianCalendar(vYear,vMonth,vDayOfMonth);
}
/**
将字符串类型的日期
(yyyy
-
MM
-
dd)
转换成
Date
**/
public
static
Date parse2Date(String pDate){
try
{
return
sDateFormat
.parse(pDate);
}
catch
(ParseException ex) {
return
null
;
}
}
1.5.其他
这部分主要是一些常用方法的包装,包括获得今天是本月的第几天,本月是本年的第几个月,获得给定字符串日期的月份数,以及获得下面这个月份区间[本月,本月+
pZoneSize]里的月份列表。即如果本月为6月,pZoneSize=6,则获得的月份列表应该为{6,7,8,9,10,11,12}
/**
返回今天是本月的第几天
**/
public
static
int
dayOfMonthOfToday(){
GregorianCalendar vTodayCal =
new
GregorianCalendar();
return
vTodayCal.get(vTodayCal.
DAY_OF_MONTH
);
}
/**
返回本月是本年的第几个月
**/
public
static
int
monthOfYear(){
GregorianCalendar vTodayCal =
new
GregorianCalendar();
return
vTodayCal.get(vTodayCal.
MONTH
)+1;
}
//
返回给定日期的月份
public
static
String getMonth(String pFormattedDate){
StringTokenizer vSt =
new
StringTokenizer(pFormattedDate,
"-"
);
vSt.nextToken();
//
跳过年份
int
val = Integer.parseInt(vSt.nextToken());
return
val+
""
;
}
/**
获得从本月开始到本月
+pZoneSize
区间内的月数
**/
public
static
String[] monthList(
int
pZoneSize){
String[] vMonthList =
new
String[pZoneSize];
GregorianCalendar vTodayCal =
new
GregorianCalendar();
for
(
int
i = 0; i < pZoneSize; i++){
vMonthList[i] = String.valueOf(vTodayCal.get(vTodayCal.
MONTH
)+1);
vTodayCal.roll(vTodayCal.
MONTH
,
true
);
}
return
vMonthList;
}
2. 数字部分:
这一部分我的工作比较简单,首先是格式化给定的Double变量,让其只显示两位小数。其次是由于我所做的程序付款额都是以万作为单位的,所以在必要的时候将其转为以万为单位的,带有两位小数的数字。由于比较简单,这里就不多说,各位看代码就好:
/**
仅显示小数点后两位的
Formater
**/
public
static
DecimalFormat
formatter
=
new
DecimalFormat(
"####.##"
);
/**
将给定的数字变成小数点后两位的字符串
**/
public
static
String format(
double
pSrcVal){
return
formatter
.format(pSrcVal);
}
/**
将原始数据除以
10000
所得的结果
**/
public
static
String fromat2Myriad(
double
pSrcVal){
return
formatter
.format(pSrcVal/10000.0);
}
/**
将原始数据除以
10000
所得的结果
**/
public
static
String fromat2Myriad(String pSrcVal){
return
formatter
.format(Double.parseDouble(pSrcVal)/10000.0);
}
/**
将给定的数字保留两位小数返回
**/
public
static
double
format2Double(
double
pSrcVal){
return
(
double
)Math.round(pSrcVal*100)/100.0;
}