java老日期处理API的诟病
Date
java的时间处理类Date一直被人们所诟病,这个类无法表示日期,只能以毫秒的精度表示时间,如果想获得年、月、日等信息需要借助Calender类;另外如果通过Date的构造函数来创建Date非常别扭,比如要创建2018年10月18日这个日期:
Date date = new Date(118,9,18);
System.out.println(date);
输出为:
Thu Oct 18 00:00:00 CST 2018
第一个参数118,表示年份,从1900算起(是不是很别扭);第二参数9,表示月份,其实是表示10月,因为月份是从0算起(是不是更别扭);第三个参数18,表示日,还算正常。
Calendar
可见 用Date带参数的构造函数创建日期,后面引入了Calendar.set(year + 1900, month, date)进行替代,具体可见Date的api:
具体用法:
Calendar cl = Calendar.getInstance();
cl.set(2018, 9, 18);
System.out.println(cl.getTime());
输出:
Thu Oct 18 08:45:05 CST 2018
可见Calendar对年份的处理进行了改进,但对月份还是处理还是从0开始表示第一个月,即这里的9表示的是10月(依然很别扭)。
作为Date类的补充,Calendar中提供了很多有用的方法,比如获取月份的最大天数,获取年份、月份、日期等api。
另外需要注意的是Date和Calendar都是可变类(相对的 String、Integer等是不可变类)
DateFormat
另外你会惊奇的发现,无论是Date还是Calendar类中都没有对日期进行格式化处理的方法,又不得不引入的DateFormat这个API(实现类SimpleDateFormat)。但由于SimpleDateFormat中使用了Calender,Calender前面提到过是可变类设计,很容易引起线程安全问题。比如下面的代码:
public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
这行代码在多线程高并发的情况下会有线程安全问题,其实这是一种错误的做法,正确的做法是每次使用时都new一个SimpleDateFormat;或者放到ThreaLocal中。
上述是java的Date、Calendar、DateFormat这些API中部分为人们所诟病的问题,对于有经验的老鸟来说,都可以一一化解,但有时也会一不小心就会掉到坑里。这也是java8引入新的日期出来api的直接原因。
java新日期和时间API
LocalDate
LocalDate是不可变类设计,所谓不可变类简单的理解就是初始化后就不能被修改,不可变类有很多优良的特性,最通常见到的也就是可以作为HashMap的key(还可以是线程安全的);同样的LocalTime、LocalDateTime都是不可变类的设计,建议在使用jdk1.8开发的项目中使用这些新的日期、时间API。下面首先来看LocalDate的基本用法:
LocalDate ld = LocalDate.of(2018,10,18) ;
int year = ld.getYear();
Month month = ld.getMonth();
int day = ld.getDayOfMonth();
System.out.println(year+"年"+month.getValue()+"月"+day+"日");
//获取当前的星期数
DayOfWeek dow = ld.getDayOfWeek();
int len=ld.lengthOfMonth();
//获取当天在当年中的天数
int lenyear = ld.lengthOfYear();
boolean leap = ld.isLeapYear();
//从系统时钟中获取当前的日期
LocalDate today = LocalDate.now();
// 根据字符串取:
// 严格按照ISO yyyy-MM-dd验证,02写成2都不行,当然也有一个重载方法允许自己定义格式
LocalDate endOfFeb = LocalDate.parse("2014-02-28");
// 无效日期无法通过:DateTimeParseException: Invalid date
LocalDate.parse("2014-02-29");
// 取本月最后一天,再也不用计算是28,29,30还是31:
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
//还有很多其他有用的方法可以直接查看 LocalDate的api即可。
//ChronoField参数方式,ChronoField是枚举类型,枚举值都可以作为参数
//可以根据实际需要进行选择
int now_year = today.get(ChronoField.YEAR);
int now_month = today.get(ChronoField.MONTH_OF_YEAR) ;
int now_day = today.get(ChronoField.DAY_OF_MONTH);
System.out.println(now_year+"年"+now_month+"月"+now_day+"日");
可以看到LocalDate的使用非常简单,而且对应的构造函数的参数更符合人们的习惯,创建LocalDate一般有三种方式:of方法、parse方法、now方法。根据不同的情况自行选择即可。
LocalTime
LocalTime跟LocalDate的功能基本相同,特性完全相同(如 不可变性),只是前者用于表示时间,后者由于表示日期而已。
//参数顺序分别为 时、分、秒
LocalTime t1 = LocalTime.of(9,34,20);
int h = t1.getHour();
int m = t1.getMinute();
int s = t1.getSecond();
LocalTime t2 = LocalTime.now();
//顺序也是时分秒,以冒号间隔,格式必须为xx:xx:xx,不足10必须补0
LocalTime t3 = LocalTime.parse("09:34:20");
System.out.println(t3);
创建LocalTime也是三种方式:of方法、parse方法、now方法。
LocalDateTime
LocalDate和LocalTime分别表示日期、时间,LocalDateTime则是二者的合体,创建方式也与二者基本相同。
//of方法有两种参数格式
LocalDateTime dt1 = LocalDateTime.of(2018,10,22,9,10,20);
LocalDateTime dt2 = LocalDateTime.of(today,t2);
LocalDateTime dt3 = LocalDateTime.now();
System.out.println(dt3);
//atTime(知道日期,设置时间)和atDate方法(知道时间,设置日期)
LocalDateTime dt4 = today.atTime(t2);
LocalDateTime dt5 = t2.atDate(today);
//从LocalDateTime中提取Date和time
LocalDate date_x = dt5.toLocalDate();
LocalTime day_x = dt5.toLocalTime();
Instant
上述年月日时分秒时方便人类识别时间的方式,对于机器而已就不太合适需要一个转换,这就是Instant的作用:它是以Unix元年(UTC时间1970年1月1日)为基准,当前时间的秒数。
//Unix元年时间 + 2秒
Instant ins1 = Instant.ofEpochSecond(2);
System.out.println(ins1.getEpochSecond());
//Unix元年时间+3秒-12纳秒
Instant ins2 = Instant.ofEpochSecond(3,-12);
System.out.println(ins2);
//当前时间
Instant ins3 = Instant.now();
System.out.println(ins3.getEpochSecond());
//与System.currentTimeMillis()对比
System.out.println(System.currentTimeMillis());
Duration和Period
Duration用于计算两个时间之间的间隔,不能用于日期:
LocalTime localTime1 = LocalTime.now();
Thread.sleep(1);
LocalTime localTime2 = LocalTime.now();
//参数可以是LocalTime 也可以是Instant
Duration du1 = Duration.between(localTime1,localTime2);
System.out.println(du1.getNano());
要计算日期之间的间隔,要用Period:
//参数为两个LocalDate
Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
LocalDate.of(2014, 3, 18));
System.out.println(tenDays.getDays());
Duration类和Period类共享了很多相似的方法:
平时大家都是使用的mysql数据库,mysql数据库中的日期时间字段与新api的对应转换关系如下:
SQL -> Java --------------------------
date -> LocalDate
time -> LocalTime
timestamp -> LocalDateTime
操纵日期
修改日期(本质上不是修改,而是创建一个新的LocalDate),首先来看LocalDate的withAttribute用法:
LocalDate ld1 = LocalDate.of(2018,10,23);
LocalDate ld2 = ld1.withYear(2017);
LocalDate ld3 = ld2.withDayOfMonth(25);
LocalDate ld4 = ld3.with(ChronoField.MONTH_OF_YEAR,8);
System.out.println(ld4);
日期的加减(plus和minus)
plus和minus方法可以对日期进行增减,注意这里也不是修改,而是重新创建一个新的LocalDate:
LocalDate date1 = today.plusWeeks(1);//加一周
LocalDate date2 = date1.plusYears(1);//加一年
LocalDate date3 = date2.minusMonths(1);//减一月
//自定义加减
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);
System.out.println(date4);
TemporalAdjuster的使用
使用日期的带TemporalAdjusters工厂方法参数的重载with方法可以很方便的创建需要的日期:
//静态导入方法
import static java.time.temporal.TemporalAdjusters.*;
//下一个周日
LocalDate ld5 = ld4.with(nextOrSame(DayOfWeek.SUNDAY));
System.out.println(ld5);
LocalDate ld6 = ld5.with(lastDayOfMonth());
System.out.println(ld6);
//本月第4周的周日
LocalDate ld7 = ld6.with(dayOfWeekInMonth(4,DayOfWeek.SUNDAY));
System.out.println(ld7);
TemporalAdjusters中提供的其他工厂方法列表(这些方法都返回一个具体的TemporalAdjuster对象):
当然如果上述方法都不满足你的要求的化,还可以自己实现TemporalAdjuster接口,然后作为参数传递给LocalDate的with方法。
格式化处理日期和时间
文章开头提到过,以前对Date的格式化只能使用DateFormat,但DateFormat是线程不安全的,所以很容易踩坑。
在新的api中,可以使用LocalDate和LocalTime的format方法,结合DateTimeFormatter进行格式化处理:
//使用已定义的DateTimeFormatter格式
//LocalDate转string
String s1 = today.format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(s1);
String s2 = today.format(DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println(s2);
//String转LocalDate
LocalDate s3 = LocalDate.parse("20181023",DateTimeFormatter.BASIC_ISO_DATE);
LocalDate s4 = LocalDate.parse("2018-08-13",DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println(s3);
System.out.println(s4);
//自己指定格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s5 = today.format(formatter);
System.out.println(s5);
//国际化处理
DateTimeFormatter italianFormatter =
DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
LocalDate dd = LocalDate.of(2014, 3, 18);
String formattedDate = dd.format(italianFormatter);
System.out.println(formattedDate);
//使用DateTimeFormatterBuilder自定义DateTimeFormatter
DateTimeFormatter italianFormatter2 = new DateTimeFormatterBuilder() .appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN);
时区和历法
新的java.time.ZoneId类是老版java.util.TimeZone的替代品,同样ZoneId是不可变的。
ZoneId romeZone = ZoneId.of("Europe/Rome");
//TimeZone转换为ZoneId
ZoneId zoneId = TimeZone.getDefault().toZoneId();
有了ZoneId之后,就可以用于创建ZonedDateTime:
LocalDate ldate = LocalDate.of(2014,Month.APRIL,18);
ZonedDateTime zdate = ldate.atStartOfDay(romeZone);
System.out.println(zdate);
LocalDateTime ldatetime = LocalDateTime.of(2014,10,24,13,13,13);
ZonedDateTime zdate2 = ldatetime.atZone(romeZone);
System.out.println(zdate2);
Instant nowInstant = Instant.now();
ZonedDateTime zdate3 = nowInstant.atZone(romeZone);
System.out.println(zdate3);
日本日历系统
LocalDate dated = LocalDate.of(2014, Month.MARCH, 18);
JapaneseDate japaneseDate = JapaneseDate.from(dated);
System.out.println(japaneseDate);
Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
ChronoLocalDate now = japaneseChronology.dateNow();
System.out.println(now);
伊斯兰教日历
HijrahDate ramadanDate = HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1)
.with(ChronoField.MONTH_OF_YEAR, 9);
System.out.println("Ramadan starts on " +
IsoChronology.INSTANCE.date(ramadanDate) +
" and ends on " +
IsoChronology.INSTANCE.date(
ramadanDate.with(
TemporalAdjusters.lastDayOfMonth())));
相关推荐
这是一个重要的里程碑,因为它引入了许多新的特性和改进,包括Lambda表达式、Stream API、默认方法、新的日期时间API以及性能优化等。Lambda表达式使得函数式编程风格在Java中变得更加简洁和高效,而Stream API则...
4. **日期与时间API的改进**:Java 8使用新的java.time包替代了旧的日期和时间API,提供了更加强大和易用的时间日期处理功能。 5. **默认方法**:接口中可以定义具有实现的默认方法,使得接口可以扩展其行为,而...
3. **新的日期和时间API**:Java 8提供了新的java.time包,替代了旧的Calendar和Date API,提供了更直观、更易用的时间日期处理功能。 4. **Stream API**:这是一个用于处理集合的新API,提供了序列化操作的方式,...
4. **Date和Time API的改进**:Java 8引入了全新的java.time包,提供了更加丰富和灵活的日期和时间处理功能,替代了原有的Calendar和Date类。 5. **Optional类**:为了解决null值可能导致的NullPointerException,...
4. **Date和Time API的增强**:Java 8改进了日期和时间处理,引入了新的`java.time`包,包含`LocalDate`、`LocalTime`、`LocalDateTime`等类,相比旧的`java.util.Date`和`java.util.Calendar`更易于使用且功能更...
5. **Date和Time API的改进**:Java 8对日期和时间API进行了重大改进,引入了`java.time`包,取代了过时的`java.util.Date`和`java.util.Calendar`,使日期和时间的处理更加直观和易于使用。 6. **Optional类**:...
JDK1.8对日期和时间API进行了重大改进,提供了`java.time`包,包括`LocalDate`、`LocalTime`、`LocalDateTime`等新类,取代了旧的`java.util.Date`和`java.util.Calendar`,使日期和时间的操作更加直观和高效。...
- **Date/Time API增强**:JDK 1.8对日期和时间API进行了重写,提供了更强大和易用的`java.time`包。 - **Optional类**:用于表示可能为null的对象,帮助开发者避免空指针异常。 - **并发改进**:包括Fork/Join...
5. **Date和Time API改进**:引入了新的java.time包,取代了旧的java.util.Date和Calendar,提供了更强大、更直观的时间日期处理功能。 6. **类型推断增强**:编译器可以更好地推断泛型的类型,使代码更简洁。 7. ...
- ** Date和Time API**:对日期和时间处理进行了全面的改进,引入了新的java.time包,取代了旧的java.util.Date和java.util.Calendar。 - ** Method References**:允许直接引用类或对象的方法,作为函数接口的实现...
5. **Date和Time API增强**:JDK 1.8对日期和时间API进行了全面改进,引入了`java.time`包,提供更加易用和强大的日期和时间操作功能。 6. **新的 Nashorn JavaScript引擎**:允许Java代码与JavaScript交互,使得...
4. **日期和时间API的改进**:Java 8引入了新的java.time包,提供了更好的日期和时间处理能力,取代了原有的java.util.Date和Calendar。 5. **接口默认方法和静态方法**:接口可以有默认方法,允许在不破坏向后兼容...
JDK1.8对日期时间API进行了重大改进,引入了java.time包,包括LocalDate、LocalTime、LocalDateTime等类,以及Temporal、TemporalAdjuster等接口,提供了更强大、易用且符合ISO-8601标准的日期时间处理功能。...
- **Date和Time API的改进**:Java 8引入了新的日期和时间API (`java.time`包),替代了过时的`java.util.Date`和`java.util.Calendar`。 - **并发改进**:`ForkJoinPool`和`CompletableFuture`提供了更高效的并发...
Java 8引入了全新的java.time包,提供了更直观、更强大的日期和时间API,如LocalDate、LocalTime、LocalDateTime等,大大改善了日期和时间的处理能力。 五、接口默认方法和静态方法 Java 8允许接口中定义默认方法,...
3. Date/Time API的改进:用java.time包取代了java.util.Date和Calendar,提供了更强大、更直观的时间日期处理能力。 4. Nashorn JavaScript引擎:内置JavaScript引擎,使得Java应用可以直接执行JavaScript代码,...
4. **日期和时间API的改进**:Java 8引入了全新的java.time包,替代了原有的java.util.Date和Calendar类,提供了更直观、更易于使用的日期和时间API。 5. **接口的默认方法**:默认方法允许在接口中定义带有实现的...
在日期和时间API方面,JDK1.8彻底重写了这部分,引入了java.time包,提供了更强大、更易于使用的API,替代了以前的java.util.Date和java.util.Calendar。 总的来说,"jdk1.8-64位最新免安装版本"是针对64位系统优化...
新API更易于使用,功能更强大,能够更好地处理日期、时间和时区。 接下来,关于JDK 8u152的安装步骤,通常包括以下过程: 1. **下载**:从官方Oracle网站或其他可靠的来源下载适用于Linux x64的JDK 1.8u152压缩包...
这个压缩包包含了两个版本:32位(jdk1.8-32)和64位(jdk1.8-64),分别适用于32位和64位操作系统的计算机。这两个版本的主要区别在于它们能够处理的内存大小以及与操作系统交互的方式,64位版本通常在处理大量数据...