※多執行緒用 SimpleDateFormat 有問題
DateFormat df = new SimpleDateFormat("yyyyMMdd HHmmss"); ExecutorService es = Executors.newFixedThreadPool(5); Stream.iterate(0, i -> ++i).limit(1000).forEach(i -> { es.execute(() -> { try { // System.out.println(df.parse("20101112 235958")); // 要用時再 new 可解決,但浪費記憶體 System.out.println(new SimpleDateFormat("yyyyMMdd HHmmss").parse("20101112 235958")); // synchronized 也可解決,但高併發時會阻塞 // 用 ThreadLocal 也可解決 System.out.println(ThreadLocalDateFormat.convertDate("20101112 235958")); } catch (ParseException e) { e.printStackTrace(); } }); }); es.shutdown(); class ThreadLocalDateFormat { private static ThreadLocal<DateFormat> tl = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd HHmmss")); static Date convertDate(String ymdhms) throws ParseException { return tl.get().parse(ymdhms); } }
※日期
java.time開頭的5個package都是java8新增的LocalDate、LocalTime、LocalDateTime看要不要有年月日或時分秒用的
※LocalDate
LocalDate today = LocalDate.now(); System.out.println("現在日期=" + today); System.out.println("下星期=" + today.plus(1, ChronoUnit.WEEKS)); System.out.println("上一個月=" + today.plus(-1, ChronoUnit.MONTHS)); System.out.println("明年=" + today.plus(1, ChronoUnit.YEARS)); System.out.println("下一個十年" + today.plus(1, ChronoUnit.DECADES));
※ChronoUnit還可以加100年、1000年…等
※ChronoUnit.YEARS
ChronoUnit.WEEKS
ChronoUnit.MONTHS
ChronoUnit.DAYS
ChronoUnit.HOURS
ChronoUnit.MINUTES
ChronoUnit.SECONDS
ChronoUnit.MILLIS
ChronoUnit.NANOS
時、分、秒、毫秒、毫微秒要使用 LocalDateTime
※日期相差
LocalDate date1 = LocalDate.now(); System.out.println("date1=" + date1); LocalDate date2 = date1.plus(1, ChronoUnit.MONTHS); System.out.println("date2=" + date2); // 右邊減左邊 Period period = Period.between(date1, date2); System.out.println("相差=" + period);// P1M LocalTime time1 = LocalTime.now(); System.out.println("time1=" + time1); Duration twoHours = Duration.ofHours(-2); LocalTime time2 = time1.plus(twoHours); System.out.println("time2=" + time2); // 右邊減左邊 Duration duration = Duration.between(time1, time2); System.out.println("Duration: " + duration);// PT-2H
※開頭的P和PT固定會有,可以再 getXXX 取得數字,但時分秒是分開的,要小心
※上/下 一個日期
LocalDate today = LocalDate.now(); System.out.println("現在日期=" + today); LocalDate nextTuesday = today.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)); System.out.println("下個星期二=" + nextTuesday); // 這個月的第一天 LocalDate one = LocalDate.of(today.getYear(), today.getMonth(), 1); LocalDate two = one.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY)); System.out.println("下一個星期六(如果1號也是星期六,那就是1號)=" + two); LocalDate three = two.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)); System.out.println("下一個星期五=" + three);
※要注意next和nextOrSame的差別
※LocalDateTime
LocalDateTime currentTime = LocalDateTime.now(); System.out.println("現在日期時間=" + currentTime);// 2016-12-13T17:43:00.427 System.out.println("年月日=" + currentTime.toLocalDate());// 2016-12-13 System.out.println("年=" + currentTime.getYear());// 2016 System.out.println("1月1日到現在是第幾天=" + currentTime.getDayOfYear());// 348 System.out.println("英文月=" + currentTime.getMonth());// DECEMBER System.out.println("月=" + currentTime.getMonthValue());// 12 System.out.println("x月1日到現在是第幾天=" + currentTime.getDayOfMonth());// 13 System.out.println("星期=" + currentTime.getDayOfWeek());// TUESDAY System.out.println("時=" + currentTime.getHour());// 17 System.out.println("分=" + currentTime.getMinute());// 43 System.out.println("秒=" + currentTime.getSecond());// 0 System.out.println("毫秒=" + currentTime.getNano());// 427000000 System.out.println("getChronology=" + currentTime.getChronology());// ISO LocalDateTime defineymd = currentTime.withDayOfMonth(10).withYear(2012); // 沒with的就以現在時間為主 System.out.println("自訂日期: " + defineymd);// 2012-12-10T17:43:00.427 System.out.println("年月日: " + LocalDate.of(2014, Month.DECEMBER, 12));// 2014-12-12 System.out.println("時分: " + LocalTime.of(22, 15));// 22:15 System.out.println("時分秒: " + LocalTime.parse("20:15:30"));// 20:15:30
※LocalDateTime 相關的 class,預設已經加上時區了,但不包括日光節約時間,
台灣日光節約日期可參考這裡,1975/4/1 - 1975/9/30 是其中一組,
所可以用 1975/3/31 23:59:59 加 1 秒會變成凌晨 1 點
1975/9/30 加 1 秒,會變成 1975-09-30T23:00+08:00[Asia/Taipei],
再扣一小時會變成 1975-09-30T23:00+09:00[Asia/Taipei],
也就是在日光節約時間裡是 UTC+9
※預設時區的名字,可用 ZoneId.systemDefault() 替看
※ZonedDateTime比LocalDateTime多了ZoneOffset和ZoneId
※ZoneOffset就是+08:00這種東東;而parse裡面的[]就是ZoneId,可以使用ZoneId.systemDefault()取得
※ZoneOffset.ofHours(8)裡的8,上一行印出ZonedDateTime時,可以知道
※Date和Calendar在1.8也新增了toInstant方法,可以和新的日期做轉換使用
※Date有from可以轉成ZonedDateTime和LocalDateTime;Calendar沒看到有轉換成Instant的方法,只好和以前一樣,轉換成Date了
※如果固定是 GMT +或-多少,直接寫死的就行,相當於 OffsetDateTime
※
※
※ZonedDateTime和轉換
// ZoneId.getAvailableZoneIds().forEach(System.out::println);
ZonedDateTime d = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]"); System.out.println("d=" + d); ZoneId id = ZoneId.of("Europe/Paris"); // ZoneId.of(ZoneId.SHORT_IDS.get("ECT")); System.out.println("ZoneId=" + id); System.out.println("================================"); ZoneId zoneId = ZoneId.systemDefault(); System.out.println("zoneId=" + zoneId);
ZonedDateTime zonedDateTime2 = ZonedDateTime.of( LocalDateTime.of(2021, 2, 20, 10, 0, 0), zoneId)
System.out.println(zonedDateTime2.getOffset().getTotalSeconds() / 60 / 60); // 取得 UTC 差幾小時 Date today = new Date(); System.out.println("現在時間=" + today); Instant dateInstant = today.toInstant(); // Date轉LocalDateTime LocalDateTime localDateTime = LocalDateTime.ofInstant(dateInstant, zoneId); System.out.println("LocalDateTime=" + localDateTime); // Date轉ZonedDateTime ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(dateInstant, zoneId); System.out.println("ZonedDateTime=" + zonedDateTime); // LocalDateTime轉Date Instant instant = localDateTime.toInstant(ZoneOffset.ofHours(8)); Date date = Date.from(instant); System.out.println(date); // ZonedDateTime轉Date System.out.println(Date.from(zonedDateTime.toInstant())); System.out.println("================================"); Calendar calendar = Calendar.getInstance(); Instant calendarInstant = calendar.toInstant(); // Calendar轉LocalDateTime LocalDateTime localDateTime2 = LocalDateTime.ofInstant(calendarInstant, zoneId); System.out.println("LocalDateTime2=" + localDateTime2); // Calendar轉ZonedDateTime ZonedDateTime zonedDateTime2 = ZonedDateTime.ofInstant(calendarInstant, zoneId); System.out.println("ZonedDateTime2=" + zonedDateTime2);
OffsetDateTime offsetDateTime2 = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.of("-5"));
※ZonedDateTime比LocalDateTime多了ZoneOffset和ZoneId
※ZoneOffset就是+08:00這種東東;而parse裡面的[]就是ZoneId,可以使用ZoneId.systemDefault()取得
※ZoneOffset.ofHours(8)裡的8,上一行印出ZonedDateTime時,可以知道
※Date和Calendar在1.8也新增了toInstant方法,可以和新的日期做轉換使用
※Date有from可以轉成ZonedDateTime和LocalDateTime;Calendar沒看到有轉換成Instant的方法,只好和以前一樣,轉換成Date了
※OffsetDateTime 並沒有考慮日光節約時間;而 ZonedDateTime 有考慮到
※GMT 是用地球公轉來表示;UTC 是用銫原子去做,是人為的,但因為地球公轉有變慢的趨勢,所以有做加 1 秒的動作,會變成 23:59:60,時間越久,GMT 和 UTC 會越差越多,我測的 java 是在 windows 裡,沒有測出這個
※這裡可以查詢時區和日光節約時區的資訊
※如果有夏令時的國家 ZoneId 要用類似 America/Los_Angeles 這樣的格式才可以
※如果固定是 GMT +或-多少,直接寫死的就行,相當於 OffsetDateTime
※西方人的想法是以他們為主,所以西方的 GMT 是+的,東方是-的,如果要用西方的格式要寫成 Etc/GMT-8,用我們的想法要寫成 GMT+8
LocalDateTime localDateTime = LocalDateTime.of(2024, 3, 15, 0, 0, 0);
LocalDateTime localDateTime = LocalDateTime.of(2024, 3, 15, 0, 0, 0);
ZonedDateTime z1 = ZonedDateTime.of(localDateTime, ZoneId.of("Etc/GMT-8"));
ZonedDateTime z2 = ZonedDateTime.of(localDateTime, ZoneId.of("GMT+8"));
ZonedDateTime z2 = ZonedDateTime.of(localDateTime, ZoneId.of("GMT+8"));
※z1 和 z2 是一樣的時間,差在東西方的格式而已
※有時間的字串轉換
時區要寫在 DateTimeFormatter 的 withZone 方法
final String DATE = "2018-09-18T19:13:00Z"; // Z 就是 zone 的意思
ZonedDateTime z0 = ZonedDateTime.parse(DATE);
ZonedDateTime z1 = ZonedDateTime.parse(DATE, DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneId.of("Asia/Taipei")));
ZonedDateTime z2 = ZonedDateTime.parse(DATE, DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneId.of("America/Indiana/Indianapolis")));
2018-09-18T19:13Z // 沒有 zone 資訊
2018-09-18T19:13+08:00[Asia/Taipei] // +8小時後的時間
2018-09-18T19:13-04:00[America/Indiana/Indianapolis] // -4小時後的時間
※DateTimeFormatter 的 X
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss XXX");
ZonedDateTime tmpTimestamp = ZonedDateTime.parse("2018-07-31 12:13:14 +03:00", formatter);
偏移量X和x:它將根據圖案字母的數量來格式化偏移量。
除非分鐘非零,否則一個字母僅輸出小時,例如“+01”,在這種情況下,分鐘也會輸出,例如“+0130”。
兩個字母輸出小時和分鐘,不帶冒號,例如'+0130'。
三個字母輸出小時和分鐘,並帶有冒號,例如'+01:30'。
四個字母輸出小時和分鐘,可選秒,不帶冒號,例如“+013015”。
五個字母輸出小時和分鐘,可選秒,以冒號表示,例如“+01:30:15”。
六個或更多字母會引發IllegalArgumentException。
當要輸出的偏移量為零時,模式字母“X”(大寫)將輸出“Z”,而模式字母“x”(小寫)將輸出“+00”,“+ 0000”或“+00” :00'。
或者,您可以使用五個字母(XXXXX),也可以使用ZZZ或ZZZZZ代替XXX或XXXXX。
※Base64
java.util包裡有三個Base64開頭的class,也都是java8新增的※
try { // BASIC Encode String encode = Base64.getEncoder().encodeToString("xxx".getBytes("UTF-8")); System.out.println("encode=" + encode); // Decode byte[] decode = Base64.getDecoder().decode(encode); System.out.println("basic decode=" + new String(decode, "UTF-8")); System.out.println(); // URL/Filename safe Encode String urlEncode = Base64.getUrlEncoder().encodeToString("http://www.google.com".getBytes("UTF-8")); System.out.println("urlEncode=" + urlEncode); // Decode System.out.println("url decode=" + new String(Base64.getDecoder().decode(urlEncode), "UTF-8")); System.out.println(); // MIME Encode byte[] mimeBytes = new String("text/html").getBytes("UTF-8"); String mimeEncode = Base64.getMimeEncoder().encodeToString(mimeBytes); System.out.println("mimeEncode=" + mimeEncode); // Decode System.out.println("mime decode=" + new String(Base64.getDecoder().decode(mimeEncode), "UTF-8")); } catch (UnsupportedEncodingException e) { System.out.println("Error :" + e.getMessage()); }
※
沒有留言:
張貼留言