Discuz! Board

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1755|回复: 5
打印 上一主题 下一主题

Java中的Date

[复制链接]

697

主题

1142

帖子

4086

积分

认证用户组

Rank: 5Rank: 5

积分
4086
跳转到指定楼层
楼主
发表于 2018-5-17 11:27:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 java 于 2018-5-17 11:43 编辑

1.Date中保存的是什么
Date date = new Date();
System.out.println(date);
输出:
Thu May 17 11:23:47 CST 2018

也就是我执行上述代码的时刻:2018年5月17日11点23分47秒。
是不是Date对象里存了年月日时分秒呢?不是的,Date对象里存的只是一个long型的变量,其值为自1970年1月1日0点至Date对象所记录时刻经过的毫秒数,调用Date对象getTime()方法就可以返回这个毫秒数,如下代码:
Date date = new Date();System.out.println(date + ", " + date.getTime());
输出:
Thu May 17 11:27:26 CST 2018, 1526527646259
即上述程序执行的时刻是2018年5月17日11点27分26秒,该时刻距离1970年1月1日0点经过了1526527646259毫秒。反过来说,输出的年月日时分秒其实是根据这个毫秒数来反算出来的。

2.时区

全球分为24个时区,相邻时区时间相差1个小时。比如北京处于东八时区,东京处于东九时区,北京时间比东京时间晚1个小时,而英国伦敦时间比北京晚7个小时(英国采用夏令时时,8月英国处于夏令时)。比如此刻北京时间是2017年8月24日11:17:10,则东京时间是2017年8月24日12:17:10,伦敦时间是2017年8月24日4:17:10。

既然Date里存放的是当前时刻距1970年1月1日0点时刻的毫秒数,如果此刻在伦敦、北京、东京有三个程序员同时执行如下语句:

  1. Date date = new Date();
复制代码
那这三个date对象里存的毫秒数是相同的吗?还是北京的比东京的小3600000(北京时间比东京时间晚1小时,1小时为3600秒即3600000毫秒)?答案是,这3个Date里的毫秒数是完全一样的。确切的说,Date对象里存的是自格林威治时间( GMT)1970年1月1日0点至Date对象所表示时刻所经过的毫秒数。所以,如果某一时刻遍布于世界各地的程序员同时执行new Date语句,这些Date对象所存的毫秒数是完全一样的。也就是说,Date里存放的毫秒数是与时区无关的。


继续上述例子,如果上述3个程序员调用那一刻的时间是北京时间2017年8月24日11:17:10,他们继续调用
  1. System.out.println(date);
复制代码

那么北京的程序员将会打印出2017年8月24日11:17:10,而东京的程序员会打印出2017年8月24日12:17:10,伦敦的程序员会打印出2017年8月24日4:17:10。既然Date对象只存了一个毫秒数,为什么这3个毫秒数完全相同的Date对象,可以打印出不同的时间呢?这是因为Sysytem.out.println函数在打印时间时,会取操作系统当前所设置的时区,然后根据这个时区将同毫秒数解释成该时区的时间。当然我们也可以手动设置时区,以将同一个Date对象按不同的时区输出。可以做如下实验验证:
  1. Date date = new Date(1503544630000L);  // 对应的北京时间是2017-08-24 11:17:10

  2. SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     // 北京
  3. bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));  // 设置北京时区

  4. SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  // 东京
  5. tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));  // 设置东京时区

  6. SimpleDateFormat londonSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 伦敦
  7. londonSdf.setTimeZone(TimeZone.getTimeZone("Europe/London"));  // 设置伦敦时区

  8. System.out.println("毫秒数:" + date.getTime() + ", 北京时间:" + bjSdf.format(date));
  9. System.out.println("毫秒数:" + date.getTime() + ", 东京时间:" + tokyoSdf.format(date));
  10. System.out.println("毫秒数:" + date.getTime() + ", 伦敦时间:" + londonSdf.format(date));
复制代码
输出为:
毫秒数:1503544630000, 北京时间:2017-08-24 11:17:10
毫秒数:1503544630000, 东京时间:2017-08-24 12:17:10
毫秒数:1503544630000, 伦敦时间:2017-08-24 04:17:10

可以看出,同一个Date对象,按不同的时区来格式化,将得到不同时区的时间。由此可见,Date对象里保存的毫秒数和具体输出的时间(即年月日时分秒)是模型和视图的关系,而时区(即Timezone)则决定了将同一个模型展示成什么样的视图。




3.从字符串中读取时间有时我们会遇到从一个字符串中读取时间的要求,即从字符串中解析时间并得到一个Date对象,比如将"2017-8-24 11:17:10"解析为一个Date对象。现在问题来了,这个时间到底指的是北京时间的2017年8月24日11:17:10,还是东京时间的2017年8月24日11:17:10?如果指的是北京时间,那么这个时间对应的东京时间2017年8月24日12:17:10;如果指的是东京时间,那么这个时间对应的北京时间就是2017年8月24日10:17:10。因此,只说年月日时分秒而不说是哪个时区的,是有歧义的,没有歧义的做法是,给出一个时间字符串,同时指明这是哪个时区的时间。从字符串中解析时间的正确作法是:指定时区来解析。示例如下:
  1. String timeStr = "2017-8-24 11:17:10"; // 字面时间
  2. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  3. sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置北京时区
  4. Date d = sdf.parse(timeStr);
  5. System.out.println(sdf.format(d) + ", " + d.getTime());
复制代码
输出为:
2017-08-24 11:17:10, 1503544630000,

将一个时间字符串按不同时区来解释,得到的Date对象的值是不同的。验证如下:
  1. String timeStr = "2017-8-24 11:17:10"; // 字面时间
  2. SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  3. bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
  4. Date bjDate = bjSdf.parse(timeStr);  // 解析
  5. System.out.println("字面时间: " + timeStr +",按北京时间来解释:" + bjSdf.format(bjDate) + ", " + bjDate.getTime());

  6. SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  // 东京
  7. tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));  // 设置东京时区
  8. Date tokyoDate = tokyoSdf.parse(timeStr); // 解析
  9. System.out.println("字面时间: " + timeStr +",按东京时间来解释:"  + tokyoSdf.format(tokyoDate) + ", " + tokyoDate.getTime());
复制代码
输出为:
字面时间: 2017-8-24 11:17:10,按北京时间来解释:2017-08-24 11:17:10, 1503544630000
字面时间: 2017-8-24 11:17:10,按东京时间来解释:2017-08-24 11:17:10, 1503541030000
可以看出,对于"2017-8-24 11:17:10"这个字符串,按北京时间来解释得到Date对象的毫秒数是
1503544630000;而按东京时间来解释得到的毫秒数是1503541030000,前者正好比后者大于3600000毫秒即1个小时,正好是北京时间和东京时间的时差。这很好理解,北京时间2017-08-24 11:17:10对应的毫秒数是1503544630000,而东京时间2017-08-24 11:17:10对应的北京时间其实是2017-08-24 10:17:10(因为北京时间比东京时间晚1个小时),北京时间2017-08-24 10:17:10自然比北京时间2017-08-24 11:17:10少3600000毫秒。

4.将字符串表示的时间转换成另一个时区的时间字符串综合以上分析,如果给定一个时间字符串,并告诉你这是某个时区的时间,要将它转换为另一个时区的时间并输出,正确的做法是:1.将字符串按原时区转换成Date对象;2.将Date对象格式化成目标时区的时间。比如,将北京时间"2017-8-24 11:17:10"输出成东京时间,代码为:
  1. String timeStr = "2017-8-24 11:17:10"; // 字面时间
  2. SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  3. bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
  4. Date date = bjSdf.parse(timeStr);  // 将字符串时间按北京时间解析成Date对象

  5. SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  // 东京
  6. tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));  // 设置东京时区
  7. System.out.println("北京时间: " + timeStr +"对应的东京时间为:"  + tokyoSdf.format(date));
复制代码
输出为:
北京时间:2017-8-24 11:17:10对应的东京时间为:2017-08-24 12:17:10

https://blog.csdn.net/halfclear/article/details/77573956
回复

使用道具 举报

697

主题

1142

帖子

4086

积分

认证用户组

Rank: 5Rank: 5

积分
4086
沙发
 楼主| 发表于 2018-5-17 11:46:45 | 只看该作者
本帖最后由 java 于 2018-5-18 10:50 编辑

时区信息文件:
/usr/lib/jvm/java/jdk1.8.0_131/jre/lib/rt.jar!/sun/util/calendar/ZoneInfoFile.class
{{"ACT", "Australia/Darwin"}, {"AET", "Australia/Sydney"}, {"AGT", "America/Argentina/Buenos_Aires"}, {"ART", "Africa/Cairo"}, {"AST", "America/Anchorage"}, {"BET", "America/Sao_Paulo"}, {"BST", "Asia/Dhaka"}, {"CAT", "Africa/Harare"}, {"CNT", "America/St_Johns"}, {"CST", "America/Chicago"}, {"CTT", "Asia/Shanghai"}, {"EAT", "Africa/Addis_Ababa"}, {"ECT", "Europe/Paris"}, {"IET", "America/Indiana/Indianapolis"}, {"IST", "Asia/Kolkata"}, {"JST", "Asia/Tokyo"}, {"MIT", "Pacific/Apia"}, {"NET", "Asia/Yerevan"}, {"NST", "Pacific/Auckland"}, {"PLT", "Asia/Karachi"}, {"PNT", "America/Phoenix"}, {"PRT", "America/Puerto_Rico"}, {"PST", "America/Los_Angeles"}, {"SST", "Pacific/Guadalcanal"}, {"VST", "Asia/Ho_Chi_Minh"}}



/usr/lib/jvm/java/jdk1.8.0_131/src.zip!/java/time/ZoneId.java
        Map<String, String> map = new HashMap<>(64);
        map.put("ACT", "Australia/Darwin");
        map.put("AET", "Australia/Sydney");
        map.put("AGT", "America/Argentina/Buenos_Aires");
        map.put("ART", "Africa/Cairo");
        map.put("AST", "America/Anchorage");
        map.put("BET", "America/Sao_Paulo");
        map.put("BST", "Asia/Dhaka");
        map.put("CAT", "Africa/Harare");
        map.put("CNT", "America/St_Johns");
        map.put("CST", "America/Chicago");
        map.put("CTT", "Asia/Shanghai");
        map.put("EAT", "Africa/Addis_Ababa");
        map.put("ECT", "Europe/Paris");
        map.put("IET", "America/Indiana/Indianapolis");
        map.put("IST", "Asia/Kolkata");
        map.put("JST", "Asia/Tokyo");
        map.put("MIT", "Pacific/Apia");
        map.put("NET", "Asia/Yerevan");
        map.put("NST", "Pacific/Auckland");
        map.put("PLT", "Asia/Karachi");
        map.put("PNT", "America/Phoenix");
        map.put("PRT", "America/Puerto_Rico");
        map.put("PST", "America/Los_Angeles");
        map.put("SST", "Pacific/Guadalcanal");
        map.put("VST", "Asia/Ho_Chi_Minh");
        map.put("EST", "-05:00");
        map.put("MST", "-07:00");
        map.put("HST", "-10:00");

回复 支持 反对

使用道具 举报

697

主题

1142

帖子

4086

积分

认证用户组

Rank: 5Rank: 5

积分
4086
板凳
 楼主| 发表于 2018-5-18 11:07:59 | 只看该作者
本帖最后由 java 于 2018-5-18 11:16 编辑

public void testDate(){
                Date date = new Date(1503544630000L);  // 对应的北京时间是2017-08-24 11:17:10

                SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     // 北京
                bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));  // 设置北京时区


                SimpleDateFormat bjSdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     // 北京2
                bjSdf2.setTimeZone(TimeZone.getTimeZone("CTT"));  // 设置北京时区

                SimpleDateFormat bjSdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     // 北京3
                bjSdf3.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));  // 设置北京时区

                //T代表后面跟着时间,Z代表UTC统一时间
                SimpleDateFormat sdfUTC = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                sdfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));

                SimpleDateFormat bjSdfGMT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     //
                bjSdfGMT.setTimeZone(TimeZone.getTimeZone("GMT"));

                SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  // 东京
                tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));  // 设置东京时区

                System.out.println("毫秒数:" + date.getTime() + ", 北京时间:" + bjSdf.format(date));
                System.out.println("毫秒数:" + date.getTime() + ", 北京时间2:" + bjSdf2.format(date));
                System.out.println("毫秒数:" + date.getTime() + ", 北京时间3:" + bjSdf3.format(date));
                System.out.println("毫秒数:" + date.getTime() + ", GMT:" + bjSdfGMT.format(date));
                System.out.println("毫秒数:" + date.getTime() + ", UTC:" + sdfUTC.format(date));
                System.out.println("毫秒数:" + date.getTime() + ", UTC2:" + date);
                System.out.println("毫秒数:" + date.getTime() + ", 东京时间:" + tokyoSdf.format(date));
        }


毫秒数:1503544630000, 北京时间:2017-08-24 11:17:10
毫秒数:1503544630000, 北京时间2:2017-08-24 11:17:10
毫秒数:1503544630000, 北京时间3:2017-08-24 03:17:10
毫秒数:1503544630000, GMT:2017-08-24 03:17:10
毫秒数:1503544630000, UTC:2017-08-24T03:17:10Z
毫秒数:1503544630000, UTC2:Thu Aug 24 11:17:10 CST 2017
毫秒数:1503544630000, 东京时间:2017-08-24 12:17:10

Java Date使用UTC时间,如 Tue Jan 05 14:28:41 CST 2016 表示China Standard Time UT+8:00 。

回复 支持 反对

使用道具 举报

697

主题

1142

帖子

4086

积分

认证用户组

Rank: 5Rank: 5

积分
4086
地板
 楼主| 发表于 2018-5-22 16:33:12 | 只看该作者
基于java8
  1.         ZonedDateTime beijingDateTime = ZonedDateTime.parse("2018-05-01 12:01:01", beijingFormatter);
  2.                 System.out.println(beijingDateTime.withZoneSameInstant(ZoneId.of("UTC")));
  3.                 System.out.println(beijingDateTime.withZoneSameInstant(ZoneId.of(timeZone)));
  4.                 Date from = Date.from(beijingDateTime.toInstant());
  5.                 SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  6.                 bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
  7.                 System.out.println("毫秒数:" + from.getTime() + ", 北京时间:" + bjSdf.format(from));


  8.                 Date date = new Date(1503544630000L);  // 对应的北京时间是2017-08-24 11:17:10
  9.                 Instant instantStartQuery = date.toInstant();
  10. //                ZonedDateTime beijingDateTimeStartQuery = ZonedDateTime.ofInstant(instantStartQuery, ZoneId.of(timeZone));
  11. //                System.out.println(beijingDateTimeStartQuery.withZoneSameInstant(ZoneId.of(timeZone)));
  12.                 LocalDateTime localQueryStart = instantStartQuery.atZone(ZoneId.of(timeZone)).toLocalDateTime();
  13.                 System.out.println("查询时间:" + localQueryStart);
  14.                 LocalDateTime preYearLocal = localQueryStart.plusYears(-1);
  15.                 System.out.println("前一年:" + preYearLocal);
  16.                 LocalDateTime lastYearLocal = localQueryStart.plusYears(1);
  17.                 System.out.println("后一年:" + lastYearLocal);

  18.                 LocalDateTime lastMonthrLocal = localQueryStart.plusMonths(5);
  19.                 System.out.println("后一月:" + lastMonthrLocal);

  20.                 LocalDateTime lastWeekLocal = localQueryStart.plusWeeks(1);
  21.                 System.out.println("后一周:" + lastWeekLocal);

  22.                 LocalDateTime lastDayLocal = localQueryStart.plusDays(1);
  23.                 System.out.println("后一天:" + lastDayLocal);

  24.                 localQueryStart = localQueryStart.plusDays(-1);
  25.                 System.out.println("前一天:" + localQueryStart);

  26.                 Date queryTime =  Date.from(localQueryStart.atZone(ZoneId.of(timeZone)).toInstant());
  27.                 System.out.println("查询时间:" + bjSdf.format(queryTime));


  28.                 localQueryStart = localQueryStart.plusDays(1);
  29.                 localQueryStart = LocalDateTime.of(localQueryStart.getYear(), localQueryStart.getMonth(), localQueryStart.getDayOfMonth(), 0, 0, 0);
  30.                 System.out.println("查询时间去掉时分秒:" + localQueryStart);
复制代码
回复 支持 反对

使用道具 举报

697

主题

1142

帖子

4086

积分

认证用户组

Rank: 5Rank: 5

积分
4086
5#
 楼主| 发表于 2018-5-24 14:15:03 | 只看该作者
本帖最后由 java 于 2018-5-24 14:17 编辑

Date->Instant->LocalDateTime->ZonedDateTime->Date
    String timeZone = "Asia/Shanghai";
    Date date = new Date(1503544630000L);  // 对应的北京时间是2017-08-24 11:17:10
    Instant instantStartQuery = date.toInstant();
    LocalDateTime localQueryStart = instantStartQuery.atZone(ZoneId.of(timeZone)).toLocalDateTime();
    localQueryStart = localQueryStart.plusDays(1);
    ZonedDateTime zdt = localQueryStart.atZone(ZoneId.of(timeZone));
    Date dateNewDay = Date.from(zdt.toInstant());



LocalDateTime localDateTime1 = null;
    LocalDateTime localDateTime2 = null;
localDateTime1 = LocalDateTime.of(localDateTime1.getYear(), localDateTime1.getMonth(), localDateTime1.getDayOfMonth(), localDateTime2.getHour(), localDateTime2.getMinute(), localDateTime2.getSecond(), localDateTime2.getNano());

回复 支持 反对

使用道具 举报

697

主题

1142

帖子

4086

积分

认证用户组

Rank: 5Rank: 5

积分
4086
6#
 楼主| 发表于 2018-5-25 10:56:43 | 只看该作者
本帖最后由 java 于 2018-5-25 14:26 编辑

https://www.cnblogs.com/exmyth/p/6425878.html
https://blog.csdn.net/u010088951/article/details/56489075

Date date = new Date(eventQueryVO.getStartTime());
        Instant instant = date.toInstant();
        java.time.LocalDateTime localQueryStart = LocalDateTime.ofInstant(instant, ZoneId.of(timeZone));
        java.time.LocalDate localDate = localQueryStart.toLocalDate();
        Date startQueryDate = getStartDayOfWeek(localDate);
        eventQueryVO.setStartTime(startQueryDate.getTime());

private Date getStartDayOfWeek(TemporalAccessor date) {
     TemporalField fieldISO = WeekFields.of(Locale.CHINA).dayOfWeek();
     java.time.LocalDate localDate = java.time.LocalDate.from(date);
     localDate=localDate.with(fieldISO, 1);
     return localDate2Date(localDate);
}
private Date localDate2Date(java.time.LocalDate localDate){
     ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
     Instant instant1 = zonedDateTime.toInstant();
     Date from = Date.from(instant1);
     return  from;
}
private Date getEndDayOfWeek(TemporalAccessor date) {
        TemporalField fieldISO = WeekFields.of(Locale.CHINA).dayOfWeek();
        java.time.LocalDate localDate = java.time.LocalDate.from(date);
        localDate=localDate.with(fieldISO, 7);
        return Date.from(localDate.atStartOfDay(ZoneId.of(timeZone)).plusDays(1L).minusNanos(1L).toInstant());
    }

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|firemail ( 粤ICP备15085507号-1 )

GMT+8, 2024-5-5 21:40 , Processed in 0.063196 second(s), 18 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表