2023年12月26日 星期二

Java 日期操作筆記 - 使用 DateTimeFormatter, OffsetDateTime, LocalDateTime, TemporalAdjusters 等

紀錄下 Java 的各種日期時間操作

使用 JDK 11

package test;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;

public class DateTimeFormatterTest {

	public static void main(String[] args) {
		String timeStr = "2023-01-01T00:00:00+00:00";
		//使用 DateTimeFormatter
		DateTimeFormatter dtf_utc0 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx")
													  .withZone(ZoneId.of("+0000"));
		//上例可以簡單寫成 DateTimeFormatter dtf = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
		OffsetDateTime offsetDateTime = OffsetDateTime.parse(timeStr, dtf_utc0);
		//轉換成 Date
		Date date = Date.from(offsetDateTime.toInstant());
		//轉換成 Calendar
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        
        //將 Calendar 轉成 Date,再從 Date 轉成 OffsetDateTime
        //atOffset(Offset) 只是用來把 Date 轉成 OffsetDateTime,
        //Offset 設多少不重要,因為都可以再用 DateTimeFormatter.format() 將其 format 成其他時區的日期文字 
        OffsetDateTime offsetDateTime2 = calendar.getTime() // 將 Calendar 轉成 Date
        								 .toInstant()
        								 .atOffset(ZoneOffset.of("+0800"));
        
        //用不同時區和 format 印出日期,可以注意 xxx 和 xx 的不同,xxx會印出冒號而xx不會
        DateTimeFormatter dtf_utc3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxx")
        							 				  .withZone(ZoneId.of("+0300"));
        System.out.println(dtf_utc3.format(offsetDateTime2)); //2023-01-01T03:00:00+0300
        
        ////////////////////////// - LocalDate, LocalTime 等串接練習 - //////////////////
        //只有日期沒有時間的 DateTimeFormatter
  		DateTimeFormatter dtf_utc0_dateOnly = DateTimeFormatter.ofPattern("yyyy-MM-dd")
  													           .withZone(ZoneId.of("+0000"));
  		//含有有日期及時間的 DateTimeFormatter
  		DateTimeFormatter dtf_losAngeles_withTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
  				                                                     .withZone(ZoneId.of("America/Los_Angeles"));
  		//只有日期不含時間、時區的 LocalDate
  		LocalDate localDate = LocalDate.parse("2024-01-01", dtf_utc0_dateOnly);
  		System.out.println(localDate); //2024-01-01
  		//只有時間不含日期、時區的 LocalTime
  		LocalTime localTime = LocalTime.of(0, 0, 0);
  		System.out.println(localTime); //00:00
  		//LocalDateTime 含有日期和時間,但不含時區,可以用 LocalDate 加 LocalTime 組合而成
  		LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
  		System.out.println(localDateTime); //2024-01-01T00:00
  		//OffsetDateTime 含有日期、時間和時區,可以用 LocalDateTime 加 ZoneOffset 組合而成
  		OffsetDateTime offsetDateTime1 = OffsetDateTime.of(localDateTime, ZoneOffset.of("+0000"));
  		
  		System.out.println(dtf_losAngeles_withTime.format(offsetDateTime1)); //2023-12-31 16:00:00		
  		
  		//OffsetDateTime 也提供多種不同的建立 Method,
  		//例如其中的一個 OffsetDateTime.of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset)
  		//它可以用以1來表示1月 (而不是用0來表示1月),如下例:
  		OffsetDateTime offsetDateTime_uct0 = OffsetDateTime.of(2024, 6, 6, 20, 0, 0, 0, ZoneOffset.of("+0000"));
  		System.out.println(offsetDateTime_uct0); //2024-06-06T20:00Z
  		//得到當日是星期幾,可能會依不同時區而不同
  		System.out.println(offsetDateTime_uct0.getDayOfWeek()); //THURSDAY
  		//可以把日期、時間提出來換上不同時區建立新的 OffsetDateTime,此時其 Instant 代表的毫秒數可能會改變 
  		System.out.println(OffsetDateTime.of(offsetDateTime_uct0.toLocalDateTime(), ZoneOffset.of("+0800"))); //2024-06-06T20:00+08:00
  		//可以用 OffsetDateTime.withOffsetSameInstant(ZoneOffset)在Instant代表毫秒數不變的情況下修改時區
  		OffsetDateTime offsetDateTime_uct8 = offsetDateTime_uct0.withOffsetSameInstant(ZoneOffset.of("+0800"));
  		System.out.println(offsetDateTime_uct8); //2024-06-07T04:00+08:00
  		//可以看到因為時區不同而顯示不同的星期
  		System.out.println(offsetDateTime_uct8.getDayOfWeek()); //FRIDAY
  		
  		//////////////////////////- TemporalAdjusters 練習 - //////////////////
  		//配合 OffsetDateTime.with() 和 TemporalAdjusters 可以得到特定條件的修改時間,
  		//例如此例使用 TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY)
  		//可以得到往前算的最近一次星期五(包含今天)
  		OffsetDateTime lastFirdayIncludeToday_utc0 = offsetDateTime_uct0.with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY));
  		System.out.println(lastFirdayIncludeToday_utc0); //2024-05-31T20:00Z
  		
  		OffsetDateTime lastFirdayIncludeToday_utc8 = offsetDateTime_uct8.with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY));
  		System.out.println(lastFirdayIncludeToday_utc8); //2024-06-07T04:00+08:00
	}
}

參考資料:

  1. DateTimeFormatter (Java Platform SE 8 )