🔥
Caster 開發日誌
  • Java
    • JVM Performance Tool
      • Java Debug Wire Protocol (JDWP) 的詳細介紹
      • JConsole 詳細介紹
    • Spring Boot
      • Spring Security
      • Spring Boot Admin
      • Spring Event
      • Spring AOP
      • Spring Boot JUnit 5
      • Apache Dubbo
    • Reflect 應用
    • ELK + F 建構
    • Socket.IO
    • OCR - 光學字元辨識
    • 讀取JAR resource文件
    • LocalTime & MySQL時間精度
    • Gradle multi module
    • MyBatis-Plus
    • Java Date operation
    • Java IP to Long
    • Apache Commons lang3 應用
      • Function 應用
    • Cloud Platform
      • Amazon S3
        • SDK V1
          • Bucket
        • SDK V2
          • Bucket
      • Google Cloud Platform
      • Azure Cloud
        • Storage
      • OVHcloud
        • Config
    • SSL/TLS工具
    • Util 工具
      • Jackson Json工具
      • Charles應用
      • JMeter – Performing Distributed Load Testing with Docker
    • Redis
      • Stream
      • Redisson 分布式鎖機制
      • Create Redis Cluster Using Docker
      • List Operations
    • Java 8
      • method & constructor Reference
      • CompletableFuture
      • FunctionInterface
      • Stream 應用
      • 繁簡轉換 - 簡易調整
    • MySQL
      • 建立測試用 流水號Table
      • SQL 效能調校 - Explain
      • SQL 效能調校 - Partition
      • 排程 - Event
    • Apache ShardingSphere
  • Kubernetes
    • 初入江湖(K8S)
    • 零中斷服務滾動更新
    • Kubernetes DNS
    • Ingress & Ingress Controller 教學
    • Ingress TLS Easy setup
  • 指令集
  • Telegram
  • SourceTree
    • 踩坑紀錄(ㄧ) - Git Flow
    • 踩坑紀錄(二) - 修改檔名
  • 專案統計
    • Robot
    • Recharge
  • GitHub
    • Actions
  • GitLab
    • 介紹 GitLab
    • 使用 Docker 自架 GitLab
    • 簡介 GitLab CI/CD
      • GitLab Runner 詳細介紹與設定方式
Powered by GitBook
On this page
  • 前情提要
  • 人狠,話不多,上扣!!
  • 測試!
  • 結論
  1. Java

Java Date operation

有關Java Date 相關操作,工作遇到時區轉換的問題,透過此次機會一起統整經過及方法。

前情提要

Java Date 物件底層都是透過TimeZone 來去做時區判斷,但是這會依賴系統時間,即使透過TimeZone setDefaultTimeZone 來去做完時區轉換的方式,但是這無法一勞永逸且不具有彈性的設計,所以才會有這篇文章的誕生。 我這邊也不賣關子,我自己測試下來的結論,Date物件不好操作,日後有關時區問題,我是會棄用他的。

人狠,話不多,上扣!!

/**
 * convert date String with time zone to LocalDateTime Object
 *
 * @param dateStr
 * @param formatterPattern
 * @param dateStrTimeZone
 * @param toTimeZon
 * @return
 * @throws ParseException
 */
private static LocalDateTime convertDateStrWithTimeZone(String dateStr, String formatterPattern, TimeZone dateStrTimeZone, ZoneId toTimeZon) throws ParseException {
    /**
     * 這邊會發生兩件事情
     * 1.SimpleDateFormat 認定字串 為 GMT+0
     * 2.Date Object 將轉換的GMT+0 加上系統預設時區
     * Ex. "2023-03-23 12:00:00" -> 是GMT+0 時區的時間
     * 當下系統 default 時區是 GMT+8
     * 轉換結果就會變成 2023-03-23 20:00:00[GMT+8]
     */

    // Create a SimpleDateFormat object for parsing the date string
    SimpleDateFormat sdf = new SimpleDateFormat(formatterPattern);

    // Set the time zone of the SimpleDateFormat object to GMT+0
    sdf.setTimeZone(dateStrTimeZone);

    // Parse the date string into an Instant object and transfer to LocalDataTime
    return sdf.parse(dateStr).toInstant().atZone(toTimeZon).toLocalDateTime();
}

/**
 * @param dateTime
 * @param fromZone
 * @param toZone
 * @return
 */
private static LocalDateTime convertTimeZoneUseZoneId(LocalDateTime dateTime, ZoneId fromZone, ZoneId toZone) {
    ZonedDateTime originZoneDateTime = .of(dateTime, fromZone);
    ZonedDateTime transZoned = originZoneDateTime.withZoneSameInstant(toZone);
    return transZoned.toLocalDateTime();
}

/**
 * @param dateTime
 * @param fromOffset
 * @param toOffset
 * @return
 */
public static LocalDateTime convertTimeZoneUseZoneOffset(LocalDateTime dateTime, ZoneOffset fromOffset, ZoneOffset toOffset) {
    long timeDifference = toOffset.getTotalSeconds() - fromOffset.getTotalSeconds();
    return dateTime.plusSeconds(timeDifference);
}

其實 convertTimeZoneUseZoneId跟 convertTimeZoneUseZoneOffset 背後是繼承關係,所以基本上是一樣的東西,有可能差別在於效能上,但這部分我就沒有花太多時間著墨了。

測試!

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;


public class DateTimeConvertWithTimeZoneUtil {
    final static DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
    static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    final static String GMT0_STR = "GMT+0";
    final static String GMT8_STR = "GMT+8";
    final static ZoneId gmt8Zone = ZoneId.of(GMT8_STR);
    final static ZoneId gmt0Zone = ZoneId.of(GMT0_STR);
    final static TimeZone timeZoneGMT0 = TimeZone.getTimeZone(GMT0_STR);
    final static TimeZone timeZoneGMT8 = TimeZone.getTimeZone(GMT8_STR);
    final static TimeZone timeZoneGMT_M8 = TimeZone.getTimeZone("GMT-8");

    public static void main(String[] args) throws ParseException {

//        String dateStr = "1970-01-01 00:00:00.000";
        String dateStr = "2023-01-05 8:00:00.000";
        String dateStrPattern = "yyyy-MM-dd HH:mm:ss";

        System.out.println("dateStr >> " + dateStr);
        System.out.println("GMT+8 dateStr to GMT+0 LocalDateTime >>> " + df.format(convertDateStrWithTimeZone(dateStr, dateStrPattern, timeZoneGMT8
                , gmt0Zone)));
        System.out.println("GMT+8 dateStr to GMT+8 LocalDateTime >>> " + df.format(convertDateStrWithTimeZone(dateStr, dateStrPattern,
        timeZoneGMT8, gmt8Zone)));
        System.out.println("GMT+0 dateStr to GMT+8 LocalDateTime >>> " + df.format(convertDateStrWithTimeZone(dateStr, dateStrPattern,
        timeZoneGMT0, gmt8Zone)));
        System.out.println("GMT+0 dateStr to GMT+0 LocalDateTime >>> " + df.format(convertDateStrWithTimeZone(dateStr, dateStrPattern,
        timeZoneGMT0, gmt0Zone)));

        System.out.println("=================================================");
        LocalDateTime GMT8LDT = convertDateStrWithTimeZone(dateStr, dateStrPattern, timeZoneGMT8, gmt8Zone);
        LocalDateTime GMT0LDT = convertDateStrWithTimeZone(dateStr, dateStrPattern, timeZoneGMT0, gmt0Zone);

        long start2 = System.currentTimeMillis();
        for (int i = 18; i >= -18; i--) {
            System.out.println("GMT+8 dateStr to GMT" + (i > 0 ? "+" + i : i) + " LocalDateTime >>> " +
                    df.format(convertTimeZoneUseZoneId(GMT8LDT, ZoneOffset.ofHours(8), ZoneOffset.ofHours(i))));
        }

        for (int i = 18; i >= -18; i--) {
            System.out.println("GMT+0 dateStr to GMT" + (i > 0 ? "+" + i : i) + " LocalDateTime >>> " +
                    df.format(convertTimeZoneUseZoneId(GMT0LDT, ZoneOffset.ofHours(0), ZoneOffset.ofHours(i))));
        }
        System.out.println("two end: >>>" + (System.currentTimeMillis() - start2));

        System.out.println("======== change ZoneId =======");

        long start1 = System.currentTimeMillis();
        for (int i = 18; i >= -18; i--) {
            System.out.println("GMT+8 dateStr to GMT" + (i > 0 ? "+" + i : i) + " LocalDateTime >>> " + df.format(convertTimeZoneUseZoneOffset(GMT8LDT,
                    ZoneOffset.ofHours(8), ZoneOffset.ofHours(i))));
        }

        System.out.println(" reverse ~~ ");

        for (int i = 18; i >= -18; i--) {
            System.out.println("GMT+0 dateStr to GMT" + (i > 0 ? "+" + i : i) + " LocalDateTime >>> " + df.format(convertTimeZoneUseZoneOffset(GMT0LDT,
                    ZoneOffset.ofHours(0), ZoneOffset.ofHours(i))));
        }

        System.out.println("one end: >>>" + (System.currentTimeMillis() - start1));

    }
}

結論

不斷的測試及驗證,最後發現Date 物件對於時區操作,並不是這麼靈活且適合,但我本身就不是那麼愛用Date,Java 8 過後新增的LocalDateTime 等等新增的時間類別,相較於Date 比較好用,但是呢~~~ 永遠都有但是工作上既有產品還是使用Date 所以你還是無法避免這狀況,只能將狀況變成最適合自己的操作方式,盡可能提高自己工作的效率。🔥

PreviousMyBatis-PlusNextJava IP to Long

Last updated 1 year ago