偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

寫(xiě)了這么多年DateUtils,殊不知你還有這么多彎彎繞!

開(kāi)發(fā) 后端
Java 8 推出了新的時(shí)間日期類(lèi) ZoneId、ZoneOffset、LocalDateTime、ZonedDateTime 和 DateTimeFormatter,處理時(shí)區(qū)問(wèn)題更簡(jiǎn)單清晰。

大家好,我是哪吒。

在日常開(kāi)發(fā)中,Date工具類(lèi)使用頻率相對(duì)較高,大家通常都會(huì)這樣寫(xiě):
public static Date getData(String date) throws ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf.parse(date);
}

public static Date getDataByFormat(String date, String format) throws ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat(format);
    return sdf.parse(date);
}
這很簡(jiǎn)單啊,有什么爭(zhēng)議嗎?

你應(yīng)該聽(tīng)過(guò)“時(shí)區(qū)”這個(gè)名詞,大家也都知道,相同時(shí)刻不同時(shí)區(qū)的時(shí)間是不一樣的。

因此在使用時(shí)間時(shí),一定要給出時(shí)區(qū)信息。

public static void getDataByZone(String param, String format) throws ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat(format);

    // 默認(rèn)時(shí)區(qū)解析時(shí)間表示
    Date date = sdf.parse(param);
    System.out.println(date + ":" + date.getTime());

    // 東京時(shí)區(qū)解析時(shí)間表示
    sdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));
    Date newYorkDate = sdf.parse(param);
    System.out.println(newYorkDate + ":" + newYorkDate.getTime());
}

public static void main(String[] args) throws ParseException {
   getDataByZone("2023-11-10 10:00:00","yyyy-MM-dd HH:mm:ss");
}

對(duì)于當(dāng)前的上海時(shí)區(qū)和紐約時(shí)區(qū),轉(zhuǎn)化為 UTC 時(shí)間戳是不同的時(shí)間。

對(duì)于同一個(gè)本地時(shí)間的表示,不同時(shí)區(qū)的人解析得到的 UTC 時(shí)間一定是不同的,反過(guò)來(lái)不同的本地時(shí)間可能對(duì)應(yīng)同一個(gè) UTC。

格式化后出現(xiàn)的時(shí)間錯(cuò)亂:
public static void getDataByZoneFormat(String param, String format) throws ParseException {
   SimpleDateFormat sdf = new SimpleDateFormat(format);
    Date date = sdf.parse(param);
    // 默認(rèn)時(shí)區(qū)格式化輸出
    System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss Z]").format(date));
    // 東京時(shí)區(qū)格式化輸出
    TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
    System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss Z]").format(date));
}

public static void main(String[] args) throws ParseException {
   getDataByZoneFormat("2023-11-10 10:00:00","yyyy-MM-dd HH:mm:ss");
}

我當(dāng)前時(shí)區(qū)的 Offset(時(shí)差)是 +8 小時(shí),對(duì)于 +9 小時(shí)的紐約,整整差了1個(gè)小時(shí),北京早上 10 點(diǎn)對(duì)應(yīng)早上東京 11 點(diǎn)。

看看Java 8是如何解決時(shí)區(qū)問(wèn)題的:

Java 8 推出了新的時(shí)間日期類(lèi) ZoneId、ZoneOffset、LocalDateTime、ZonedDateTime 和 DateTimeFormatter,處理時(shí)區(qū)問(wèn)題更簡(jiǎn)單清晰。

public static void getDataByZoneFormat8(String param, String format) throws ParseException {
    ZoneId zone = ZoneId.of("Asia/Shanghai");
    ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");
    ZoneId timeZone = ZoneOffset.ofHours(2);

    // 格式化器
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format);
    ZonedDateTime date = ZonedDateTime.of(LocalDateTime.parse(param, dtf), zone);

    // withZone設(shè)置時(shí)區(qū)
    DateTimeFormatter dtfz = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z");
    System.out.println(dtfz.withZone(zone).format(date));
    System.out.println(dtfz.withZone(tokyoZone).format(date));
    System.out.println(dtfz.withZone(timeZone).format(date));
}

public static void main(String[] args) throws ParseException {
    getDataByZoneFormat8("2023-11-10 10:00:00","yyyy-MM-dd HH:mm:ss");
}
  • Asia/Shanghai對(duì)應(yīng)+8,對(duì)應(yīng)2023-11-10 10:00:00。
  • Asia/Tokyo對(duì)應(yīng)+9,對(duì)應(yīng)2023-11-10 11:00:00。
  • timeZone 是+2,所以對(duì)應(yīng)2023-11-10 04:00:00。

在處理帶時(shí)區(qū)的國(guó)際化時(shí)間問(wèn)題,推薦使用jdk8的日期時(shí)間類(lèi):
  • 通過(guò)ZoneId,定義時(shí)區(qū);
  • 使用ZonedDateTime保存時(shí)間;
  • 通過(guò)withZone對(duì)DateTimeFormatter設(shè)置時(shí)區(qū);
  • 進(jìn)行時(shí)間格式化得到本地時(shí)間;

思路比較清晰,不容易出錯(cuò)。

在與前端聯(lián)調(diào)時(shí),報(bào)了個(gè)錯(cuò),java.lang.NumberFormatException: multiple points,起初我以為是時(shí)間格式傳的不對(duì),仔細(xì)一看,不對(duì)啊。

百度一下,才知道是高并發(fā)情況下SimpleDateFormat有線程安全的問(wèn)題。

下面通過(guò)模擬高并發(fā),把這個(gè)問(wèn)題復(fù)現(xiàn)一下:

public static void getDataByThread(String param, String format) throws InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(5);
    SimpleDateFormat sdf = new SimpleDateFormat(format);
    // 模擬并發(fā)環(huán)境,開(kāi)啟5個(gè)并發(fā)線程
    for (int i = 0; i < 5; i++) {
        threadPool.execute(() -> {
            for (int j = 0; j < 2; j++) {
                try {
                    System.out.println(sdf.parse(param));
                } catch (ParseException e) {
                    System.out.println(e);
                }
            }
        });
    }
    threadPool.shutdown();

    threadPool.awaitTermination(1, TimeUnit.HOURS);
}

果不其然,報(bào)錯(cuò)。還將2023年轉(zhuǎn)換成2220年,我勒個(gè)乖乖。

在時(shí)間工具類(lèi)里,時(shí)間格式化,我都是這樣弄的啊,沒(méi)問(wèn)題啊,為啥這個(gè)不行?原來(lái)是因?yàn)楣灿昧送粋€(gè)SimpleDateFormat,在工具類(lèi)里,一個(gè)線程一個(gè)SimpleDateFormat,當(dāng)然沒(méi)問(wèn)題啦!

可以通過(guò)TreadLocal 局部變量,解決SimpleDateFormat的線程安全問(wèn)題。

public static void getDataByThreadLocal(String time, String format) throws InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(5);

    ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat(format);
        }
    };

    // 模擬并發(fā)環(huán)境,開(kāi)啟5個(gè)并發(fā)線程
    for (int i = 0; i < 5; i++) {
        threadPool.execute(() -> {
            for (int j = 0; j < 2; j++) {
                try {
                    System.out.println(sdf.get().parse(time));
                } catch (ParseException e) {
                    System.out.println(e);
                }
            }
        });
    }
    threadPool.shutdown();

    threadPool.awaitTermination(1, TimeUnit.HOURS);
}

看一下SimpleDateFormat.parse的源碼:
public class SimpleDateFormat extends DateFormat {
 @Override
 public Date parse(String text, ParsePosition pos){
  CalendarBuilder calb = new CalendarBuilder();
  
  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();
          }
      }
  }
 }
}

class CalendarBuilder {
 Calendar establish(Calendar cal) {
     boolean weekDate = isSet(WEEK_YEAR)
                         && field[WEEK_YEAR] > field[YEAR];
     if (weekDate && !cal.isWeekDateSupported()) {
         // Use YEAR instead
         if (!isSet(YEAR)) {
             set(YEAR, field[MAX_FIELD + WEEK_YEAR]);
         }
         weekDate = false;
     }
 
     cal.clear();
     // Set the fields from the min stamp to the max stamp so that
     // the field resolution works in the Calendar.
     for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
         for (int index = 0; index <= maxFieldIndex; index++) {
             if (field[index] == stamp) {
                 cal.set(index, field[MAX_FIELD + index]);
                 break;
             }
         }
     }
  ...
 
 }
}
  • 先new CalendarBuilder()。
  • 通過(guò)parsedDate = calb.establish(calendar).getTime();解析時(shí)間。
  • establish方法內(nèi)先cal.clear(),再重新構(gòu)建cal,整個(gè)操作沒(méi)有加鎖。

上面幾步就會(huì)導(dǎo)致在高并發(fā)場(chǎng)景下,線程1正在操作一個(gè)Calendar,此時(shí)線程2又來(lái)了。線程1還沒(méi)來(lái)得及處理 Calendar 就被線程2清空了。

因此,通過(guò)編寫(xiě)Date工具類(lèi),一個(gè)線程一個(gè)SimpleDateFormat,還是有一定道理的。

責(zé)任編輯:姜華 來(lái)源: 哪吒編程
相關(guān)推薦

2023-11-13 08:49:54

2021-01-14 05:08:44

編譯鏈接

2017-06-16 16:16:36

庫(kù)存扣減查詢

2021-02-03 08:24:32

JavaScript技巧經(jīng)驗(yàn)

2018-10-06 21:51:37

代碼SOLID編程

2017-07-04 14:01:40

機(jī)房機(jī)柜

2018-10-07 06:30:40

代碼設(shè)計(jì)模式面向?qū)ο笤瓌t

2018-06-26 15:00:24

Docker安全風(fēng)險(xiǎn)

2022-05-29 08:54:44

Edge瀏覽器

2022-07-26 23:43:29

編程語(yǔ)言開(kāi)發(fā)Java

2017-12-21 19:38:50

潤(rùn)乾中間表

2015-03-27 10:20:41

谷歌地圖谷歌偉大

2022-03-03 07:00:43

Mybatiswhere標(biāo)簽

2018-05-29 14:57:59

HashMap容量初始化

2017-11-30 07:30:27

程序員代碼軟件世界觀

2024-05-13 16:22:25

固態(tài)硬盤(pán)接口硬盤(pán)

2013-01-15 09:41:45

編程語(yǔ)言

2021-05-21 05:24:03

Excel數(shù)據(jù)技巧

2024-01-31 12:34:16

panic錯(cuò)誤檢測(cè)recover

2024-01-02 12:48:49

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)