日期时间处理

目录

1970

世界时区划分

Code

ISO-8601

java8

转换

String <> LocalDate

Timestamp <> LocalDateTime

运算

LocalDateTime计算时间差

 字符串做运算

Duration类

ChronoUnit类

Date与LocalDateTime、LocalDate、LocalTime互转

java 7


早期使用的是格林尼治时间(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 &plusmn; 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); 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大怀特

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值