如何才能在Java中優雅的操縱時間?

阿里妹導讀
怎麼才能在Java中優雅的操縱時間呢,作者整理了相關的概念和工具類,希望幫助大家在程式碼開發的過程中對時間的使用更加優雅。
在開發時候,發現有很多需要用到時間的地方,例如記錄操作的時間、比較時間判斷產品是否有效等。總而言之,時間是我們業務開發必須關注、時刻注意的點。但目前工程的程式碼中使用了非常多時間的工具類,一會兒用Java.util.Date記錄時間,一會用Java.time.LocalDateTime記錄時間,怎麼才能在Java中優雅的操縱時間呢,我整理了相關的概念和工具類,希望幫助大家在程式碼開發的過程中對對時間的使用更加優雅。
這裡先寫一個結論:
  • 建議使用java8的時間API,在安全性和易用性上都遠高於java.util.Date。
  • 目前比較流行的封裝java API的時間工具類大都基於java.util.Date,建議在開發過程中根據業務需要基於java.time.*的方法封裝工具類(文末給出了一個簡單的實現)。
時間在計算機中的儲存和展示
時間以整數的方式進行儲存:時間在計算機中儲存的本質是一個整數,稱為Epoch Time(時間戳),計算從1970年1月1日零點(格林威治時間/GMT+00:00)到現在所經歷的秒數。
在java程式中,時間戳通常使用long表示毫秒數,透過System.currentTimeMillis()可以獲取時間戳。時間戳對我們人來說是不易理解的,因此需要將其轉換為易讀的時間,例如,2024-10-7 20:21:59(實際上說的是本地時間),而同一時刻不同時區的人看到的本地時間是不一樣,所以在時間展示的時候需要加上時區的資訊,才能精準的找到對應的時刻。
時區與世界時間標準相關:
世界時間的標準在1972年發生了變化,但我們在開發程式的時候可以忽略GMT和UTC的差異, 因為計算機的時鐘在聯網的時候會自動與時間伺服器同步時間。
本地時間等於我們所在(或者所使用)時區內的當地時間,它由與世界標準時間(UTC)之間的偏移量來定義。這個偏移量可以表示為 UTC- 或 UTC+,後面接上偏移的小時和分鐘數。 例如:GMT+08:00或者UTC+08:00表示東八區,2024-10-7 20:21:59 UTC+08:00便可以精準的定位一個時刻。
日期API
JDK以版本8為界,有兩套處理日期/時間的API。
簡單的比較如下:
java.util
在jdk8之前,Java使用java.util中的API對處理時間。
在獲取年月日的時候,Date和Calendar需要進行不同的轉換=>規則不統一。

Date

java.util.Date用於表示一個日期和時間的物件,實現很簡單(實際上儲存了一個long型別的以毫秒錶示的時間戳,在透過new Date()獲取當前時間的時候,實際上是透過System.currentTimeMillis()獲取時間戳進行賦值)。
publicclassDate {long fastTime;publicDate(long date) { fastTime = date; }publiclonggetTime() {return fastTime; }}
java.util.Date承載的功能有限,且在利用Date類獲取具體年/月/日的時候需要注意:getYear()返回的年份必須加上1900getMonth()返回的月份是0-11分別表示1-12月,所以要加1,而getDate()返回的日期範圍是1~31,又不能加1。

Calendar

Calendar可以用於獲取並設定年、月、日、時、分、秒,它和Date比,主要多了一個可以做簡單的日期和時間運算的功能,但程式碼粗糙,API不好用,效能也不好。
Calendar物件getTime()可以獲得Date物件:
import java.util.*;publicclassMain{publicstatic void main(String[] args) {// 獲取當前時間:Calendarc = Calendar.getInstance(); int y = c.get(Calendar.YEAR);//返回年份不用轉換 int m = 1 + c.get(Calendar.MONTH);//返回月份需要加1 int d = c.get(Calendar.DAY_OF_MONTH); int w = c.get(Calendar.DAY_OF_WEEK);//返回的 int hh = c.get(Calendar.HOUR_OF_DAY); int mm = c.get(Calendar.MINUTE); int ss = c.get(Calendar.SECOND); int ms = c.get(Calendar.MILLISECOND);System.out.println(y + "-" + m + "-" + d + " " + w + " " + hh + ":" + mm + ":" + ss + "." + ms); }}
import java.text.*;import java.util.*;publicclassMain{publicstatic void main(String[] args) {// 當前時間:Calendarc = Calendar.getInstance();// 清除所有:c.clear();// 設定年月日時分秒:c.set(2019, 10/* 11月 */, 20, 8, 15, 0);// 加5天並減去2小時:c.add(Calendar.DAY_OF_MONTH, 5);c.add(Calendar.HOUR_OF_DAY, -2);// 顯示時間:var sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date d = c.getTime();System.out.println(sdf.format(d));// 2019-11-25 6:15:00 }}

TimeZone

CalendarDate相比,它提供了時區轉換的功能。時區用TimeZone物件表示。
時區的唯一標識是以字串表示的ID,獲取指定TimeZone物件也是以這個ID為引數獲取,GMT+09:00Asia/Shanghai都是有效的時區ID。可以透過TimeZone.getAvailableIDs()獲取系統支援的所有ID。
import java.text.*;import java.util.*;publicclasslearnTime{publicstatic void main(String[] args) {// 當前時間:Calendarc = Calendar.getInstance();// 清除所有欄位:c.clear();// 設定為北京時區:c.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));// 設定年月日時分秒:c.set(2024, 9/* 10月 */, 10, 8, 15, 0);// 顯示時間:SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));System.out.println(sdf.format(c.getTime()));// 2024-10-09 20:15:00 }}

java.text.SimpleDateFormat

Date和SimpleDateFormat使用解析時間:
// SimpleDateFormat執行緒不安全,每次使用都要構造新的,在初始的時候定義解析的字串格式SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 將指定字串String解析為DateDate date = format.parse("2024-10-07 16:10:22");// 將Date格式化為StringString str = format.format(date);
由於SimpleDateFormat執行緒不安全,為了提升效能,會使用ThreadLocalCache,如下:
staticfinal ThreadLocal<SimpleDateFormat> SIMPLE_DATE_FORMAT_LOCAL = ThreadLocal.withInitial( () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
Java.time.*
開源社群開發了一個日期類Joda,API清晰,效能較好,提交了JSR-310,在java8中稱為JDK基礎類庫。
  • 本地日期和時間:LocalDateTime(日期和時間)LocalDate(日期)LocalTime(時間)
    • 因為沒有時區無法與時間戳轉換。
  • 帶時區的日期和時間:ZonedDateTime
  • 時刻:Instant
  • 時區:ZoneIdZoneOffset
  • 時間間隔:Duration
以及一套新的用於取代SimpleDateFormat的格式化型別DateTimeFormatter

LocalDate/LocalTime/LocalDateTime

  • 預設嚴格按照ISO 8601規定日期和時間格式進行列印(日期和時間的分隔符是T)。
    • 日期:yyyy-MM-dd; 時間HH:mm:ss
    • 日期和時間:yyyy-MM-dd'T'HH:mm:ss
  • 可以解析簡單格式獲取型別:
LocalDateTime localDayTime=LocalDateTime.of(2024, 10, 07, 8, 15, 0);LocalDate localDay=LocalDate.of(2024, 10, 07); LocalTime localTime=LocalTime.parse("08:15:07");
  • 有對日期和時間進行加減的非常簡單的鏈式呼叫,透過plusXxx()/minusXxx()對時間進行變換:
publicclasslearnTime {publicstaticvoidmain(String[] args) { LocalDateTime dt = LocalDateTime.of(2024, 10, 10, 20, 30, 59); System.out.println(dt);// 加5天減3小時:2024-10-10T20:30:59 LocalDateTime dt2 = dt.plusDays(5).minusHours(3); System.out.println(dt2); // 2024-10-15T17:30:59// 減1月: LocalDateTime dt3 = dt2.minusMonths(1); //2024-09-15T17:30:59 System.out.println(dt3); // 2019-09-30T17:30:59 }}
  • 對日期和時間進行調整使用withXxx(),例如將月份調整為:
    9月 dataLocalTime.withMonth(9)
  • 複雜的操作:獲取特殊時間
    • with和TemporalAdjusters配合使用找到特殊時間(當月的第一天)。
publicclassMain {publicstaticvoidmain(String[] args) { LocalDateTime now = LocalDateTime.now();// 獲取本月第一天0:00時刻: System.out.println("當月第一天0:00時刻"+now.withDayOfMonth(1).atStartOfDay());//獲取當月第一天 System.out.println("當月第一天:"+now.with(TemporalAdjusters.firstDayOfMonth()));//獲取下月第一天 System.out.println("下月第一天:"+now.with(TemporalAdjusters.firstDayOfNextMonth()));//獲取明年第一天 System.out.println("明年第一天:"+now.with(TemporalAdjusters.firstDayOfNextYear()));//獲取本年第一天 System.out.println("本年第一天:"+now.with(TemporalAdjusters.firstDayOfYear()));//獲取當月最後一天 System.out.println("當月最後一天:"+now.with(TemporalAdjusters.lastDayOfMonth()));//獲取本年最後一天 System.out.println("本年最後一天:"+now.with(TemporalAdjusters.lastDayOfYear()));//獲取當月第三週星期五 System.out.println("當月第三週星期五:"+now.with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.FRIDAY)));//獲取上週一 System.out.println("上週一:"+now.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)));//獲取下週日 System.out.println("下週日:"+now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY))); }}
  • 比較可以使用 isBefore()isAfter()

Duration和Period

  • Duration
    基於時間值(Instant/LocalDateTime),表示兩個時刻時間的時間間隔,適合處理較短的時間,需要更高的精確性。
    • 使用between()方法比較兩個瞬間的差;
    • 使用getSeconds()getNanosecends()方法獲取時間單元的值;
    • 獲得具體的粒度的間隔:ofDays(),ofHours(), ofMillis(), ofMinutes(), ofNanos(), ofSeconds()
    • 透過文字建立Duration物件,格式為 “PnDTnHnMn.nS”,
      Duration.parse("P1DT1H10M10.5S")
    • 使用toDays(), toHours(), toMillis(), toMinutes()方法把Duration物件可以轉成其他時間單元;
    • 透過 plusX()minusX()方法增加或減少Duration物件,其中X表示days, hours, millis, minutes, nanos 或 seconds。
  • Period基於日期值,表示一段時間的年、月、日:
    • 使用between()方法比較兩個日期的差;
    • 使用getYears(),getMonhs(),getDays()方法獲取具體粒度差距(返回的型別是int);
    • 透過文字建立Period物件,格式為 “PnYnMnD”:Period.parse("P2Y3M5D")
    • 可以透過plusX()minusX()方法進行增加或減少,其中X表示日期單元;

ZonedDateTime

ZonedDateTimeLocalDateTimeZoneId
  • ZonedDateTime 帶時區時間的常見方法:
    • now():獲取當前時區的ZonedDateTime物件
    • now(ZoneId zone):獲取指定時區的ZonedDateTime物件
    • getYear, getMonthValue, getDayOfMonth等:
      獲取年月日、時分秒、納秒等
    • withXxx(時間):修改時間系列的方法
    • minusXxx(時間):減少時間系列的方法
    • plusXxx(時間):增加時間系列的方法
  • 時區轉換
import java.time.*;publicclassMain {publicstaticvoidmain(String[] args) {// 以中國時區獲取當前時間: ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));// 轉換為紐約時間: ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York")); System.out.println(zbj); System.out.println(zny); }}

ZoneId時區類

時區類,功能和java.util.TimeZone類似。
ZoneId支援兩種型別格式初始化,一種是時區偏移的格式(基於UTC/Greenwich時),一種是地域時區的格式(eg:Europe/Paris)。ZoneId是抽象類,具體的邏輯實現由來子類完成,ZoneOffset處理時區偏移型別的格式,ZoneRegion處理基於地域時區的格式:
  • getAvailableZoneIds(): 獲取Java中支援的所有時區
  • systemDefault(): 獲取系統預設時區
  • of(String zoneId): 獲取一個指定時區

Instant

時間線上的某個時刻/時間戳
透過獲取Instant的物件可以拿到此刻的時間,該時間由兩部分組成:從1970-01-01 00:00:00 開始走到此刻的總秒數+不夠1秒的納秒數。
  • 作用:可以用來記錄程式碼的執行時間,或用於記錄使用者操作某個事件的時間點。
  • 傳統的Date類,只能精確到毫秒,並且是可變物件。
  • 新增的Instant類,可以精確到納秒,並且是不可變物件,推薦用Instant代替Date。
//1、建立Instant的物件,獲取此刻時間資訊Instant now = Instant.now(); //不可變物件//2、獲取總秒數long second = now.getEpochSecond();system.out.println(second) ;//3、不夠1秒的納秒數int nano = now.getNano();system.out.println(nano) ;system.out.println(now);//可以進行加減法 Instant instant = now.plusNanos(111);//將納秒加111// Instant物件的作用:做程式碼的效能分析,或者記錄使用者的操作時間點Instant now1 = Instant.now();//程式碼執行...Instant now2 = Instant.now();//用這兩個時間點相減就可以知道這段程式碼運行了多少時間

DataTimeFormatter

使用方式,傳入格式化字串,可以指定local。
import java.time.*;import java.time.format.*;import java.util.Locale;publicclassMain{publicstatic void main(String[] args) {ZonedDateTime zdt = ZonedDateTime.now();DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ");System.out.println(formatter.format(zdt));DateTimeFormatter zhFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);System.out.println(zhFormatter.format(zdt));DateTimeFormatter usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);System.out.println(usFormatter.format(zdt));//2024-10-08T00:25 GMT+08:00//2024 十月 08 星期二 00:25//Tue, October/08/2024 00:25 }}
轉換
新老API轉換參考:https://blog.csdn.net/qq_31635851/article/details/120150588
LocalTimeTime和Date的相互轉換:
LocalDateTime不包括時區,而——
<font style="background-color:rgb(249, 242, 244);">Date</font>代表一個具體的時間瞬間,精度為毫秒。
為了從<font style="background-color:rgb(249, 242, 244);">LocalDateTime</font>轉換到——
<font style="background-color:rgb(249, 242, 244);">Date</font>,需要提供時區。
// LocalDateTime 轉換為 DateLocalDateTime localDateTime = LocalDateTime.now();ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());Date date = Date.from(zonedDateTime.toInstant());// Date 轉換為 LocalDateTimeDate date = newDate();Instant instant = date.toInstant();LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();

資料庫對映變化

  • java.util.Date和資料庫對映:

    <arg column="gmt_create" jdbcType="TIMESTAMP" javaType="java.util.Date"/>

  • java.time.*和資料庫對映:

    <arg column="gmt_create" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/>

    • mybatis 3.5.0以後已經支援,有LocalDateTimeTypeHandler等型別處理器支援,不需要額外操作。
    • 比較老的mybatis版本可能會報錯,需要新增相關的依賴。
    <dependency><groupId>org.mybatis</groupId><artifactId>mybatis-typehandlers-jsr310</artifactId><version>1.0.2</version></dependency>

Mybatis中和時間相關的 jdbcType和javaType、typeHandler的對照關係

操作時間相關的工具
有一些對基礎的API進行了封裝便於我們在開發中有效的處理時間。
  • 螞蟻時間工具類:
    com.iwallet.biz.common.util.DateUtil
    • 基於Java.Util.Date,提供了廣泛的日期/時間處理方法,可滿足絕大部分需求。
  • org.apache.commons.lang3.time
    • 包括多種基於Java.util.Date封裝的工具類,提供了很多方便操作日期和時間的演算法。
目前暫時沒有發現基於Java.time*封裝的公共的時間工具類。
在很多情況下,因為已有的工具類不能滿足當下的業務需求,工程內部需要自己實現類似DateUtil的工具類,建議基於java.time*實現相關的工具類。
import java.time.*;import java.time.format.DateTimeFormatter;import java.time.temporal.ChronoUnit;publicclassDateUtils{// 獲取當前日期publicstatic LocalDate getCurrentDate(){return LocalDate.now(); }// 獲取當前時間publicstatic LocalTime getCurrentTime(){return LocalTime.now(); }// 獲取當前日期時間publicstatic LocalDateTime getCurrentDateTime(){return LocalDateTime.now(); }// 格式化日期為字串publicstatic String formatLocalDate(LocalDate date, String pattern){ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);return date.format(formatter); }// 解析字串為LocalDatepublicstatic LocalDate parseLocalDate(String dateStr, String pattern){ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);return LocalDate.parse(dateStr, formatter); }// 增加指定天數publicstatic LocalDate addDays(LocalDate date, long days){return date.plusDays(days); }// 減少指定天數publicstatic LocalDate minusDays(LocalDate date, long days){return date.minusDays(days); }// 計算兩個日期之間的天數差publicstaticlonggetDaysBetween(LocalDate startDate, LocalDate endDate){return ChronoUnit.DAYS.between(startDate, endDate); }// 獲取指定日期所在月份的第一天publicstatic LocalDate getFirstDayOfMonth(LocalDate date){return date.withDayOfMonth(1); }// 獲取指定日期所在月份的最後一天publicstatic LocalDate getLastDayOfMonth(LocalDate date){return date.withDayOfMonth(date.lengthOfMonth()); }// 判斷兩個日期是否相等publicstaticbooleanisSameDate(LocalDate date1, LocalDate date2){return date1.isEqual(date2); }// 判斷日期是否在指定範圍內publicstaticbooleanisDateInRange(LocalDate date, LocalDate startDate, LocalDate endDate){return date.isAfter(startDate) && date.isBefore(endDate); }// 獲取指定日期的星期幾publicstatic DayOfWeek getDayOfWeek(LocalDate date){return date.getDayOfWeek(); }// 判斷是否為閏年publicstaticbooleanisLeapYear(int year){return Year.of(year).isLeap(); }// 獲取指定月份的天數publicstaticintgetDaysInMonth(int year, int month){return YearMonth.of(year, month).lengthOfMonth(); }// 獲取指定日期的年份publicstaticintgetYear(LocalDate date){return date.getYear(); }// 獲取指定日期的月份publicstaticintgetMonth(LocalDate date){return date.getMonthValue(); }// 獲取指定日期的天數publicstaticintgetDayOfMonth(LocalDate date){return date.getDayOfMonth(); }// 獲取指定日期的小時數publicstaticintgetHour(LocalDateTime dateTime){return dateTime.getHour(); }// 獲取指定日期的分鐘數publicstaticintgetMinute(LocalDateTime dateTime){return dateTime.getMinute(); }// 獲取指定日期的秒數publicstaticintgetSecond(LocalDateTime dateTime){return dateTime.getSecond(); }// 判斷指定日期是否在當前日期之前publicstaticbooleanisBefore(LocalDate date){return date.isBefore(LocalDate.now()); }// 判斷指定日期是否在當前日期之後publicstaticbooleanisAfter(LocalDate date){return date.isAfter(LocalDate.now()); }// 判斷指定日期是否在當前日期之前或相等publicstaticbooleanisBeforeOrEqual(LocalDate date){return date.isBefore(LocalDate.now()) || date.isEqual(LocalDate.now()); }// 判斷指定日期是否在當前日期之後或相等publicstaticbooleanisAfterOrEqual(LocalDate date){return date.isAfter(LocalDate.now()) || date.isEqual(LocalDate.now()); }// 獲取指定日期的年齡publicstaticintgetAge(LocalDate birthDate){ LocalDate currentDate = LocalDate.now();return Period.between(birthDate, currentDate).getYears(); }// 獲取指定日期的季度publicstaticintgetQuarter(LocalDate date){return (date.getMonthValue() - 1) / 3 + 1; }// 獲取指定日期的下一個工作日publicstatic LocalDate getNextWorkingDay(LocalDate date){do { date = date.plusDays(1); } while (date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY);return date; }// 獲取指定日期的上一個工作日publicstatic LocalDate getPreviousWorkingDay(LocalDate date){do { date = date.minusDays(1); } while (date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY);return date; }// 獲取指定日期所在周的第一天(週一)publicstatic LocalDate getFirstDayOfWeek(LocalDate date){return date.with(DayOfWeek.MONDAY); }// 獲取指定日期所在周的最後一天(週日)publicstatic LocalDate getLastDayOfWeek(LocalDate date){return date.with(DayOfWeek.SUNDAY); }// 獲取指定日期所在年的第一天publicstatic LocalDate getFirstDayOfYear(LocalDate date){return date.withDayOfYear(1); }// 獲取指定日期所在年的最後一天publicstatic LocalDate getLastDayOfYear(LocalDate date){return date.withDayOfYear(date.lengthOfYear()); }// 獲取指定日期所在季度的第一天publicstatic LocalDate getFirstDayOfQuarter(LocalDate date){int month = (date.getMonthValue() - 1) / 3 * 3 + 1;return LocalDate.of(date.getYear(), month, 1); }// 獲取指定日期所在季度的最後一天publicstatic LocalDate getLastDayOfQuarter(LocalDate date){int month = (date.getMonthValue() - 1) / 3 * 3 + 3;return LocalDate.of(date.getYear(), month, Month.of(month).maxLength()); }// 判斷指定日期是否為工作日(週一至週五)publicstaticbooleanisWeekday(LocalDate date){return date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY; }// 判斷指定日期是否為週末(週六或週日)publicstaticbooleanisWeekend(LocalDate date){return date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY; }// 獲取指定日期所在月份的工作日天數publicstaticintgetWeekdayCountOfMonth(LocalDate date){int weekdayCount = 0; LocalDate firstDayOfMonth = getFirstDayOfMonth(date); LocalDate lastDayOfMonth = getLastDayOfMonth(date);while (!firstDayOfMonth.isAfter(lastDayOfMonth)) {if (isWeekday(firstDayOfMonth)) { weekdayCount++; } firstDayOfMonth = firstDayOfMonth.plusDays(1); }return weekdayCount; }// 獲取指定日期所在月份的週末天數publicstaticintgetWeekendCountOfMonth(LocalDate date){int weekendCount = 0; LocalDate firstDayOfMonth = getFirstDayOfMonth(date); LocalDate lastDayOfMonth = getLastDayOfMonth(date);while (!firstDayOfMonth.isAfter(lastDayOfMonth)) {if (isWeekend(firstDayOfMonth)) { weekendCount++; } firstDayOfMonth = firstDayOfMonth.plusDays(1); }return weekendCount; }// 獲取指定日期所在年份的工作日天數publicstaticintgetWeekdayCountOfYear(LocalDate date){int weekdayCount = 0; LocalDate firstDayOfYear = getFirstDayOfYear(date); LocalDate lastDayOfYear = getLastDayOfYear(date);while (!firstDayOfYear.isAfter(lastDayOfYear)) {if (isWeekday(firstDayOfYear)) { weekdayCount++; } firstDayOfYear = firstDayOfYear.plusDays(1); }return weekdayCount; }}

相關文章