package org.nutz.lang; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 一些时间相关的帮助函数 * * @author zozoh(zozohtnt@gmail.com) */ public abstract class Times { /** * 判断一年是否为闰年,如果给定年份小于1全部为 false * * @param year * 年份,比如 2012 就是二零一二年 * @return 给定年份是否是闰年 */ public static boolean leapYear(int year) { if (year < 4) return false; return (year % 400 == 0) || (year % 100 != 0 && year % 4 == 0); } /** * 判断某年(不包括自己)之前有多少个闰年 * * @param year * 年份,比如 2012 就是二零一二年 * @return 闰年的个数 */ public static int countLeapYear(int year) { // 因为要计算年份到公元元年(0001年)的年份跨度,所以减去1 int span = year - 1; return (span / 4) - (span / 100) + (span / 400); } /** * 将一个秒数(天中),转换成一个如下格式的数组: * * <pre> * [0-23][0-59[-059] * </pre> * * @param sec * 秒数 * @return 时分秒的数组 */ public static int[] T(int sec) { int[] re = new int[3]; re[0] = Math.min(23, sec / 3600); re[1] = Math.min(59, (sec - (re[0] * 3600)) / 60); re[2] = Math.min(59, sec - (re[0] * 3600) - (re[1] * 60)); return re; } /** * 将一个时间字符串,转换成一个一天中的绝对秒数 * * @param ts * 时间字符串,符合格式 "HH:mm:ss" 或者 "HH:mm" * @return 一天中的绝对秒数 */ public static int T(String ts) { String[] tss = Strings.splitIgnoreBlank(ts, ":"); if (null != tss) { // 仅仅到分钟 if (tss.length == 2) { int hh = Integer.parseInt(tss[0]); int mm = Integer.parseInt(tss[1]); return hh * 3600 + mm * 60; } // 到秒 if (tss.length == 3) { int hh = Integer.parseInt(tss[0]); int mm = Integer.parseInt(tss[1]); int ss = Integer.parseInt(tss[2]); return hh * 3600 + mm * 60 + ss; } } throw Lang.makeThrow("Wrong format of time string '%s'", ts); } /** * 返回服务器当前时间 * * @return 服务器当前时间 */ public static Date now() { return new Date(System.currentTimeMillis()); } private static Pattern _P_TIME = Pattern.compile("^((\\d{2,4})([/\\\\-])(\\d{1,2})([/\\\\-])(\\d{1,2}))?" + "(([ T])?" + "(\\d{1,2})(:)(\\d{1,2})((:)(\\d{1,2}))?" + "(([.])" + "(\\d{1,}))?)?" + "(([+-])(\\d{1,2})(:\\d{1,2})?)?" + "$"); private static Pattern _P_TIME_LONG = Pattern.compile("^[0-9]+(L)?$"); /** * 根据默认时区计算时间字符串的绝对毫秒数 * * @param ds * 时间字符串 * @return 绝对毫秒数 * * @see #ams(String, TimeZone) */ public static long ams(String ds) { return ams(ds, null); } /** * 根据字符串得到相对于 "UTC 1970-01-01 00:00:00" 的绝对毫秒数。 * 本函数假想给定的时间字符串是本地时间。所以计算出来结果后,还需要减去时差 * * 支持的时间格式字符串为: * * <pre> * yyyy-MM-dd HH:mm:ss * yyyy-MM-dd HH:mm:ss.SSS * yy-MM-dd HH:mm:ss; * yy-MM-dd HH:mm:ss.SSS; * yyyy-MM-dd; * yy-MM-dd; * HH:mm:ss; * HH:mm:ss.SSS; * </pre> * * 时间字符串后面可以跟 +8 或者 +8:00 表示 GMT+8:00 时区。 同理 -9 或者 -9:00 表示 GMT-9:00 时区 * * @param ds * 时间字符串 * @param tz * 你给定的时间字符串是属于哪个时区的 * @return 时间 * @see #_P_TIME */ public static long ams(String ds, TimeZone tz) { Matcher m = _P_TIME.matcher(ds); if (m.find()) { int yy = _int(m, 2, 1970); int MM = _int(m, 4, 1); int dd = _int(m, 6, 1); int HH = _int(m, 9, 0); int mm = _int(m, 11, 0); int ss = _int(m, 14, 0); int ms = _int(m, 17, 0); /* * zozoh: 先干掉,还是用 SimpleDateFormat 吧,"1980-05-01 15:17:23" 之前的日子 * 得出的时间竟然总是多 30 分钟 long day = (long) D1970(yy, MM, dd); long MS = * day * 86400000L; MS += (((long) HH) * 3600L + ((long) mm) * 60L + * ss) * 1000L; MS += (long) ms; * * // 如果没有指定时区 ... if (null == tz) { // 那么用字符串中带有的时区信息, if * (!Strings.isBlank(m.group(17))) { tz = * TimeZone.getTimeZone(String.format("GMT%s%s:00", m.group(18), * m.group(19))); // tzOffset = Long.parseLong(m.group(19)) // * * 3600000L // * (m.group(18).charAt(0) == '-' ? -1 : 1); * * } // 如果依然木有,则用系统默认时区 else { tz = TimeZone.getDefault(); } } * * // 计算 return MS - tz.getRawOffset() - tz.getDSTSavings(); */ String str = String.format("%04d-%02d-%02d %02d:%02d:%02d.%03d", yy, MM, dd, HH, mm, ss, ms); SimpleDateFormat df = (SimpleDateFormat) DF_DATE_TIME_MS4.clone(); // 那么用字符串中带有的时区信息 ... if (null == tz && !Strings.isBlank(m.group(18))) { tz = TimeZone.getTimeZone(String.format("GMT%s%s:00", m.group(19), m.group(20))); } // 指定时区 ... if (null != tz) df.setTimeZone(tz); // 解析返回 try { return df.parse(str).getTime(); } catch (ParseException e) { throw Lang.wrapThrow(e); } } else if (_P_TIME_LONG.matcher(ds).find()) { if (ds.endsWith("L")) ds.substring(0, ds.length() - 1); return Long.parseLong(ds); } throw Lang.makeThrow("Unexpect date format '%s'", ds); } /** * 这个接口函数是 1.b.49 提供了,下一版本将改名为 ams,预计在版本 1.b.51 之后被移除 * * @deprecated since 1.b.49 util 1.b.51 */ public static long ms(String ds, TimeZone tz) { return ams(ds, tz); } /** * 返回时间对象在一天中的毫秒数 * * @param d * 时间对象 * * @return 时间对象在一天中的毫秒数 */ public static long ms(Date d) { return ms(C(d)); } /** * 返回时间对象在一天中的毫秒数 * * @param c * 时间对象 * * @return 时间对象在一天中的毫秒数 */ public static int ms(Calendar c) { int ms = c.get(Calendar.HOUR_OF_DAY) * 3600000; ms += c.get(Calendar.MINUTE) * 60000; ms += c.get(Calendar.SECOND) * 1000; ms += c.get(Calendar.MILLISECOND); return ms; } /** * 返回当前时间在一天中的毫秒数 * * @return 当前时间在一天中的毫秒数 */ public static int ms() { return ms(Calendar.getInstance()); } /** * 根据一个当天的绝对毫秒数,得到一个时间字符串,格式为 "HH:mm:ss.EEE" * * @param ms * 当天的绝对毫秒数 * @return 时间字符串 */ public static String mss(int ms) { int sec = ms / 1000; ms = ms - sec * 1000; return secs((int) sec) + "." + Strings.alignRight(ms, 3, '0'); } /** * 根据一个当天的绝对秒数,得到一个时间字符串,格式为 "HH:mm:ss" * * @param sec * 当天的绝对秒数 * @return 时间字符串 */ public static String secs(int sec) { int hh = sec / 3600; sec -= hh * 3600; int mm = sec / 60; sec -= mm * 60; return Strings.alignRight(hh, 2, '0') + ":" + Strings.alignRight(mm, 2, '0') + ":" + Strings.alignRight(sec, 2, '0'); } /** * 返回时间对象在一天中的秒数 * * @param d * 时间对象 * * @return 时间对象在一天中的秒数 */ public static int sec(Date d) { Calendar c = C(d); int sec = c.get(Calendar.HOUR_OF_DAY) * 3600; sec += c.get(Calendar.MINUTE) * 60; sec += c.get(Calendar.SECOND); return sec; } /** * 返回当前时间在一天中的秒数 * * @return 当前时间在一天中的秒数 */ public static int sec() { return sec(now()); } /** * 根据字符串得到时间对象 * * @param ds * 时间字符串 * @return 时间 * * @see #ams(String) */ public static Date D(String ds) { return D(ams(ds)); } private static int _int(Matcher m, int index, int dft) { String s = m.group(index); if (Strings.isBlank(s)) return dft; return Integer.parseInt(s); } // 常量数组,一年每个月多少天 private static final int[] _MDs = new int[]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /** * 计算一个给定日期,距离 1970 年 1 月 1 日有多少天 * * @param yy * 年,比如 1999,或者 43 * @param MM * 月,一月为 1,十二月为 12 * @param dd * 日,每月一号为 1 * @return 距离 1970 年 1 月 1 日的天数 */ public static int D1970(int yy, int MM, int dd) { // 转换成相对公元元年的年份 // 如果给的年份小于 100,那么就认为是从 1970 开始算的年份 int year = (yy < 100 ? yy + 1970 : yy); // 得到今年之前的基本天数 int day = (year - 1970) * 365; // 补上闰年天数 day += countLeapYear(year) - countLeapYear(1970); // 计算今年本月之前的月份 int mi = Math.min(MM - 1, 11); boolean isLeapYear = leapYear(yy); for (int i = 0; i < mi; i++) { day += _MDs[i]; } // 考虑今年是闰年的情况 if (isLeapYear && MM > 2) { day++; } // 最后加上天数 day += Math.min(dd, _MDs[mi]) - 1; // 如果是闰年且本月是 2 月 if (isLeapYear && dd == 29) { day++; } // 如果是闰年并且过了二月 return day; } /** * 根据毫秒数得到时间 * * @param ms * 时间的毫秒数 * @return 时间 */ public static Date D(long ms) { return new Date(ms); } /** * 根据字符串得到时间 * * <pre> * 如果你输入了格式为 "yyyy-MM-dd HH:mm:ss" * 那么会匹配到秒 * * 如果你输入格式为 "yyyy-MM-dd" * 相当于你输入了 "yyyy-MM-dd 00:00:00" * </pre> * * @param ds * 时间字符串 * @return 时间 */ public static Calendar C(String ds) { return C(D(ds)); } /** * 根据日期对象得到时间 * * @param d * 时间对象 * @return 时间 */ public static Calendar C(Date d) { return C(d.getTime()); } /** * 根据毫秒数得到时间 * * @param ms * 时间的毫秒数 * @return 时间 */ public static Calendar C(long ms) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(ms); return c; } /** * 把时间转换成格式为 y-M-d H:m:s.S 的字符串 * * @param d * 时间对象 * @return 该时间的字符串形式 , 格式为 y-M-d H:m:s.S */ public static String sDTms(Date d) { return format(DF_DATE_TIME_MS, d); } /** * 把时间转换成格式为 yy-MM-dd HH:mm:ss.SSS 的字符串 * * @param d * 时间对象 * @return 该时间的字符串形式 , 格式为 yy-MM-dd HH:mm:ss.SSS */ public static String sDTms2(Date d) { return format(DF_DATE_TIME_MS2, d); } /** * 把时间转换成格式为 yyyy-MM-dd HH:mm:ss 的字符串 * * @param d * 时间对象 * @return 该时间的字符串形式 , 格式为 yyyy-MM-dd HH:mm:ss */ public static String sDT(Date d) { return format(DF_DATE_TIME, d); } /** * 把时间转换成格式为 yyyy-MM-dd 的字符串 * * @param d * 时间对象 * @return 该时间的字符串形式 , 格式为 yyyy-MM-dd */ public static String sD(Date d) { return format(DF_DATE, d); } /** * 将一个秒数(天中),转换成一个格式为 HH:mm:ss 的字符串 * * @param sec * 秒数 * @return 格式为 HH:mm:ss 的字符串 */ public static String sT(int sec) { int[] ss = T(sec); return Strings.alignRight(ss[0], 2, '0') + ":" + Strings.alignRight(ss[1], 2, '0') + ":" + Strings.alignRight(ss[2], 2, '0'); } /** * 将一个秒数(天中),转换成一个格式为 HH:mm 的字符串(精确到分钟) * * @param sec * 秒数 * @return 格式为 HH:mm:ss 的字符串 */ public static String sTmin(int sec) { int[] ss = T(sec); return Strings.alignRight(ss[0], 2, '0') + ":" + Strings.alignRight(ss[1], 2, '0'); } /** * 以本周为基础获得某一周的时间范围 * * @param off * 从本周偏移几周, 0 表示本周,-1 表示上一周,1 表示下一周 * * @return 时间范围(毫秒级别) * * @see org.nutz.lang.Times#weeks(long, int, int) */ public static Date[] week(int off) { return week(System.currentTimeMillis(), off); } /** * 以某周为基础获得某一周的时间范围 * * @param base * 基础时间,毫秒 * @param off * 从本周偏移几周, 0 表示本周,-1 表示上一周,1 表示下一周 * * @return 时间范围(毫秒级别) * * @see org.nutz.lang.Times#weeks(long, int, int) */ public static Date[] week(long base, int off) { return weeks(base, off, off); } /** * 以本周为基础获得时间范围 * * @param offL * 从本周偏移几周, 0 表示本周,-1 表示上一周,1 表示下一周 * @param offR * 从本周偏移几周, 0 表示本周,-1 表示上一周,1 表示下一周 * * @return 时间范围(毫秒级别) * * @see org.nutz.lang.Times#weeks(long, int, int) */ public static Date[] weeks(int offL, int offR) { return weeks(System.currentTimeMillis(), offL, offR); } /** * 按周获得某几周周一 00:00:00 到周六 的时间范围 * <p> * 它会根据给定的 offL 和 offR 得到一个时间范围 * <p> * 对本函数来说 week(-3,-5) 和 week(-5,-3) 是一个意思 * * @param base * 基础时间,毫秒 * @param offL * 从本周偏移几周, 0 表示本周,-1 表示上一周,1 表示下一周 * @param offR * 从本周偏移几周, 0 表示本周,-1 表示上一周,1 表示下一周 * * @return 时间范围(毫秒级别) */ public static Date[] weeks(long base, int offL, int offR) { int from = Math.min(offL, offR); int len = Math.abs(offL - offR); // 现在 Calendar c = Calendar.getInstance(); c.setTimeInMillis(base); Date[] re = new Date[2]; // 计算开始 c.add(Calendar.DAY_OF_YEAR, 7 * from); c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); re[0] = c.getTime(); // 计算结束 c.add(Calendar.DAY_OF_YEAR, 7 * (len + 1)); c.add(Calendar.MILLISECOND, -1); re[1] = c.getTime(); // 返回 return re; } private static final String[] _MMM = new String[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; /** * 将一个时间格式化成容易被人类阅读的格式 * * <pre> * 如果 1 分钟内,打印 Just Now * 如果 1 小时内,打印多少分钟 * 如果 1 天之内,打印多少小时之前 * 如果是今年之内,打印月份和日期 * 否则打印月份和年 * </pre> * * @param d * @return 日期字符串 */ public static String formatForRead(Date d) { long ms = System.currentTimeMillis() - d.getTime(); // 如果 1 分钟内,打印 Just Now if (ms < (60000)) { return "Just Now"; } // 如果 1 小时内,打印多少分钟 if (ms < (60 * 60000)) { return "" + (ms / 60000) + "Min."; } // 如果 1 天之内,打印多少小时之前 if (ms < (24 * 3600 * 1000)) { return "" + (ms / 3600000) + "hr."; } // 如果一周之内,打印多少天之前 if (ms < (7 * 24 * 3600 * 1000)) { return "" + (ms / (24 * 3600000)) + "Day"; } // 如果是今年之内,打印月份和日期 Calendar c = Calendar.getInstance(); int thisYear = c.get(Calendar.YEAR); c.setTime(d); int yy = c.get(Calendar.YEAR); int mm = c.get(Calendar.MONTH); if (thisYear == yy) { int dd = c.get(Calendar.DAY_OF_MONTH); return String.format("%s %d", _MMM[mm], dd); } // 否则打印月份和年 return String.format("%s %d", _MMM[mm], yy); } /** * 以给定的时间格式来安全的对时间进行格式化,并返回格式化后所对应的字符串 * * @param fmt * 时间格式 * @param d * 时间对象 * @return 格式化后的字符串 */ public static String format(DateFormat fmt, Date d) { return ((DateFormat) fmt.clone()).format(d); } /** * 以给定的时间格式来安全的对时间进行格式化,并返回格式化后所对应的字符串 * * @param fmt * 时间格式 * @param d * 时间对象 * @return 格式化后的字符串 */ public static String format(String fmt, Date d) { return new SimpleDateFormat(fmt, Locale.ENGLISH).format(d); } /** * 以给定的时间格式来安全的解析时间字符串,并返回解析后所对应的时间对象(包裹RuntimeException) * * @param fmt * 时间格式 * @param s * 时间字符串 * @return 该时间字符串对应的时间对象 */ public static Date parseq(DateFormat fmt, String s) { try { return parse(fmt, s); } catch (ParseException e) { throw Lang.wrapThrow(e); } } /** * 以给定的时间格式来安全的解析时间字符串,并返回解析后所对应的时间对象(包裹RuntimeException) * * @param fmt * 时间格式 * @param s * 时间字符串 * @return 该时间字符串对应的时间对象 */ public static Date parseq(String fmt, String s) { try { return parse(fmt, s); } catch (ParseException e) { throw Lang.wrapThrow(e); } } /** * 以给定的时间格式来安全的解析时间字符串,并返回解析后所对应的时间对象 * * @param fmt * 时间格式 * @param s * 日期时间字符串 * @return 该时间字符串对应的时间对象 */ public static Date parse(DateFormat fmt, String s) throws ParseException { return ((DateFormat) fmt.clone()).parse(s); } /** * 以给定的时间格式来安全的解析时间字符串,并返回解析后所对应的时间对象 * * @param fmt * 时间格式 * @param s * 日期时间字符串 * @return 该时间字符串对应的时间对象 */ public static Date parse(String fmt, String s) throws ParseException { return new SimpleDateFormat(fmt).parse(s); } private static final DateFormat DF_DATE_TIME_MS = new SimpleDateFormat("y-M-d H:m:s.S"); private static final DateFormat DF_DATE_TIME_MS2 = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS"); private static final DateFormat DF_DATE_TIME_MS4 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); private static final DateFormat DF_DATE_TIME = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final DateFormat DF_DATE = new SimpleDateFormat("yyyy-MM-dd"); // private static final DateFormat DF_MONTH = new // SimpleDateFormat("yyyy-MM"); public static final long T_1S = 1000; public static final long T_1M = 60 * 1000; public static final long T_1H = 60 * 60 * 1000; public static final long T_1D = 24 * 60 * 60 * 1000; /** * 方便的把时间换算成毫秒数 * * 支持几个单位, s(秒), m(分钟), h(小时), d(天) * * 比如: * * 100s -> 100000 <br> * 2m -> 120000 <br> * 3h -> 10800000 <br> * * @param tstr * 时间字符串 * @return 毫秒数 */ public static long toMillis(String tstr) { if (Strings.isBlank(tstr)) { return 0; } tstr = tstr.toLowerCase(); // FIXME 稍后改成正则判断 String tl = tstr.substring(0, tstr.length() - 1); String tu = tstr.substring(tstr.length() - 1); if (TIME_S_EN.equals(tu)) { return T_1S * Long.valueOf(tl); } if (TIME_M_EN.equals(tu)) { return T_1M * Long.valueOf(tl); } if (TIME_H_EN.equals(tu)) { return T_1H * Long.valueOf(tl); } if (TIME_D_EN.equals(tu)) { return T_1D * Long.valueOf(tl); } return Long.valueOf(tstr); } private static String TIME_S_EN = "s"; private static String TIME_M_EN = "m"; private static String TIME_H_EN = "h"; private static String TIME_D_EN = "d"; private static String TIME_S_CN = "秒"; private static String TIME_M_CN = "分"; private static String TIME_H_CN = "时"; private static String TIME_D_CN = "天"; /** * 一段时间长度的毫秒数转换为一个时间长度的字符串 * * 1000 -> 1S * * 120000 - 2M * * @param mi * 毫秒数 * @return 可读的文字 */ public static String fromMillis(long mi) { return _fromMillis(mi, true); } /** * fromMillis的中文版本 * * 1000 -> 1秒 * * 120000 - 2分 * * @param mi * 毫秒数 * @return 可读的文字 */ public static String fromMillisCN(long mi) { return _fromMillis(mi, false); } private static String _fromMillis(long mi, boolean useEnglish) { if (mi <= T_1S) { return "1" + (useEnglish ? TIME_S_EN : TIME_S_CN); } if (mi < T_1M && mi > T_1S) { return (int) (mi / T_1S) + (useEnglish ? TIME_S_EN : TIME_S_CN); } if (mi >= T_1M && mi < T_1H) { int m = (int) (mi / T_1M); return m + (useEnglish ? TIME_M_EN : TIME_M_CN) + _fromMillis(mi - m * T_1M, useEnglish); } if (mi >= T_1H && mi < T_1D) { int h = (int) (mi / T_1H); return h + (useEnglish ? TIME_H_EN : TIME_H_CN) + _fromMillis(mi - h * T_1H, useEnglish); } // if (mi >= T_1D) { int d = (int) (mi / T_1D); return d + (useEnglish ? TIME_D_EN : TIME_D_CN) + _fromMillis(mi - d * T_1D, useEnglish); // } // WTF ? // throw Lang.impossible(); } /** * 比较2个字符串格式时间yyyy-MM-dd hh:mm:ss大小 2017-2-8 17:14:14 * * @param t1 * 第一个时间 * @param t2 * 第二个时间 * @return true,如果相等 */ public static boolean sDTcompare(String t1, String t2) { // 将字符串形式的时间转化为Date类型的时间 Date d1 = parseq(DF_DATE_TIME, t1); Date d2 = parseq(DF_DATE_TIME, t2); // Date类的一个方法,如果a早于b返回true,否则返回false if (d1.before(d2)) return true; else return false; } /** * Unix时间戳转String日期 * * @param timestamp * 时间戳 * @param sf * 日期格式 * @return 日期字符串 */ public static String ts2S(long timestamp, String sf) { DateFormat format = new SimpleDateFormat(sf); return format.format(new Date(Long.parseLong(timestamp * 1000 + ""))); } /** * 取Unix时间戳 * * @return 时间戳 */ public static long getTS() { return System.currentTimeMillis() / 1000; } /** * 字符串yyyy-MM-dd HH:mm:ss时间转化成Unix时间戳 * * @param str * 日期,符合yyyy-MM-dd HH:mm:ss * @return timestamp 时间戳字符串 */ public static String sDT2TS(String str, DateFormat df) { String timestamp = null; Date date; try { date = df.parse(str); long l = date.getTime(); String tmp = String.valueOf(l); timestamp = tmp.substring(0, 10); } catch (Exception e) { e.printStackTrace(); } return timestamp; } /** * 取当前时间的字符串形式 , 格式为 yyyy-MM-dd HH:mm:ss * * @return 时间字符串 */ public static String getNowSDT() { return sDT(now()); } /** * 获得某月的天数 * * @param year * 年 * @param month * 月 * @return int 指定年月的天数 */ public static int getDaysOfMonth(String year, String month) { int days = 0; if (month.equals("1") || month.equals("3") || month.equals("5") || month.equals("7") || month.equals("8") || month.equals("10") || month.equals("12")) { days = 31; } else if (month.equals("4") || month.equals("6") || month.equals("9") || month.equals("11")) { days = 30; } else { if ((Integer.parseInt(year) % 4 == 0 && Integer.parseInt(year) % 100 != 0) || Integer.parseInt(year) % 400 == 0) { days = 29; } else { days = 28; } } return days; } /** * 获取某年某月的天数 * * @param year * int 年 * @param month * int 月份[1-12] 月 * @return int 指定年月的天数 */ public static int getDaysOfMonth(int year, int month) { Calendar calendar = Calendar.getInstance(); calendar.set(year, month - 1, 1); return calendar.getActualMaximum(Calendar.DAY_OF_MONTH); } /** * 获得当前日期 * * @return 当前日期,按月算,即DAY_OF_MONTH */ public static int getToday() { Calendar calendar = Calendar.getInstance(); return calendar.get(Calendar.DATE); } /** * 获得当前月份 * * @return 当前月份,1开始算 */ public static int getToMonth() { Calendar calendar = Calendar.getInstance(); return calendar.get(Calendar.MONTH) + 1; } /** * 获得当前年份 * * @return 当前年份 */ public static int getToYear() { Calendar calendar = Calendar.getInstance(); return calendar.get(Calendar.YEAR); } /** * 返回日期的天 * * @param date * 指定的Date * @return 指定时间所在月的DAY_OF_MONTH */ public static int getDay(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar.get(Calendar.DATE); } /** * 返回日期的年 * * @param date * 指定的Date * @return 指定时间的年份 */ public static int getYear(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar.get(Calendar.YEAR); } /** * 返回日期的月份,1-12 * * @param date * 指定的Date * @return 指定时间的月份 */ public static int getMonth(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar.get(Calendar.MONTH) + 1; } /** * 计算两个日期相差的天数,如果date2 > date1 返回正数,否则返回负数 * * @param date1 * Date * @param date2 * Date * @return long */ public static long dayDiff(Date date1, Date date2) { return (date2.getTime() - date1.getTime()) / 86400000; } /** * 比较两个日期的年差 * * @param before * 前一个日期,格式yyyy-MM-dd * @param after * 后一个日期,格式yyyy-MM-dd * @return 年份差值 */ public static int yearDiff(String before, String after) { Date beforeDay = parseq(DF_DATE, before); Date afterDay = parseq(DF_DATE, after); return getYear(afterDay) - getYear(beforeDay); } /** * 比较指定日期与当前日期的年差 * * @param after * 指定的后一个日期,格式yyyy-MM-dd * @return 年份差值 */ public static int yearDiffCurr(String after) { Date beforeDay = new Date(); Date afterDay = parseq(DF_DATE, after); return getYear(beforeDay) - getYear(afterDay); } /** * 比较指定日期与当前日期的天差 * * @param before * 指定的前应日期,格式yyyy-MM-dd * @return 天差 */ public static long dayDiffCurr(String before) { Date currDate = parseq(DF_DATE, sD(now())); Date beforeDate = parseq(DF_DATE, before); return (currDate.getTime() - beforeDate.getTime()) / 86400000; } /** * 根据生日获取星座 * * @param birth * 日期格式为YYYY-mm-dd * @return 星座,单一字符 */ public static String getAstro(String birth) { if (!isDate(birth)) { birth = "2000" + birth; } if (!isDate(birth)) { return ""; } int month = Integer.parseInt(birth.substring(birth.indexOf("-") + 1, birth.lastIndexOf("-"))); int day = Integer.parseInt(birth.substring(birth.lastIndexOf("-") + 1)); String s = "魔羯水瓶双鱼牡羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯"; int[] arr = {20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22}; int start = month * 2 - (day < arr[month - 1] ? 2 : 0); return s.substring(start, start + 2) + "座"; } /** * 判断日期是否有效,包括闰年的情况 * * @param date * 日期格式YYYY-mm-dd * @return true,如果合法 */ public static boolean isDate(String date) { StringBuffer reg = new StringBuffer("^((\\d{2}(([02468][048])|([13579][26]))-?((((0?"); reg.append("[13578])|(1[02]))-?((0?[1-9])|([1-2][0-9])|(3[01])))"); reg.append("|(((0?[469])|(11))-?((0?[1-9])|([1-2][0-9])|(30)))|"); reg.append("(0?2-?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][12"); reg.append("35679])|([13579][01345789]))-?((((0?[13578])|(1[02]))"); reg.append("-?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))"); reg.append("-?((0?[1-9])|([1-2][0-9])|(30)))|(0?2-?((0?["); reg.append("1-9])|(1[0-9])|(2[0-8]))))))"); Pattern p = Pattern.compile(reg.toString()); return p.matcher(date).matches(); } /** * 取得指定日期过 years 年后的日期 (当 years 为负数表示指定年之前); * * @param date * 日期 为null时表示当天 * @param years * 相加(相减)的年数 */ public static Date nextYear(Date date, int years) { Calendar cal = Calendar.getInstance(); if (date != null) { cal.setTime(date); } cal.add(Calendar.YEAR, years); return cal.getTime(); } /** * 取得指定日期过 months 月后的日期 (当 months 为负数表示指定月之前); * * @param date * 日期 为null时表示当天 * @param months * 相加(相减)的月数 */ public static Date nextMonth(Date date, int months) { Calendar cal = Calendar.getInstance(); if (date != null) { cal.setTime(date); } cal.add(Calendar.MONTH, months); return cal.getTime(); } /** * 取得指定日期过 day 周后的日期 (当 day 为负数表示指定月之前) * * @param date * 日期 为null时表示当天 */ public static Date nextWeek(Date date, int week) { Calendar cal = Calendar.getInstance(); if (date != null) { cal.setTime(date); } cal.add(Calendar.WEEK_OF_MONTH, week); return cal.getTime(); } /** * 取得指定日期过 day 天后的日期 (当 day 为负数表示指日期之前); * * @param date * 日期 为null时表示当天 * @param day * 相加(相减)的月数 */ public static Date nextDay(Date date, int day) { Calendar cal = Calendar.getInstance(); if (date != null) { cal.setTime(date); } cal.add(Calendar.DAY_OF_YEAR, day); return cal.getTime(); } /** * 取得当前时间距离1900/1/1的天数 * * @return 天数 */ public static int getDayNum() { int daynum = 0; GregorianCalendar gd = new GregorianCalendar(); Date dt = gd.getTime(); GregorianCalendar gd1 = new GregorianCalendar(1900, 1, 1); Date dt1 = gd1.getTime(); daynum = (int) ((dt.getTime() - dt1.getTime()) / (24 * 60 * 60 * 1000)); return daynum; } /** * getDayNum的逆方法(用于处理Excel取出的日期格式数据等) * * @param day * 天数 * @return 反推出的时间 */ public static Date getDateByNum(int day) { GregorianCalendar gd = new GregorianCalendar(1900, 1, 1); Date date = gd.getTime(); date = nextDay(date, day); return date; } /** * 取得距离今天 day 日的日期 * * @param day * 天数 * @return 日期字符串 */ public static String nextDay(int day) { Calendar cal = Calendar.getInstance(); cal.setTime(now()); cal.add(Calendar.DAY_OF_YEAR, day); return format(DF_DATE, cal.getTime()); } /** * 获取明天的日期 * * return 明天的日期 */ public static String afterDay() { return nextDay(1); } /** * 获取昨天的日期 * * @return 昨天的日期 */ public static String befoDay() { return nextDay(-1); } /** * 获取本月最后一天 * * @return 本月最后一天 */ public static String getLastDayOfMonth() { Calendar cal = Calendar.getInstance(); cal.set(Calendar.DATE, 1); cal.add(Calendar.MONTH, 1); cal.add(Calendar.DATE, -1); return format(DF_DATE, cal.getTime()); } /** * 获取本月第一天 * * @return 本月第一天 */ public static String getFirstDayOfMonth() { Calendar cal = Calendar.getInstance(); cal.set(Calendar.DATE, 1); return format(DF_DATE, cal.getTime()); } public static final long T_1MS = 1; public static final long T_1W = 7 * 24 * 60 * 60 * 1000; /** * 判断两个日期相差的时长 * * @param s * 起始日期 * @param e * 结束日期 * @param unit * 相差的单位 T_1MS 毫秒 T_1S 秒 T_1M 分 T_1H 时 T_1D 天 T_1W 周 * @return 相差的数量 */ public static long between(Date s, Date e, long unit) { Date start; Date end; if (s.before(e)) { start = s; end = e; } else { start = e; end = s; } long diff = end.getTime() - start.getTime(); return diff / unit; } }