如何才能在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(实际上说的是本地时间),而同一时刻不同时区的人看到的本地时间是不一样,所以在时间展示的时候需要加上时区的信息,才能精准的找到对应的时刻。


时区与世界时间标准相关:


image.png


世界时间的标准在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。

image.png

简单的比较如下:

image.png

image.png


java.util


在jdk8之前,Java使用java.util中的API对处理时间。


在获取年月日的时候,Date和Calendar需要进行不同的转换=>规则不统一。

Date

java.util.Date用于表示一个日期和时间的对象,实现很简单(实际上存储了一个long类型的以毫秒表示的时间戳,在通过new Date()获取当前时间的时候,实际上是通过System.currentTimeMillis()获取时间戳进行赋值)。


public class Date {
    long fastTime;

    public Date(long date) {
        fastTime = date;
    }

    public long getTime() {
        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.*;

public class Main {
    public static void main(String[] args) {
        // 获取当前时间:
        Calendar c = 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.*;

public class Main {
    public static void main(String[] args) {
        // 当前时间:
        Calendar c = 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.*;

public class learnTime {
    public static void main(String[] args) {
        // 当前时间:
        Calendar c = 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解析为Date
Date date = format.parse("2024-10-07 16:10:22");

// 将Date格式化为String
String str = format.format(date);

由于SimpleDateFormat线程不安全,为了提升性能,会使用ThreadLocalCache,如下:


static final 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()对时间进行变换:
public class learnTime {
    public static void main(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配合使用找到特殊时间(当月的第一天)。


public class Main {
    public static void main(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.*;

public class Main {
    public static void main(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): 获取一个指定时区


image.png

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;

public class Main {
    public static 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 转换为 Date
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
Date date = Date.from(zonedDateTime.toInstant());
// Date 转换为 LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();

数据库映射变化

  • java.util.Date和数据库映射:
  • java.time.*和数据库映射:
  • 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的对照关系

image.png

操作时间相关的工具


有一些对基础的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;

public class DateUtils {

    // 获取当前日期
    public static LocalDate getCurrentDate() {
        return LocalDate.now();
    }

    // 获取当前时间
    public static LocalTime getCurrentTime() {
        return LocalTime.now();
    }

    // 获取当前日期时间
    public static LocalDateTime getCurrentDateTime() {
        return LocalDateTime.now();
    }

    // 格式化日期为字符串
    public static String formatLocalDate(LocalDate date, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return date.format(formatter);
    }

    // 解析字符串为LocalDate
    public static LocalDate parseLocalDate(String dateStr, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDate.parse(dateStr, formatter);
    }

    // 增加指定天数
    public static LocalDate addDays(LocalDate date, long days) {
        return date.plusDays(days);
    }

    // 减少指定天数
    public static LocalDate minusDays(LocalDate date, long days) {
        return date.minusDays(days);
    }

    // 计算两个日期之间的天数差
    public static long getDaysBetween(LocalDate startDate, LocalDate endDate) {
        return ChronoUnit.DAYS.between(startDate, endDate);
    }

    // 获取指定日期所在月份的第一天
    public static LocalDate getFirstDayOfMonth(LocalDate date) {
        return date.withDayOfMonth(1);
    }

    // 获取指定日期所在月份的最后一天
    public static LocalDate getLastDayOfMonth(LocalDate date) {
        return date.withDayOfMonth(date.lengthOfMonth());
    }

    // 判断两个日期是否相等
    public static boolean isSameDate(LocalDate date1, LocalDate date2) {
        return date1.isEqual(date2);
    }

    // 判断日期是否在指定范围内
    public static boolean isDateInRange(LocalDate date, LocalDate startDate, LocalDate endDate) {
        return date.isAfter(startDate) && date.isBefore(endDate);
    }

    // 获取指定日期的星期几
    public static DayOfWeek getDayOfWeek(LocalDate date) {
        return date.getDayOfWeek();
    }

    // 判断是否为闰年
    public static boolean isLeapYear(int year) {
        return Year.of(year).isLeap();
    }

    // 获取指定月份的天数
    public static int getDaysInMonth(int year, int month) {
        return YearMonth.of(year, month).lengthOfMonth();
    }

    // 获取指定日期的年份
    public static int getYear(LocalDate date) {
        return date.getYear();
    }

    // 获取指定日期的月份
    public static int getMonth(LocalDate date) {
        return date.getMonthValue();
    }

    // 获取指定日期的天数
    public static int getDayOfMonth(LocalDate date) {
        return date.getDayOfMonth();
    }

    // 获取指定日期的小时数
    public static int getHour(LocalDateTime dateTime) {
        return dateTime.getHour();
    }

    // 获取指定日期的分钟数
    public static int getMinute(LocalDateTime dateTime) {
        return dateTime.getMinute();
    }

    // 获取指定日期的秒数
    public static int getSecond(LocalDateTime dateTime) {
        return dateTime.getSecond();
    }

    // 判断指定日期是否在当前日期之前
    public static boolean isBefore(LocalDate date) {
        return date.isBefore(LocalDate.now());
    }

    // 判断指定日期是否在当前日期之后
    public static boolean isAfter(LocalDate date) {
        return date.isAfter(LocalDate.now());
    }

    // 判断指定日期是否在当前日期之前或相等
    public static boolean isBeforeOrEqual(LocalDate date) {
        return date.isBefore(LocalDate.now()) || date.isEqual(LocalDate.now());
    }

    // 判断指定日期是否在当前日期之后或相等
    public static boolean isAfterOrEqual(LocalDate date) {
        return date.isAfter(LocalDate.now()) || date.isEqual(LocalDate.now());
    }

    // 获取指定日期的年龄
    public static int getAge(LocalDate birthDate) {
        LocalDate currentDate = LocalDate.now();
        return Period.between(birthDate, currentDate).getYears();
    }

    // 获取指定日期的季度
    public static int getQuarter(LocalDate date) {
        return (date.getMonthValue() - 1) / 3 + 1;
    }

    // 获取指定日期的下一个工作日
    public static LocalDate getNextWorkingDay(LocalDate date) {
        do {
            date = date.plusDays(1);
        } while (date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY);
        return date;
    }

    // 获取指定日期的上一个工作日
    public static LocalDate getPreviousWorkingDay(LocalDate date) {
        do {
            date = date.minusDays(1);
        } while (date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY);
        return date;
    }

    // 获取指定日期所在周的第一天(周一)
    public static LocalDate getFirstDayOfWeek(LocalDate date) {
        return date.with(DayOfWeek.MONDAY);
    }

    // 获取指定日期所在周的最后一天(周日)
    public static LocalDate getLastDayOfWeek(LocalDate date) {
        return date.with(DayOfWeek.SUNDAY);
    }

    // 获取指定日期所在年的第一天
    public static LocalDate getFirstDayOfYear(LocalDate date) {
        return date.withDayOfYear(1);
    }

    // 获取指定日期所在年的最后一天
    public static LocalDate getLastDayOfYear(LocalDate date) {
        return date.withDayOfYear(date.lengthOfYear());
    }

    // 获取指定日期所在季度的第一天
    public static LocalDate getFirstDayOfQuarter(LocalDate date) {
        int month = (date.getMonthValue() - 1) / 3 * 3 + 1;
        return LocalDate.of(date.getYear(), month, 1);
    }

    // 获取指定日期所在季度的最后一天
    public static LocalDate getLastDayOfQuarter(LocalDate date) {
        int month = (date.getMonthValue() - 1) / 3 * 3 + 3;
        return LocalDate.of(date.getYear(), month, Month.of(month).maxLength());
    }

    // 判断指定日期是否为工作日(周一至周五)
    public static boolean isWeekday(LocalDate date) {
        return date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY;
    }

    // 判断指定日期是否为周末(周六或周日)
    public static boolean isWeekend(LocalDate date) {
        return date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY;
    }

    // 获取指定日期所在月份的工作日天数
    public static int getWeekdayCountOfMonth(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;
    }

    // 获取指定日期所在月份的周末天数
    public static int getWeekendCountOfMonth(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;
    }

    // 获取指定日期所在年份的工作日天数
    public static int getWeekdayCountOfYear(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;
    }

}






来源  |  阿里云开发者公众号

作者  |  青栎

相关文章
|
4天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
8天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
4天前
|
人工智能 运维 双11
2024阿里云双十一云资源购买指南(纯客观,无广)
2024年双十一,阿里云推出多项重磅优惠,特别针对新迁入云的企业和初创公司提供丰厚补贴。其中,36元一年的轻量应用服务器、1.95元/小时的16核60GB A10卡以及1元购域名等产品尤为值得关注。这些产品不仅价格亲民,还提供了丰富的功能和服务,非常适合个人开发者、学生及中小企业快速上手和部署应用。
|
13天前
|
人工智能 弹性计算 文字识别
基于阿里云文档智能和RAG快速构建企业"第二大脑"
在数字化转型的背景下,企业面临海量文档管理的挑战。传统的文档管理方式效率低下,难以满足业务需求。阿里云推出的文档智能(Document Mind)与检索增强生成(RAG)技术,通过自动化解析和智能检索,极大地提升了文档管理的效率和信息利用的价值。本文介绍了如何利用阿里云的解决方案,快速构建企业专属的“第二大脑”,助力企业在竞争中占据优势。
|
15天前
|
自然语言处理 数据可视化 前端开发
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
合合信息的智能文档处理“百宝箱”涵盖文档解析、向量化模型、测评工具等,解决了复杂文档解析、大模型问答幻觉、文档解析效果评估、知识库搭建、多语言文档翻译等问题。通过可视化解析工具 TextIn ParseX、向量化模型 acge-embedding 和文档解析测评工具 markdown_tester,百宝箱提升了文档处理的效率和精确度,适用于多种文档格式和语言环境,助力企业实现高效的信息管理和业务支持。
3936 2
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
|
4天前
|
算法 安全 网络安全
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
2024阿里云11.11金秋云创季活动火热进行中,活动月期间(2024年11月01日至11月30日)通过折扣、叠加优惠券等多种方式,阿里云WoSign SSL证书实现优惠价格新低,DV SSL证书220元/年起,助力中小企业轻松实现HTTPS加密,保障数据传输安全。
502 3
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
|
11天前
|
安全 数据建模 网络安全
2024阿里云双11,WoSign SSL证书优惠券使用攻略
2024阿里云“11.11金秋云创季”活动主会场,阿里云用户通过完成个人或企业实名认证,可以领取不同额度的满减优惠券,叠加折扣优惠。用户购买WoSign SSL证书,如何叠加才能更加优惠呢?
985 3
|
8天前
|
机器学习/深度学习 存储 人工智能
白话文讲解大模型| Attention is all you need
本文档旨在详细阐述当前主流的大模型技术架构如Transformer架构。我们将从技术概述、架构介绍到具体模型实现等多个角度进行讲解。通过本文档,我们期望为读者提供一个全面的理解,帮助大家掌握大模型的工作原理,增强与客户沟通的技术基础。本文档适合对大模型感兴趣的人员阅读。
412 17
白话文讲解大模型| Attention is all you need
|
8天前
|
算法 数据建模 网络安全
阿里云SSL证书2024双11优惠,WoSign DV证书220元/年起
2024阿里云11.11金秋云创季火热进行中,活动月期间(2024年11月01日至11月30日),阿里云SSL证书限时优惠,部分证书产品新老同享75折起;通过优惠折扣、叠加满减优惠券等多种方式,阿里云WoSign SSL证书将实现优惠价格新低,DV SSL证书220元/年起。
560 5
|
4天前
|
安全 网络安全
您有一份网络安全攻略待领取!!!
深入了解如何保护自己的云上资产,领取超酷的安全海报和定制鼠标垫,随时随地提醒你保持警惕!
697 1
您有一份网络安全攻略待领取!!!