java 获取当前时间的三种方法,java中获取当前时间戳(java按格式获取当前时间)
上一周在做一个产品的需求的时候有个动态计算时间段(如现在是13:00,则时间段为15:10-17:10、17:10-19:10、19:10-21:10;即最早的出发时间为当前时间+参数【2h10min】,最迟的时间段为开始时间在20点前结束时间在20点后的时间段),期间大量使用到了日期时间类库,本着熟悉日期时间类库才有了这篇文章,文章最后我会把我如何实现的这个需求的一个算法贴出来。
我们在使用Java8之前的类库时,都会在处理日期-时间的时候总是不爽,这其中包括且不限于以下的槽点:
在Java 1.0版本中,对时间、日期的操作完全依赖于 java.util.Data 类,只能以毫秒的精度表示时间,无法表示日期。
在Java1.1 版本中,废弃了很多Date 类中的很多方法,并且新增了 java.util.Calendar。但是与Date相同,Calendar 类也有类似的问题和设计缺陷,导致在使用这些类写出的代码也很容易出错。
由于 parse 方法使用的贡献变量 calendar 不是线程安全的。在 format (subFormat) 方法中进行了 calendar 的赋值,在 parse 进行了值得处理,因此在并发的情况下会造成 calendar 清理不及时,值被覆盖的情况。
/**
* The {@link Calendar} instance used for calculating the date-time fields
* and the instant of time. This field is used for both formatting and
* parsing.
*
* <p>Subclasses should initialize this field to a {@link Calendar}
* appropriate for the {@link Locale} associated with this
* <code>DateFormat</code>.
* @serial
*/protected Calendar calendar;
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition pos){
pos.beginIndex = pos.endIndex = 0;
return format(date, toAppendTo, pos.getFieldDelegate());
}
// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
// At this point the fields of Calendar have been set. Calendar
// will fill in default values for missing fields when the time
// is computed.
pos.index = start;
Date parsedDate;
try {
parsedDate = calb.establish(calendar).getTime();
// If the year value is ambiguous,
// then the two-digit year == the default start year
if (ambiguousYear[0]) {
if (parsedDate.before(defaultCenturyStart)) {
parsedDate = calb.addYear(100).establish(calendar).getTime();
}
}
}
}
Joda-Time 是Joda提供的一个遵循Apache2.0 开源协议的 JDK以外的优质日期和时间开发库。
Joda除Joda-Time之外的项目有Joda-Money、Joda-Beans、Joda-Convert、Joda-Collect Joda网站
正因为Joda-Time 与 Java8 之前的时间类库相比,具备了如此多的优点,所以 Joda-Time 成为事实上的标准的Java日期和时间库。
互操作性是指:Joda 类能够生成 java.util.Date 的实例(以及Calendar),这可以让我们保留现有对JDK的依赖,又能够使用Joda处理复杂的日期/时间计算。
Date date = new Date();
DateTime dateTime = new DateTime(date);
Calendar calendar = Calendar.getInstance();
DateTime dateTime = new DateTime(calendar);
Date date = new Date();
DateTime dateTime = new DateTime(date);
Date date2 = dateTime.toDate();
Calendar calendar = Calendar.getInstance();
dateTime = new DateTime(calendar);
Calendar calendar2 = dateTime.toCalendar(Locale.CHINA);
Joda 使用了以下概念,使得它们可以应用到任何日期/时间库:
Joda-Time与java.lang.String类似,它们的实例均无法修改(因为任意对其值改变的操作都会生成新的对象),这也代表了它们是线程安全的。
如接口 org.joda.time.ReadableInstant 中所表示的那样,Instant 表示的是一个精确的时间点,是从 epoch:1970-01-01T00:00:00Z 开始计算的毫秒数,这也的设计也使得其子类都可以与JDK Date 以及 Calendar 类兼容。
/**
* Defines an instant in the datetime continuum.
* This interface expresses the datetime as milliseconds from 1970-01-01T00:00:00Z.
* <p>
* The implementation of this interface may be mutable or immutable.
* This interface only gives access to retrieve data, never to change it.
* <p>
* Methods in your application should be defined using <code>ReadableInstant</code>
* as a parameter if the method only wants to read the instant without needing to know
* the specific datetime fields.
* <p>
* The {@code compareTo} method is no longer defined in this class in version 2.0.
* Instead, the definition is simply inherited from the {@code Comparable} interface.
* This approach is necessary to preserve binary compatibility.
* The definition of the comparison is ascending order by millisecond instant.
* Implementors are recommended to extend {@code AbstractInstant} instead of this interface.
*
* @author Stephen Colebourne
* @since 1.0
*/public interface ReadableInstant extends Comparable<ReadableInstant> {
/**
* Get the value as the number of milliseconds since
* the epoch, 1970-01-01T00:00:00Z.
*
* @return the value as milliseconds
*/ long getMillis();
······
}
DateTime 类继承图如下:
瞬时性表达的是与epoch相对的时间上的一个精确时刻,而一个局部时间指的是一个时间的一部分片段,其可以通过一些方法使得时间产生变动(本质上还是生成了新的类),这样可以把它当做重复周期中的一点,用到多个地方。
Joda-Time的设计核心就是年表(org.joda.time.Chronology),从根本上将,年表是一种日历系统,是一种计算时间的特殊方式,并且在其中执行日历算法的框架。Joda-Time支持的8种年表如下所示:
以上的每一种年表都可以作为特定日历系统的计算引擎,是可插拔的实现。
具体定义详见百科解释,在实际编码过程中任何严格的时间计算都必须涉及时区(或者相对于GMT),Joda-Time中对应的核心类为org.joda.time.DateTimeZone,虽然日常的使用过程中,并未涉及到对时区的操作,但是DateTimeZone如何对DateTime产生影响是比较值得注意的,此处不进行赘述。
上面介绍我完了Joda-Time的一些概念,接下来具体使用我们来进行说明:
// 1.使用系统时间
DateTime dateTime1 = new DateTime();
// 2.使用jdk中的date
Date jdkDate1 = new Date();
DateTime dateTime2 = new DateTime(jdkDate1);
// 3.使用毫秒数指定
Date jdkDate2 = new Date();
long millis = jdkDate.getTime();
DateTime dateTime3 = new DateTime(millis);
// 4.使用Calendar
Calendar calendar = Calendar.getInstance();
DateTime dateTime4 = new DateTime(calendar);
// 5.使用多个字段指定一个瞬间时刻(局部时间片段)
// year month day hour(midnight is zero) minute second milliseconds
DateTime dateTime5 = new DateTime(2000, 1, 1, 0, 0, 0, 0);
// 6.由一个DateTime生成另一个DateTime
DateTime dateTime6 = new DateTime(dateTime1);
// 7.有时间字符串生成DateTime
String timeString = "2019-01-01T00:00:00-06:00";
DateTime dateTime7 = DateTime.parse(timeString);
当程序中处理的日期、时间并不需要是完整时刻的时候,可以创建一个局部时间,比如只希望专注于年/月/日, 或者一天中的时间,或者是一周中的某天。Joda-Time中有表示这些时间的是org.joda.time.ReadablePartial接口,实现它的两个类LocalDate和LocalTime是分别用来表示年/月/日和一天中的某个时间的。
// 显示地提供所含的每个字段
LocalDate localDate = new LocalDate(2019, 1, 1);
// 6:30:06 PM
LocalTime localTime = new LocalTime(18, 30, 6, 0);
LocalDate是替代了早期Joda-Time版本中使用的org.joda.time.YearMonthDay,LocalTime是替代早期版本的org.joda.time.TimeOfDay。(均已被标注为过时状态)。
Joda-Time提供了三个类用于表示时间跨度(在某些业务需求中,它们可能会非常有用)。
这个类表示以毫秒为单位的绝对精度,提供标准数学转换的方法,同时把时间跨度转换为标准单位。
这个类表示以年月日单位表示。
这个类表示一个特定的时间跨度,使用一个明确的时刻界定这段时间跨度的范围。Interval 为半开 区间,所以由其封装的时间跨度包括这段时间的起始时刻,但是不包含结束时刻。
DateTime today = new DateTime();
// 获取777秒之前的时间
DateTime dateTime1 = today.minus(777 * 1000);
// 获取明天的时间
DateTime tomorrow = today.plusDays(1);
// 获取当月第一天的日期
DateTime dateTime2 = today.withDayOfMonth(1);
// 获取当前时间三个月后的月份的最后一天
DateTime dateTime3 = today.plusMonths(3).dayOfMonth().withMaximumValue();
下面列出部分DateTime方法列表: plus/minus开头的方法(比如:plusDay, minusMonths):用来返回在DateTime实例上增加或减少一段时间后的实例
与之相反的是minus前缀的 plus是增加 minus是减少
with开头的方法:用来返回在DateTime实例更新指定日期单位后的实例
判断DateTime对象大小状态的一些操作方法
// 传入的格式化模板只需与JDK SimpleDateFormat兼容的格式字符串即可
public static String convert(Date date,String dateFormat){
return new DateTime(date).toString(dateFormat);
}
// 将JDK中的Date转化为UTC时区的DateTime
DateTime dateTime = new DateTime(new Date(), DateTimeZone.UTC);
// 将String转换为DateTime
public static Date convertUTC2Date(String utcDate){
DateTime dateTime =DateTime.parse(utcDate, DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
return dateTime.toDate();
}
更多使用方法请参考官方文档。
由于JDK之前版本的类库的缺陷和糟糕的使用体验,再加上已经成为事实标准Joda-Time的影响力,Oracle决定在JAVA API中提供高质量的日期和时间支持,这也就是整合了大部分Joda-Time特性的JDK 8新的时间类库。(Joda-Time的作者实际参与开发,并且实现了JSR310的全部内容,新的API位于java.time下。常用的类有以下几个:LocalDate、LocalTime、Instant、Duration和Period。)
由于JDK 8 新的时间类库大量借鉴了Joda-Time的设计思想乃至命名,因此如果你是Joda-Time的使用者,那你可以无学习成本的使用新的API(当然,它们之间也存在些许差别需要注意到)。
首先是LocalDate,该类的实例是一个不可变对象,它只提供了简单的日期,并不含当天的时间信息。另外,它也不附带任何与时区相关的信息。
// 使用指定的日期创建LocalDate
LocalDate date = LocalDate.of(2019, 1, 1);
// 获取当前日期
LocalDate today = LocalDate.now();
// 获取今日的属性
int year = date.getYear();
Month month = date.getMonth();
int day = date.getDayOfMonth();
DayOfWeek dow = date.getDayOfWeek();
int len = date.lengthOfMonth();
boolean leap = date.isLeapYear();
// 通过ChronoField的枚举值获取需要的属性字段
int year = date.get(ChronoField.YEAR);
接着是LocalTime,它表示了一天内的某个时刻。
LocalTime time = LocalTime.of(18, 18, 18);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();
LocalDate和LocalTime都可以通过使用静态方法parse来解析字符串进行创建。
LocalDate date = LocalDate.parse("2019-01-01");
LocalTime time = LocalTime.parse("18:18:18");
也可以向parse方法传递一个DateTimeFormatter,该类的实例定义了如何格式化一个日期或者时间对象。它其实是老版java.util.DateFormat的替代品。
// 直接创建LocalDateTime
LocalDateTime dt1 = LocalDateTime.of(2019, Month.JANUARY, 1, 18, 18, 18);
// 合并日期和时间
LocalDate date = LocalDate.parse("2019-01-01");
LocalTime time = LocalTime.parse("18:18:18");
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(18, 18, 18);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);
// 从LocalDateTime中提取LocalDate或者LocalTime
LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();
Instant类是为了方便计算机理解的而设计的,它表示一个持续时间段上某个点的单一大整型数,实际上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算(最小计算单位为纳秒)。
// 传递一个秒数已创建该类的实例
Instant.ofEpochSecond(3);
// 传递一个秒数+纳秒 2 秒之后再加上100万纳秒(1秒)
Instant.ofEpochSecond(2, 1_000_000_000);
Duration是用于比较两个LocalTime对象或者两个Instant之间的时间差值。
Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);
Period是用于对年月日的方式对多个时间进行比较。
Period tenDays = Period.between(LocalDate.of(2019, 1, 1), lcalDate.of(2019, 2, 2));
当然,Duration和Period类都提供了很多非常方便的工厂类,直接创建对应的实例。
Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
// 直接使用withAttribute的方法修改
LocalDate date1 = LocalDate.of(2019, 1, 1);
LocalDate date2 = date1.withYear(2019);
LocalDate date3 = date2.withDayOfMonth(1);
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 1);
所有声明了Temporal接口的类LocalDate、LocalTime、LocalDateTime以及Instant,它们都使用get和with方法,将对象值的读取和修改区分开,如果使用了不支持的字段访问字段,会抛出一个UnsupportedTemporalTypeException异常。类似的,plus方法和minus方法都声明于Temporal接口。通过这些方法,对TemporalUnit对象加上或者减去一个数字,我们能非常方便地将Temporal对象前溯或者回滚至某个时间段,通过ChronoUnit枚举我们可以非常方便地实现TemporalUnit接口。
向重载的with方法传递一个定制化的TemporalAdjuster对象,可以更加灵活地处理日期。时间和日期的API已经提供了大量预定义的TemporalAdjuster,可以通过TemporalAdjuster类的静态工厂方法访问它们。这些方法的名称非常直观,方法名就是问题描述。某些情况下,如果你需要定义自己的TemporalAdjuster,只需要声明TemporalAdjuster接口并且自己实现对应的方法即可。
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.with(TemporalAdjuster.nextOrSame(DayOfWeek.SUNDAY));
LocalDate date3 = date2.with(TemporalAdjuster.lastDayOfMonth());
日常工作中,格式化以及解析日期-时间对象是另一个非常重要的功能,而新的java.time.format包就是特别为我们达到这个目的而设计的。这其中,最重要的类是DateTimeFormatter。所有的DateTimeFormatter实例都能用于以一定的格式创建代表特定日期或时间的字符串。(与老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全的)
// 使用不同的格式器生成字符串
LocalDate date = LocalDate.of(2019, 1, 1);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
// 生成LocalDate对象
LocalDate date1 = LocalDate.parse("20190101", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2019-01-01", DateTimeFormatter.ISO_LOCAL_DATE);
// 使用特定的模式创建格式器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date1 = LocalDate.of(2019, 1, 1);
String formattedDate = date1.format(formatter);
LocalDate date2 = LocalDate.parse(formattedDate, formatter);
在新的日期-时间类库中,为了最大程度上的减少在处理时区带来的繁琐和复杂而使用了新的java.time.ZoneId类(与其他日期和时间类一样,ZoneId类也是无法修改的) 来替代老版的java.util.TimeZone。时区是按照一定的规则将区域划分成标准时间相同的区间。在ZoneRules这个类中包含了40个这样的实例。可以简单地通过调用ZoneId的getRules()得到指定时区的规则。每个特定的ZoneId对象都由一个地区ID标识,地区ID都为“{区域}/{城市}”的格式。比如:
ZoneId romeZone = ZoneId.of("Asia/Shanghai");
Java 8中在原先的TimeZone中加入了新的方法toZoneId,其作用是将一个老的时区对象转换为ZoneId:
ZoneId zoneId = TimeZone.getDefault().toZoneId();
得到的ZoneId对象后可以将它与LocalDate、LocalDateTime或者是Instant对象整合起来,构造为一个ZonedDateTime实例,它代表了相对于指定时区的时间点:
LocalDate date = LocalDate.of(2019, Month.JANUARY, 1);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
LocalDateTime dateTime = LocalDateTime.of(2019, Month.JANUARY, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);
通过ZoneId,还可以将LocalDateTime转换为Instant:
LocalDateTime dateTime = LocalDateTime.of(2019, Month.JANUARY, 18, 13, 45);
Instant instantFromDateTime = dateTime.toInstant(romeZone);
同样可以通过反向的方式得到LocalDateTime对象:
Instant instant = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);
与Joda-Time所不同的是,Java8中的日期-时间类库提供了4种其他的日历系统,这些日历系统中的每一个都有一个对应的日志类,分别是ThaiBuddhistDate、MinguoDate 、JapaneseDate 以及HijrahDate 。所有这些类以及LocalDate 都实现了ChronoLocalDate接口,能够对公历的日期进行建模。利用LocalDate对象,你可以创建这些类的实例。同样的,利用它们提供的静态工厂方法,你可以创建任何一个Temporal对象的实例。
LocalDate date = LocalDate.of(2019, Month.JANUARY, 1);
JapaneseDate japaneseDate = JapaneseDate.from(date);
Joda-Time 简介 Joda Time项目和java8时间api
需求:如现在是13:00,则时间段为15:10-17:10、17:10-19:10、19:10-21:10;即最早的出发时间为当前时间+参数【2h10min】,最迟的时间段为开始时间在20点前结束时间在20点后的时间段),求解共有多少个时间段?
分析:
注意:计算过程都转换成毫秒
public class Test {
// 毫秒
static final long slot = 130 * 60 * 1000;
private static List<TimeSelectItem> buildStartEndTime(Long now, Long max) {
// now + (2h * n) + 10min <= max;
Long n = (max - now - 60 * 1000) / (120 * 60 * 1000);
System.out.println("max:" + max);
System.out.println("now:" + now);
System.out.println(" max - now:" + (max - now));
System.out.println("n:" + n);
List<TimeSelectItem> timeSelectItems = new ArrayList<>();
Long startTimestamp = now + slot;
Long endTimestamp = startTimestamp + 120 * 60 * 1000;
for (int i = 1; i <= n; i++) {
// 起始时间
// startTimestamp = startTimestamp + i * (120 * 60 * 1000);
// 结束时间
endTimestamp = startTimestamp + (120 * 60 * 1000);
System.out.println(startTimestamp);
System.out.println(endTimestamp);
TimeSelectItem item = new TimeSelectItem();
DateTime dt = new DateTime(startTimestamp);
int hour = dt.hourOfDay().get();
int millis = dt.getMinuteOfHour();
String startTag = hour + ":" + millis;
DateTime dt1 = new DateTime(endTimestamp);
int hour1 = dt1.hourOfDay().get();
long millis1 = dt1.getMinuteOfHour();
String enTag = hour1 + ":" + millis1;
item.setDisplayName(startTag + " - " + enTag);
item.setStartTimestamp(startTimestamp);
item.setEndTimestamp(endTimestamp);
timeSelectItems.add(item);
startTimestamp = endTimestamp;
}
return timeSelectItems;
}
public static void main(String[] args) {
Long start = DateTime.now().getMillis();
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.set(Calendar.HOUR_OF_DAY, 20);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
DateTime dt = new DateTime();
dt.withHourOfDay(20);
Long end = c.getTimeInMillis();
// List<TimeSelectItem> list = buildStartEndTime(1614747600000L, 1614772800000L);
List<TimeSelectItem> list = buildStartEndTime(1614834000000L, end);
for (TimeSelectItem item : list ) {
System.out.println(item);
}
}
}
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台用户上传并发布,不构成投资建议请自行甄别,如有侵权请联系删除:liaycn@163.com
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of the site, which is a social media platform and only provides information storage services.