目录
Date与LocalDateTime、LocalDate、LocalTime互转
早期使用的是格林尼治时间(GMT),但是,由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能和实际的太阳时相差16分钟,地球每天的自转是有些不规则的,而且正在缓慢减速。所以,格林尼治时间已经不再被作为标准时间使用,取而代之的是协调世界时(UTC),其以原子时秒长为基础,在时刻上尽量接近于格林尼治平时。
这套时间系统被应用于许多互联网和万维网的标准中,例如,网络时间协议就是协调世界时在互联网中使用的一种方式。
如果本地时间比UTC时间快,例如中国大陆的时间比UTC快8小时,就会写作UTC+8,俗称东8区。相反,如果本地时间比UTC时间慢,例如夏威夷的时间比UTC时间慢10小时,就会写作UTC-10,俗称西10区。
夏天太阳升起得比较早,白天时间很长。为了节约能源和充分利用白天的宝贵时间,世界上不少国家都采用法律规定的形式,每到夏天就将这个国家使用的时间提前一小时,也有提前半小时或几小时的;到了冬季,又将拨快的时间拨回来。这样的时间就是“夏令时”,是一种法定时间。主要的夏令时包括EDT(Eastern Daylight Time),CDT(Central Daylight Time ),PDT(Pacific Daylight Time)。非夏令时包括CST(Central Standard Time),PST(Pacific Standard Time)。
1970
MT:Greenwich Mean Time 格林尼治标准时间。这是以英国格林尼治天文台观测结果得出的时间,这是英国格林尼治当地时间,这个地方的当地时间过去被当成世界标准的时间。
UT:Universal Time 世界时。根据原子钟计算出来的时间。
UTC:Coordinated Universal Time 协调世界时。因为地球自转越来越慢,每年都会比前一年多出零点几秒,每隔几年协调世界时组织都会给世界时+1秒,让基于原子钟的世界时和基于天文学(人类感知)的格林尼治标准时间相差不至于太大。并将得到的时间称为UTC,这是现在使用的世界标准时间。
协调世界时不与任何地区位置相关,也不代表此刻某地的时间,所以在说明某地时间时要加上时区
也就是说GMT并不等于UTC,而是等于UTC+0,只是格林尼治刚好在0时区上。GMT = UTC+0
世界时区划分
由于世界各国家与地区经度不同,地方时也有所不同,因此会划分为不同的时区。
正式的时区划分包括24个时区,每一时区由一个英文字母表示。每隔经度15°划分一个时区,有一个例外,每个时区有一条中央子午线;例如,GMT属于“z”区,因此其时间后通常添加后缀“Z”(口语中用后缀“Zulu”
现今全球共分为24个时区。实际上,常常1个国家或1个省份同时跨着2个或更多时区,为了照顾到行政上的方便,常将1个国家或1个省份划在一起。所以时区并不严格按南北直线来划分,而是按自然条件来划分。例如,中国幅员宽广,差不多跨5个时区,但为了使用方便简单,实际上在只用东八时区的标准时即北京时间为准。
Code
val firstYearOfEra = new Date()
firstYearOfEra.setTime(0)
val now = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
// Returns:the number of seconds from the epoch of 1970-01-01T00:00:00Z
val now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"))
val now = LocalDateTime.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))).toEpochSecond(ZoneOffset.of("+8"))
// Returns:the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this date.
val now = Timestamp.valueOf(LocalDateTime.now()).getTime
以上code注意返回值单位及默认取值都是"Returns:
the current date-time using the system clock and default time-zone, not null"
在处理hive中对应的timestamp时返回的是秒,而不是毫秒;最简单的验证
sql("select cast(current_timestamp as bigint)").show
val now = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)
val now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"))
//////////// ZoneOffset.UTC ///////
public static final ZoneOffset UTC = ZoneOffset.ofTotalSeconds(0);
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code ZoneOffset} using the ID.
* <p>
* This method parses the string ID of a {@code ZoneOffset} to
* return an instance. The parsing accepts all the formats generated by
* {@link #getId()}, plus some additional formats:
* <ul>
* <li>{@code Z} - for UTC
* <li>{@code +h}
* <li>{@code +hh}
* <li>{@code +hh:mm}
* <li>{@code -hh:mm}
* <li>{@code +hhmm}
* <li>{@code -hhmm}
* <li>{@code +hh:mm:ss}
* <li>{@code -hh:mm:ss}
* <li>{@code +hhmmss}
* <li>{@code -hhmmss}
* </ul>
* Note that ± means either the plus or minus symbol.
* <p>
* The ID of the returned offset will be normalized to one of the formats
* described by {@link #getId()}.
* <p>
* The maximum supported range is from +18:00 to -18:00 inclusive.
*
* @param offsetId the offset ID, not null
* @return the zone-offset, not null
* @throws DateTimeException if the offset ID is invalid
*/
@SuppressWarnings("fallthrough")
public static ZoneOffset of(String offsetId) {
\\..
}
/**
* Gets the normalized zone offset ID.
* <p>
* The ID is minor variation to the standard ISO-8601 formatted string
* for the offset. There are three formats:
* <ul>
* <li>{@code Z} - for UTC (ISO-8601)
* <li>{@code +hh:mm} or {@code -hh:mm} - if the seconds are zero (ISO-8601)
* <li>{@code +hh:mm:ss} or {@code -hh:mm:ss} - if the seconds are non-zero (not ISO-8601)
* </ul>
*
* @return the zone offset ID, not null
*/
@Override
public String getId() {
return id;
}
/** Timestamp
* Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
* represented by this <code>Timestamp</code> object.
*
* @return the number of milliseconds since January 1, 1970, 00:00:00 GMT
* represented by this date.
* @see #setTime
*/
public long getTime() {
long time = super.getTime();
return (time + (nanos / 1000000));
}
/** System
* Returns the current time in milliseconds. Note that
* while the unit of time of the return value is a millisecond,
* the granularity of the value depends on the underlying
* operating system and may be larger. For example, many
* operating systems measure time in units of tens of
* milliseconds.
*
* <p> See the description of the class <code>Date</code> for
* a discussion of slight discrepancies that may arise between
* "computer time" and coordinated universal time (UTC).
*
* @return the difference, measured in milliseconds, between
* the current time and midnight, January 1, 1970 UTC.
* @see java.util.Date
*/
public static native long currentTimeMillis();
GMT:Greenwich Mean Time 格林尼治标准时间。这是以英国格林尼治天文台观测结果得出的时间,这是英国格林尼治当地时间,这个地方的当地时间过去被当成世界标准的时间。
UT:Universal Time 世界时。根据原子钟计算出来的时间。
UTC:Coordinated Universal Time 协调世界时。因为地球自转越来越慢,每年都会比前一年多出零点几秒,每隔几年协调世界时组织都会给世界时+1秒,让基于原子钟的世界时和基于天文学(人类感知)的格林尼治标准时间相差不至于太大。并将得到的时间称为UTC,这是现在使用的世界标准时间。
协调世界时不与任何地区位置相关,也不代表此刻某地的时间,所以在说明某地时间时要加上时区
也就是说GMT并不等于UTC,而是等于UTC+0,只是格林尼治刚好在0时区上。
GMT = UTC+0
ISO-8601
国际标准化组织的国际标准ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。最新为第三版ISO8601:2004,第一版为ISO8601:1988,第二版为ISO8601:2000
分类包含:日期表示法/日历日期表示法/日历星期和日表示法/时间表示法/日期和时间的组合表示法/时间段表示法/重复时间表示法
代码中用的是时间表示法
小时、分和秒都用2位数表示,对UTC时间最后加一个大写字母Z,其他时区用实际时间加时差表示。如UTC时间下午2点30分5秒表示为14:30:05Z或143005Z,当时的北京时间表示为22:30:05+08:00或223005+0800,也可以简化成223005+08。
java8
Instant、LocalDateTime与DateTimeFormatter
SimpleDateFormat把String转Date的时候出现的线程安全问题。SimpleDateFormat是线程不安全的,现在还是出现了问题。阿里Java规范中(六.5)有这么一条强制 SimpleDataFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类.
// 正例:注意线程安全,使用DateUtils.亦推荐如下处理
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
@Override
protected DataFormat initalValue(){
return new SimpleDateDateFormat("yyyy-MM-dd");
}
}
// 说明: 如果是JDK8的应用可以使用Instance代替Date, LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat.官方解释:simple beautiful strong immutable thread-safe
使用JDK8代替以前的Date、Calendar、SimpleDateFormat的示例
// DateTimeFormatter <- SimpleDateFormat
// Intant <- Date
// LocalDateTime <- Calendar
// date与instant的相互转化
Instant now = Instant.now();
Date date = Date.from(now);
Instant instant = date.toInstant();
// LocalDateTime代替Calendar
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId);
int year = localDateTime.getYear();
int month = localDateTime.getMonthValue();
int dayOfMonth = localDateTime.getDayOfMonth();
int hour = localDateTime.getHour();
int minute = localDateTime.getMinute();
int second = localDateTime.getSecond();
// DateTimeFormatter代替SimpleDateFormat
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// Date格式化成String
String format = dateTimeFormatter.format(localDateTime);
LocalDateTime parse = LocalDateTime.parse(format, dateTimeFormatter);
ZoneOffset offset = OffsetDateTime.now().getOffset();
Instant instant2 = parse.toInstant(offset);
// 从String获得Date
Date from = Date.from(instant2);
scala> System.currentTimeMillis == Timestamp.from(Instant.now()).getTime
res12: Boolean = true
scala> System.currentTimeMillis == Instant.now().toEpochMilli
res17: Boolean = true
使用JDK8的Instant、LocalDateTime、DateTimeFormatter代替之后就不用再担心不小心出现的日期格式化问题啦。
转换
String <> LocalDate
// String to LocalDate
def preQuarterDate(strDate: String, fmt:String="yyyy-MM-dd"): localDate = {
// val formatter = new DateTimeFormatter(fmt)
val formatter = DateTimeFormatter.ofPattern(fmt)
val localDate = LocalDate.parse(strDate, formatter)
}
// LocalDate to String
//获得当前时间
val ldt:LocalDateTime = LocalDateTime.now();
System.out.println(ldt);
val dtf:DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
val format = ldt.format(dtf);
Timestamp <> LocalDateTime
LocalDateTime current_date_time = LocalDateTime.now();
//returns time and date object of today's date.
//printing the time and date
System.out.println("Local Date Time : " + current_date_time);
//Timestamp object
Timestamp timestamp = Timestamp.valueOf(current_date_time);
System.out.println("Time stamp : " + timestamp_object);
// timestamp to localdatetime
LocalDateTime toLocalDateTime = timestamp.toLocalDateTime();
运算
来计算日期时间差异:
- Period
- Duration
- ChronoUnit
LocalDateTime计算时间差
val now = LocalDateTime.now()
val tomorrow = LocalDateTime.now().plus(1, ChronoUnit.DAYS)
val duration = Duration.between(now, tomorrow)
assert(duration.getSeconds / 60 / 60 == duration.toHours)
例如:duration.toMinutes() //两个时间差的分钟数
- toNanos()//纳秒
- toMillis()//毫秒
- toMinutes()//分钟
- toHours()//小时
- toDays()//天数
字符串做运算
def customAddFormatDate(strFmtDate: String, num: Long = 0, dateFormat: String = "yyyy-MM-dd", chronoUnit: ChronoUnit = ChronoUnit.DAYS): Option[String] = {
val formatter = DateTimeFormatter.ofPattern(dateFormat)
val zoneId = ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))
val dateTime = str2Date(strFmtDate, dateFormat).atStartOfDay().atZone(zoneId).
plus(num, chronoUnit).
format(formatter)
Some(dateTime)
}
Period类
主要是Period类方法getYears(),getMonths()和getDays()来计算.
package insping;
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
public class Test {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("Today : " + today);
LocalDate birthDate = LocalDate.of(1993, Month.OCTOBER, 19);
System.out.println("BirthDate : " + birthDate);
Period p = Period.between(birthDate, today);
System.out.printf("年龄 : %d 年 %d 月 %d 日", p.getYears(), p.getMonths(), p.getDays());
}
}
结果:
Today : 2017-06-16
BirthDate : 1993-10-19
年龄 : 23 年 7 月 28 日
Duration类
提供了使用基于时间的值(如秒,纳秒)测量时间量的方法。
val nowInstant = Instant.now() // 获取当前时间
val epochInstant = Instant.EPOCH // Constant for the 1970-01-01T00:00:00Z epoch instant.
package insping;
import java.time.Duration;
import java.time.Instant;
public class Test {
public static void main(String[] args) {
Instant inst1 = Instant.now();
System.out.println("Inst1 : " + inst1);
Instant inst2 = inst1.plus(Duration.ofSeconds(10));
System.out.println("Inst2 : " + inst2);
System.out.println("Difference in milliseconds : " + Duration.between(inst1, inst2).toMillis());
System.out.println("Difference in seconds : " + Duration.between(inst1, inst2).getSeconds());
}
}
结果:
Inst1 : 2017-06-16T07:46:45.085Z
Inst2 : 2017-06-16T07:46:55.085Z
Difference in milliseconds : 10000
Difference in seconds : 10
ChronoUnit类
ChronoUnit类可用于在单个时间单位内测量一段时间,例如天数或秒。 以下是使用between()方法来查找两个日期之间的区别的示例。
package insping;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
public class Test {
public static void main(String[] args) {
LocalDate startDate = LocalDate.of(1993, Month.OCTOBER, 19);
System.out.println("开始时间 : " + startDate);
LocalDate endDate = LocalDate.of(2017, Month.JUNE, 16);
System.out.println("结束时间 : " + endDate);
long daysDiff = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println("两天之间的差在天数 : " + daysDiff);
}
}
结果:
开始时间 : 1993-10-19
结束时间 : 2017-06-16
两天之间的差在天数 : 8641
格式化日期"yyyy MM dd"
http://docs.oracle.com/javase/8/docs/api/index.html
//把第1个的第一天开始算
LocalDate.parse("2017-01-08",DateTimeFormatter.ofPattern("yyyy-MM-dd")).get(ChronoField.ALIGNED_WEEK_OF_YEAR)
val currentDate =java.time.LocalDate.now.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"))
val d1 =LocalDate.parse("2017-01-01",DateTimeFormatter.ofPattern("yyyy-MM-dd"))
LocalDate date = ...;
// Or use a specific locale, or configure your own rules
WeekFields weekFields = WeekFields.of(Locale.getDefault());
int weekNumber = date.get(weekFields.weekOfWeekBasedYear());
```
```
LocalDate to String
LocalDate date = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String text = date.format(formatter);
LocalDate parsedDate = LocalDate.parse(text, formatter);
Timestamp to LocalDateTime
val ts: Timestamp = new Timestamp(1465551585889L)
val llDatetime: LocalDateTime = ts.toLocalDateTime
val formatLocalDate: String = llDatetime.format(DateTimeFormatter.ofPattern("yyyyMMdd HH"))
String to Ts
DateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMddHH");
Date parse = dateTimeFormat.parse(this.futureDateTime);
java.time.LocalTime --> java.util.Date
LocalDate localDate = LocalDate.now();
ZoneId zone = ZoneId.systemDefault();
Instant instant =localDateTime.atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
localDateTime to Timestamp
LocalDateTime ldt = timeStamp.toLocalDateTime();
Timestamp ts = Timestamp.valueOf(dateTime);
Date与LocalDateTime、LocalDate、LocalTime互转
Java 8中 java.util.Date 类新增了两个方法,分别是from(Instant instant)和toInstant()方法
// Obtains an instance of Date from an Instant object.
public static Date from(Instant instant) {
try {
return new Date(instant.toEpochMilli());
} catch (ArithmeticException ex) {
throw new IllegalArgumentException(ex);
}
}
// Converts this Date object to an Instant.
public Instant toInstant() {
return Instant.ofEpochMilli(getTime());
}
这两个方法使我们可以方便的实现将旧的日期类转换为新的日期类,具体思路都是通过Instant当中介,然后通过Instant来创建LocalDateTime(这个类可以很容易获取LocalDate和LocalTime),新的日期类转旧的也是如此,将新的先转成LocalDateTime,然后获取Instant,接着转成Date,具体实现细节如下:
// java.util.Date --> java.time.LocalDateTime
public void UDateToLocalDateTime() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
}
// java.util.Date --> java.time.LocalDate
public void UDateToLocalDate() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
LocalDate localDate = localDateTime.toLocalDate();
}
// java.util.Date --> java.time.LocalTime
public void UDateToLocalTime() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
LocalTime localTime = localDateTime.toLocalTime();
}
// java.time.LocalDateTime --> java.util.Date
public void LocalDateTimeToUdate() {
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
}
// java.time.LocalDate --> java.util.Date
public void LocalDateToUdate() {
LocalDate localDate = LocalDate.now();
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
}
// java.time.LocalTime --> java.util.Date
public void LocalTimeToUdate() {
LocalTime localTime = LocalTime.now();
LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
}
java 7
//创建不同的日期格式
DateFormat df1 = DateFormat.getInstance();
DateFormat df2 = new SimpleDateFormat("yyyy-MM-01 hh:mm:ss EE");
DateFormat df3 = DateFormat.getDateInstance(DateFormat.FULL, Locale.CHINA); //产生一个指定国家指定长度的日期格式,长度不同,显示的日期完整性也不同 18 DateFormat df4 = new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒 EE", Locale.CHINA);
DateFormat df5 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss EEEEEE", Locale.US);
DateFormat df6 = new SimpleDateFormat("yyyy-MM-dd");
//将日期按照不同格式进行输出
System.out.println("-------将日期按照不同格式进行输出------");
System.out.println("按照Java默认的日期格式,默认的区域 : " + df1.format(date));
System.out.println("按照指定格式 yyyy-MM-dd hh:mm:ss EE ,系统默认区域 :" + df2.format(date));
System.out.println("按照日期的FULL模式,区域设置为中文 : " + df3.format(date));
System.out.println("按照指定格式 yyyy年MM月dd日 hh时mm分ss秒 EE ,区域为中文 : " + df4.format(date));
System.out.println("按照指定格式 yyyy-MM-dd hh:mm:ss EE ,区域为美国 : " + df5.format(date));
System.out.println("按照指定格式 yyyy-MM-dd ,系统默认区域 : " + df6.format(date));
//将符合该格式的字符串转换为日期,若格式不相配,则会出错 32 Date date1 = df1.parse("16-01-24 下午2:32");
Date date2 = df2.parse("2016-01-24 02:51:07 星期日");
Date date3 = df3.parse("2016年01月24日 星期五");
Date date4 = df4.parse("2016年01月24日 02时51分18秒 星期日");
Date date5 = df5.parse("2016-01-24 02:51:18 Sunday");
Date date6 = df6.parse("2016-01-24");
System.out.println("-------输出将字符串转换为日期的结果------");
System.out.println(date1);
System.out.println(date2);
System.out.println(date3);
System.out.println(date4);
System.out.println(date5);
System.out.println(date6);