Minio 对象存储使用简单入门

官方文档:MinIO Quickstart Guide| Minio中文文档

1、简介

        Minio 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
Minio是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。

2、容器安装

        将独立的 MinIO 服务器作为容器运行,独立的 MinIO 服务器最适合早期开发和评估。 某些功能,例如版本控制、对象锁定和存储桶复制 需要使用擦除编码分布式部署 MinIO。 

2.1、Docker

docker run -p 9000:9000 \
  --name minio1 \
  -v /mnt/data:/data \
  -e "MINIO_ROOT_USER=AKIAIOSFODNN7EXAMPLE" \
  -e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
  minio/minio server /data

# 命令详解
# -e MINIO_ROOT_USER 指定用户名
# -e MINIO_ROOT_PASSWORD 指定密码
# -v 挂载目录,持久化minio目录

2.2、Linux

# 下载
wget https://dl.min.io/server/minio/release/linux-amd64/minio
# 加权
chmod +x minio
# 启动 你可以更换后面的目录为你自己需要的目录
./minio server /data

2.3、windows(详细)

(1)下载:

http://dl.minio.org.cn/server/minio/release/windows-amd64/minio.exe

(2)启动:

minio.exe server D:\

        将“D:\”替换为您希望 MinIO 存储数据的驱动器或目录的路径。 您必须将终端或 powershell 目录更改为 minio.exe 可执行文件的位置,将该目录的路径添加到系统 $PATH 中。

        注:也就是需要先创建好存储文件的目录,如我的启动命令为:minio.exe server D:\data,保存为bat文件,双击启动即可。

         上图就启动成功了,警告先不管,访问http://192.168.0.123:9000即可。之前创建的文件也有初始化内容了,还没有文件。

3、使用说明

(1)访问地址:

http://{ip}:{port}/minio/ 或 http://{ip}:{port}

        默认的 root 凭据 minioadmin:minioadmin。

 

        这样就可以使用浏览器来创建桶、上传对象以及浏览 MinIO 服务器的内容了。

(2)创建buckets

 (3)上传文件

去看一下之前的目录,文件也有了

4、JAVA使用minio的方法

        上面的是直接才界面控制台进行创建桶、上传文件、删除文件等操作,下面来使用java代码操作,写个简单demo,演示连接到一个对象存储服务,创建一个存储桶并上传一个文件到该桶中。

(1)添加依赖

<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>7.1.0</version>
 </dependency>

(2)参数配置

        需要有存储服务的三个参数才能连接到该服务

参数说明
Endpoint对象存储服务的URL
Access KeyAccess key就像用户ID,可以唯一标识你的账户。
Secret KeySecret key是你账户的密码。

我这里配置在yaml文件中的:

 (3)编写minio配置信息

package com.xxx.xxx.common.config;

import com.xxx.xxx.common.exception.MinioException;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.SetBucketPolicyArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 * minio自动配置.
 *
 * @author Mei rx
 * @since 2021/08/05
 */
@Configuration
@ConfigurationProperties(prefix = "minio")
@ConditionalOnClass({MinioClient.class})
@ConditionalOnProperty({"minio.endpoint"})
public class MinioAutoConfiguration {

    @Autowired
    private Environment environment;

    public MinioAutoConfiguration() {
    }

    @Bean
    public MinioClient minioClient() {
        String endpoint = this.environment.getProperty("minio.endpoint");
        String accessKey = this.environment.getProperty("minio.accessKey");
        String secretKey = this.environment.getProperty("minio.secretKey");
        String bucketName = this.environment.getProperty("minio.bucketName");
        if (endpoint != null && !"".equals(endpoint)) {
            if (accessKey != null && !"".equals(accessKey)) {
                if (secretKey != null && !"".equals(secretKey)) {
                    if (bucketName != null && !"".equals(bucketName)) {
                        MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
                        this.makeBucket(minioClient, bucketName);
                        return minioClient;
                    } else {
                        throw new MinioException("存储桶名称未在application.yml配置!");
                    }
                } else {
                    throw new MinioException("Minio密码未在application.yml配置!");
                }
            } else {
                throw new MinioException("Minio用户名未在application.yml配置!");
            }
        } else {
            throw new MinioException("Minio的URL未在application.yml配置!");
        }
    }

    private void makeBucket(MinioClient minioClient, String bucketName) {
        try {
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!isExist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                String policyJson = "Bucket already exists.";
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build());
            }
        } catch (Exception e) {
            throw new MinioException("创建minio存储桶异常", e);
        }
    }
}

(4)编写上传代码

public static String uploadFile(InputStream inputStream, String objectName, String fileName) {
        if (StringUtil.isNotBlank(fileName)) {
            objectName = objectName + "/" + fileName;
        }
        try {
            if (objectName != null && !"".equals(objectName)) {
                try {
                    minioUtil.minioClient.putObject(PutObjectArgs
                            .builder()
                            .bucket(minioUtil.bucketName)
                            .object(objectName)
                            .stream(inputStream, inputStream.available(), -1)
                            .build());
                    log.info("文件上传成功!");
                } catch (Exception var4) {
                    log.error("添加存储对象异常", var4);
                    throw new MinioException("添加存储对象异常", var4);
                }
            } else {
                throw new MinioException("存储对象名称objectName不能为空!");
            }
            log.info("文件上传成功!");
            return minioUtil.getUrl(objectName);
        } catch (Exception var4) {
            var4.printStackTrace();
            log.error("上传发生错误: {}!", var4.getMessage());
            return var4.getMessage();
        }
    }

(5)调用上传方法

    /**
     * 上传材料
     */
    @PostMapping("/upload")
    @ApiOperation("上传文件")
    public String upload(
            @ApiParam(value = "文件", example = "11.jpg", required = true)
            @RequestPart(value = "file") MultipartFile file) throws IOException {
        log.info("uploadFile:上传文件[file:{}]", file);
        return MinioUtil.uploadFile(file.getInputStream(), "my-file", file.getOriginalFilename());
    }

(6)测试

 调用接口上传文件成功,返回一个可访问的url,访问一下

注意:如果出现如下提示,很有可能是buckets的权限问题,修改一下权限即可

 

4、全部代码

配置类:

package com.guodi.zjjggl.common.config;

import com.guodi.zjjggl.common.exception.MinioException;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.SetBucketPolicyArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 * minio自动配置.
 *
 * @author 刘雨昕
 * @since 2021/09/24
 */
@Configuration
@ConfigurationProperties(prefix = "minio")
@ConditionalOnClass({MinioClient.class})
@ConditionalOnProperty({"minio.endpoint"})
public class MinioAutoConfiguration {

    @Autowired
    private Environment environment;

    public MinioAutoConfiguration() {
    }

    @Bean
    public MinioClient minioClient() {
        String endpoint = this.environment.getProperty("minio.endpoint");
        String accessKey = this.environment.getProperty("minio.accessKey");
        String secretKey = this.environment.getProperty("minio.secretKey");
        String bucketName = this.environment.getProperty("minio.bucketName");
        if (endpoint != null && !"".equals(endpoint)) {
            if (accessKey != null && !"".equals(accessKey)) {
                if (secretKey != null && !"".equals(secretKey)) {
                    if (bucketName != null && !"".equals(bucketName)) {
                        MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
                        this.makeBucket(minioClient, bucketName);
                        return minioClient;
                    } else {
                        throw new MinioException("存储桶名称未在application.yml配置!");
                    }
                } else {
                    throw new MinioException("Minio密码未在application.yml配置!");
                }
            } else {
                throw new MinioException("Minio用户名未在application.yml配置!");
            }
        } else {
            throw new MinioException("Minio的URL未在application.yml配置!");
        }
    }

    private void makeBucket(MinioClient minioClient, String bucketName) {
        try {
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!isExist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                String policyJson = "Bucket already exists.";
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build());
            }
        } catch (Exception e) {
            throw new MinioException("创建minio存储桶异常", e);
        }
    }
}

工具类:

package com.xxx.xxx.util;

import com.xxx.core.minio.exception.MinioException;
import com.xxx.core.tool.utils.Charsets;
import com.xxx.core.tool.utils.DateUtil;
import com.xxx.core.tool.utils.StringUtil;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * minio存储对象工具类.
 *
 * @author 刘雨昕
 * @since 2021/09/24
 */
@Component
@Slf4j
public class MinioUtil {
    @Value("${minio.bucketName}")
    private String bucketName;
    @Resource
    private MinioClient minioClient;
    private static MinioUtil minioUtil;

    public MinioUtil() {
    }

    @PostConstruct
    public void init() {
        minioUtil = this;
    }

    public static String uploadFile(InputStream inputStream, String objectName, String fileName) {
        if (StringUtil.isNotBlank(fileName)) {
            objectName = objectName + "/" + fileName;
        }
        try {
            if (objectName != null && !"".equals(objectName)) {
                try {
                    minioUtil.minioClient.putObject(PutObjectArgs
                            .builder()
                            .bucket(minioUtil.bucketName)
                            .object(objectName)
                            .stream(inputStream, inputStream.available(), -1)
                            .build());
                    log.info("文件上传成功!");
                } catch (Exception e) {
                    log.error("添加存储对象异常", e);
                    throw new MinioException("添加存储对象异常", e);
                }
            } else {
                throw new MinioException("存储对象名称objectName不能为空!");
            }
            log.info("文件上传成功!");
            return minioUtil.getUrl(objectName);
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error("上传发生错误: {}!", ex.getMessage());
            return ex.getMessage();
        }
    }

    public static InputStream download(String fileUrl) {
        try {
            fileUrl = fileUrl.substring(fileUrl.indexOf(minioUtil.bucketName) + minioUtil.bucketName.length());
            InputStream inputStream = minioUtil.get(fileUrl);
            log.info("下载成功");
            return inputStream;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("下载发生错误: {}!", e.getMessage());
            return null;
        }
    }

    public static void batchDownload(List<String> fileUrlList, String zipName, HttpServletResponse httpServletResponse) {
        ZipOutputStream zos;
        ZipEntry zipEntry;
        byte[] buff = new byte[1024];
        if (fileUrlList != null && !fileUrlList.isEmpty()) {
            try {
                if (StringUtil.isEmpty(zipName)) {
                    zipName = "批量下载" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
                }
                //清除缓冲区中存在的所有数据以及状态代码和标头。如果已提交响应,则此方法将抛出IllegalStateException
                httpServletResponse.reset();
                //Content-Disposition为属性名,attachment以附件方式下载,filename下载文件默认名字
                httpServletResponse.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(zipName, Charsets.UTF_8.name()) + ".zip");
                //另存为弹框加载
                httpServletResponse.setContentType("application/x-msdownload");
                httpServletResponse.setCharacterEncoding("utf-8");
                zos = new ZipOutputStream(httpServletResponse.getOutputStream());
                for (String fileUrl : fileUrlList) {
                    //获取minio对应路径文件流(从数据库中获取的url是编码的,这里要先进行解码,不然minioClient.getObject()方法里面会再进行一次编码,就获取不到对象)
                    String url = URLDecoder.decode(fileUrl,Charsets.UTF_8.name());
                    String downloadUrl = url.substring(url.indexOf(minioUtil.bucketName) + minioUtil.bucketName.length());
                    InputStream inputStream = minioUtil.get(downloadUrl);
                    String fileName = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
                    zipEntry = new ZipEntry(fileName + url.substring(url.lastIndexOf(".")));
                    zos.putNextEntry(zipEntry);
                    int length;
                    while ((length = inputStream.read(buff)) > 0) {
                        zos.write(buff, 0, length);
                    }
                }
                log.info("批量下载成功!");
                zos.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
                log.error("批量下载发生错误! msg=" + ioException.getMessage());
            } finally {
            }
        } else {
            log.error("批量下载发生错误,文件访问路径集合不能为空!");
        }

    }

    public static void removeFile(String fileUrl) {
        try {
            String downloadUrl = fileUrl.substring(fileUrl.indexOf(minioUtil.bucketName) + minioUtil.bucketName.length());
            minioUtil.rm(downloadUrl);
            log.info("文件删除成功!");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件删除失败! msg=" + e.getMessage());
        }

    }

    public static void batchRemoveFile(List<String> fileUrlList) {
        if (fileUrlList != null && !fileUrlList.isEmpty()) {
            try {
                for (String fileUrl : fileUrlList) {
                    String downloadUrl = fileUrl.substring(fileUrl.indexOf(minioUtil.bucketName) + minioUtil.bucketName.length());
                    minioUtil.rm(downloadUrl);
                }
                log.info("文件批量删除成功!");
            } catch (Exception e) {
                e.printStackTrace();
                log.error("文件批量删除失败! msg=" + e.getMessage());
            }
        }
    }

    public InputStream get(String objectName) {
        InputStream inputStream;
        try {
            inputStream = this.minioClient.getObject(GetObjectArgs.builder().bucket(this.bucketName).object(objectName).build());
            return inputStream;
        } catch (Exception e) {
            log.error("读取存储对象异常", e);
            throw new MinioException("读取存储对象异常", e);
        }
    }

    public void download(String objectName, String fileName) {
        try {
            this.minioClient.downloadObject(DownloadObjectArgs.builder().bucket(this.bucketName).object(objectName).filename(fileName).build());
        } catch (Exception e) {
            log.error("下载发生错误:[{}]", e.getMessage());
            throw new MinioException("下载发生错误", e);
        }
    }

    public String getUrl(String objectName) {
        try {
            return this.minioClient.getObjectUrl(this.bucketName, objectName);
        } catch (Exception e) {
            log.error("获取存储对象url异常", e);
            throw new MinioException("获取存储对象url异常", e);
        }
    }

    public void rm(String objectName) {
        try {
            this.minioClient.removeObject(RemoveObjectArgs.builder().bucket(this.bucketName).object(objectName).build());
        } catch (Exception e) {
            log.error("删除存储对象异常", e);
            throw new MinioException("删除存储对象异常", e);
        }
    }

    public void rmBatch(Collection<String> objectNames) {
        if (!CollectionUtils.isEmpty(objectNames)) {
            objectNames.forEach(this::rm);
        }

    }

}

异常类:

package com.guodi.zjjggl.common.exception;

/**
 * minio存储异常.
 *
 * @author 刘雨昕
 * @since 2021/09/24
 */
public class MinioException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    protected String code = "400900";
    protected String message;

    public MinioException(String message) {
        this.message = message;
    }

    public MinioException(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public MinioException(String code, String message, Throwable cause) {
        this.code = code;
        message = message + "'\\n" + cause.getMessage();
        this.message = message;
    }

    public MinioException(String message, Throwable cause) {
        message = message + "'\\n" + cause.getMessage();
        this.message = message;
    }

    public String getCode() {
        return this.code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

日期工具(可以用自己的日期工具,没有的可以用我的):

package com.xjjj.xjjjjt.uitls;

import jakarta.validation.constraints.NotNull;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalUnit;
import java.util.*;

/**
 * 日期工具类
 *
 * @author 刘雨昕
 * @since 2021/8/31
 */
public class DateUtil {
    /**
     * 日期格式器--年
     */
    public static final String DATE_FORMAT_YEAR = "yyyy";
    /**
     * 日期格式器
     */
    public static final String DATE_FORMAT_MONTH = "yyyy-MM";
    /**
     * 日期格式器
     */
    public static final String DATE_FORMAT_MONTH_DAY = "MM-dd";
    /**
     * 日期格式器
     */
    public static final String DATE_FORMAT = "yyyy-MM-dd";
    /**
     * 日期格式器
     */
    public static final String DATE_FORMAT_HH = "yyyy-MM-dd HH";
    /**
     * 日期格式器
     */
    public static final String DATE_FORMAT_MM = "yyyy-MM-dd HH:mm";
    /**
     * 时间格式器
     */
    public static final String TIME_FORMAT = "HH:mm:ss";
    /**
     * 日期时间格式器
     */
    public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * 日期时间格式器
     */
    public static final String DATETIME_FORMAT_1 = "HH:mm:ss 00:00:00";
    /**
     * 日期戳
     */
    public static final String DATE_STAMP = "yyyyMMdd";
    /**
     * 时间戳
     */
    public static final String TIME_STAMP = "HHmmssSSS";
    /**
     * 时间戳(精确到秒)
     */
    public static final String TIME_STAMP_SECOND = "HHmmss";
    /**
     * 日期时间戳
     */
    public static final String DATETIME_STAMP = "yyyyMMddHHmmssSSS";
    /**
     * 日期时间戳(精确到秒)
     */
    public static final String DATETIME_STAMP_SECOND = "yyyyMMddHHmmss";
    /**
     * UTC时间戳
     */
    public static final SimpleDateFormat UTC_TIME_STAMP_FORMAT =
            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    /**
     * UTC时间格式化器
     */
    public static final SimpleDateFormat UTC_FORMAT =
            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    /**
     * GMT时间格式化器
     */
    public static final SimpleDateFormat GMT_FORMAT =
            new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
    public static final Integer HALF_YEAR = 6;

    static {
        UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
        UTC_TIME_STAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
        GMT_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
    }

    /**
     * 天
     */
    public static final String DAY_STR = "天";
    /**
     * 小时
     */
    public static final String HOUR_STR = "小时";
    /**
     * 分钟
     */
    public static final String MINUTE_STR = "分钟";

    /**
     * 将字符串解析成yyyy-MM-dd的日期
     *
     * @param value the value
     * @return the date
     */
    public static Date parseDate(String value) {
        try {
            return new SimpleDateFormat(DATE_FORMAT, Locale.CHINA).parse(value);
        } catch (ParseException e) {
            return new Date();
        }
    }

    /**
     * 将字符串解析成HH:mm:ss的时间
     *
     * @param value the value
     * @return the date
     */
    public static Date parseTime(String value) {
        try {
            return new SimpleDateFormat(TIME_FORMAT, Locale.CHINA).parse(value);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 将字符串解析成yyyy-MM-dd HH:mm:ss的日期时间
     *
     * @param value the value
     * @return the date
     */
    public static Date parseDateTime(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        try {
            return new SimpleDateFormat(DATETIME_FORMAT, Locale.CHINA).parse(value);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 将字符串解析成自定义格式的日期时间
     *
     * @param value  the value
     * @param format the format
     * @return the date
     */
    public static Date parseDateTime(String value, String format) {
        try {
            return new SimpleDateFormat(format, Locale.CHINA).parse(value);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 将字符串解析成yyyyMMddHHmmssSSS的日期时间
     *
     * @param value the value
     * @return the date
     */
    public static Date parseDateTimeStamp(String value) {
        try {
            return new SimpleDateFormat(DATETIME_STAMP, Locale.CHINA).parse(value);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 将字符串解析成yyyyMMddHHmmss的日期时间
     *
     * @param value the value
     * @return the date
     */
    public static Date parseDateTimeSecond(String value) {
        try {
            return new SimpleDateFormat(DATETIME_STAMP_SECOND, Locale.CHINA).parse(value);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 将时间解析成yyyy-MM-dd的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatDate(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATE_FORMAT, Locale.CHINA).format(date);
    }

    /**
     * 将时间解析成yyyy的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatDateToYear(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATE_FORMAT_YEAR, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成HH:mm:ss的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatTime(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(TIME_FORMAT, Locale.CHINA).format(date);
    }

    /**
     * 将日期时间解析成yyyy-MM-dd HH:mm:ss的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatDateTime(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATETIME_FORMAT, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成yyyyMMddHHmmssSSS的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatDateTimestamp(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATETIME_STAMP, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成yyyyMMddHHmmss的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatDateTimeSecond(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATETIME_STAMP_SECOND, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成yyyyMMdd的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatDatestamp(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATE_STAMP, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成HHmmssSSS的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatTimestamp(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(TIME_STAMP, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成yyyy-MM-dd HH的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatDateHH(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATE_FORMAT_HH, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成yyyy-MM-dd HH:mm的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatDateMM(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATE_FORMAT_MM, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成yyyy-MM的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatYearMonth(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATE_FORMAT_MONTH, Locale.CHINA).format(date);
    }

    /**
     * 将日期解析成MM-dd的字符串
     *
     * @param date the date
     * @return the string
     */
    public static String formatMonthDay(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DATE_FORMAT_MONTH_DAY, Locale.CHINA).format(date);
    }

    /**
     * 获取当天的开始时间, 如:2020-06-24 00:00:00
     *
     * @return the begin of date
     */
    public static Date getBeginOfDate() {
        return getBeginOfDate(null);
    }

    /**
     * 获取当天的结束时间, 如:2020-06-24 23:59:59
     *
     * @return the end of date
     */
    public static Date getEndOfDate() {
        return getEndOfDate(null);
    }

    /**
     * 获取指定日期的开始时间, 如:2020-06-25 00:00:00
     *
     * @param date the date
     * @return the begin of date
     */
    public static Date getBeginOfDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }
        return setDayToBegin(calendar).getTime();
    }

    /**
     * 获取指定日期的结束时间, 如:2020-02-25 23:59:59
     *
     * @param date the date
     * @return the end of date
     */
    public static Date getEndOfDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }
        return setDayToEnd(calendar).getTime();
    }

    /**
     * 获取当年天数.
     *
     * @return days
     */
    public static int getDaysInYear() {
        GregorianCalendar calendar = new GregorianCalendar();
        return calendar.isLeapYear(calendar.get(Calendar.YEAR)) ? 366 : 365;
    }

    /**
     * 格式化当前日期.
     *
     * @return date型时间 sys date of date
     */
    public static Date getSysDateOfDate() {
        return parseDateTime(formatDateTime(new Date()));
    }

    /**
     * 获取指定日期.
     *
     * @param calendarType Calendar.DATE、Calendar.MONTH、Calendar.YEAR等
     * @param num          对应日期类型对应的数量
     * @return 指定date型时间 比如:离现在30天前的日期、获取上个月日期
     */
    public static Date getDefineDate(int calendarType, int num) {
        Calendar cal = Calendar.getInstance();
        cal.add(calendarType, num);
        return cal.getTime();
    }

    /**
     * 获取天数差.
     *
     * @param specifiedDate 指定的时间,即需要比较的时间
     * @param largeDate     到期的时间,即比较大的时间
     * @return 天数差 计算指定时间与当前时间的天数差
     */
    public static int getDaysDifference(Date specifiedDate, Date largeDate) {
        Calendar cNow = Calendar.getInstance();
        Calendar cReturnDate = Calendar.getInstance();
        cNow.setTime(largeDate);
        cReturnDate.setTime(specifiedDate);
        setDayToBegin(cNow);
        setDayToBegin(cReturnDate);
        long todayMs = cNow.getTimeInMillis();
        long returnMs = cReturnDate.getTimeInMillis();
        long intervalMs = todayMs - returnMs;
        return (int) (intervalMs / (1000 * 86400));
    }

    /**
     * 比较日期.
     *
     * @param referenceDate 需要比较的时间 不能为空(null),需要正确的日期格式
     * @param date          被比较的时间 为空(null)则为当前时间
     * @param type          返回值类型,0为多少天,1为多少个月,2为多少年
     * @return the int
     * @throws ParseException the parse exception
     */
    public static int compareDate(String referenceDate, String date, int type)
            throws ParseException {
        int n = 0;
        String formatStyle = type == 1 ? DATE_FORMAT_MONTH : DATE_FORMAT;

        String dateStr = date;
        if (dateStr == null) {
            dateStr = formatDate(new Date());
        }

        DateFormat df = new SimpleDateFormat(formatStyle);
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(df.parse(referenceDate));
        c2.setTime(df.parse(dateStr));

        // 循环对比,直到相等,n 就是所要的结果
        while (!c1.after(c2)) {
            n++;
            if (type == 1) {
                // 比较月份,月份+1
                c1.add(Calendar.MONTH, 1);
            } else {
                // 比较天数,日期+1
                c1.add(Calendar.DATE, 1);
            }
        }

        n = n - 1;

        if (type == 2) {
            n = n / 365;
        }

        return n;
    }

    /**
     * 获取当前时间之前 /之后多少年/月/日的时间.
     *
     * @param format   日期格式
     * @param variable 正数代表时间之后,负数代表时间之前
     * @param field    单位,年,月,日
     * @return 时间
     * @see #getBeforeDays
     */
    @Deprecated
    public static String getDiffDate(String format, int variable, int field) {
        Calendar c = Calendar.getInstance();
        SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.CHINA);
        c.set(field, c.get(field) + variable);
        return formatter.format(c.getTime());
    }

    /**
     * 获取指定时间相加天数后的时间.
     *
     * @param date   传入相加时间
     * @param field  时间格式
     * @param amount 日期或时间的数量
     * @return 相加后的时间 date
     * @see #getAfterDays
     */
    @Deprecated
    public static Date dateAdd(Date date, int field, int amount) {
        Calendar ca = Calendar.getInstance();
        if (date != null) {
            ca.setTime(date);
        }
        ca.add(field, amount);
        return ca.getTime();
    }

    /**
     * 获取当前时间相加天数的时间.
     *
     * @param field  时间格式
     * @param amount 相加的时间天数
     * @return 相加后的时间 date
     */
    public static Date dateAdd(int field, int amount) {
        return dateAdd(null, field, amount);
    }

    /**
     * 时间范围
     */
    public static class DateRange {
        /**
         * 开始时间
         */
        private Date begin;
        /**
         * 结束时间
         */
        private Date end;

        /**
         * Instantiates a new Date range.
         */
        public DateRange() {
        }

        /**
         * Instantiates a new Date range.
         *
         * @param begin the begin
         * @param end   the end
         */
        public DateRange(Date begin, Date end) {
            this.begin = begin;
            this.end = end;
        }

        /**
         * Gets begin.
         *
         * @return the begin
         */
        public Date getBegin() {
            return begin;
        }

        /**
         * Gets end.
         *
         * @return the end
         */
        public Date getEnd() {
            return end;
        }
    }

    /**
     * 获取指定日期的当天时间范围,如(2020-12-09 00:00:00 2020-12-12 23:59:59)
     *
     * @param date 时间,为空表示当前时间
     * @return 时间范围 day range
     */
    public static DateRange getDayRange(Date date) {
        DateRange range = new DateRange();
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }

        range.begin = setDayToBegin(calendar).getTime();
        range.end = setDayToEnd(calendar).getTime();

        return range;
    }

    /**
     * 获取指定日期本周的时间范围,如(2020-12-14 00:00:00 2020-12-20 23:59:59)
     *
     * @param date 时间,为空表示当前时间
     * @return 时间范围 week range
     */
    public static DateRange getWeekRange(Date date) {
        DateRange range = new DateRange();
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }

        calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        range.begin = setDayToBegin(calendar).getTime();

        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) + 6);
        range.end = setDayToEnd(calendar).getTime();

        return range;
    }

    /**
     * 获取指定日期本月的时间范围,如(2020-12-01 00:00:00 2020-12-31 23:59:59)
     *
     * @param date 时间,为空表示当前时间
     * @return 时间范围 month range
     */
    public static DateRange getMonthRange(Date date) {
        DateRange range = new DateRange();
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }
        // 获取第一天
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        range.begin = setDayToBegin(calendar).getTime();
        // 获取最后一天
        calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 1);
        range.end = setDayToEnd(calendar).getTime();

        return range;
    }

    /**
     * 获取指定日期本季度的时间范围,如(2020-10-01 00:00:00 2020-12-31 23:59:59)
     *
     * @param date 时间,为空表示当前时间
     * @return 时间范围 season range
     */
    public static DateRange getSeasonRange(Date date) {
        DateRange range = new DateRange();
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }

        int month = calendar.get(Calendar.MONTH);
        calendar.set(Calendar.MONTH, month - (month % 3));
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        range.begin = setDayToBegin(calendar).getTime();

        calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 3);
        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 1);
        range.end = setDayToEnd(calendar).getTime();

        return range;
    }

    /**
     * 获取指定日期本半年的时间范围,如(2020-06-01 00:00:00 2020-12-31 23:59:59)
     *
     * @param date 时间,为空表示当前时间
     * @return 时间范围 half year range
     */
    public static DateRange getHalfYearRange(Date date) {
        DateRange range = new DateRange();
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }
        int current = calendar.get(Calendar.MONTH) + 1;
        int month = Calendar.JANUARY;
        if (current > HALF_YEAR) {
            month = Calendar.JULY;
        }
        calendar.set(calendar.get(Calendar.YEAR), month, 1, 0, 0, 0);
        range.begin = calendar.getTime();

        calendar.set(Calendar.MONTH, month + 6);
        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 1);
        range.end = setDayToEnd(calendar).getTime();

        return range;
    }

    /**
     * 获取指定日期本年的时间范围,如(2020-01-01 00:00:00 2020-12-31 23:59:59)
     *
     * @param date 时间,为空表示当前时间
     * @return 时间范围 year range
     */
    public static DateRange getYearRange(Date date) {
        DateRange range = new DateRange();
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        }

        calendar.set(calendar.get(Calendar.YEAR), Calendar.JANUARY, 1, 0, 0, 0);
        range.begin = calendar.getTime();

        calendar.set(Calendar.MONTH, 12);
        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 1);
        range.end = setDayToEnd(calendar).getTime();

        return range;
    }

    /**
     * 获取指定年份本年的日期范围,如(2020-01-01 2020-12-31)
     *
     * @param year 年份,为空表示当前时间
     * @return 时间范围 year range
     */
    public static DateRange getYearDateRange(@NonNull String year) {
        DateRange range = new DateRange();
        String start = year + "-01-01";
        String end = year + "-12-31";
        range.begin = parseDate(start);
        range.end = parseDate(end);
        return range;
    }

    private static Calendar setDayToEnd(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar;
    }

    private static Calendar setDayToBegin(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar;
    }

    /**
     * 判断时间date1是否在时间date2之前
     *
     * @param date1 时间
     * @param date2 时间
     * @return the boolean
     */
    public static boolean isDateBefore(String date1, String date2) {
        return Objects.requireNonNull(parseDateTime(date1)).before(parseDateTime(date2));
    }

    /**
     * 验证当前时间是否在指定时间范围内(例如9:00-22:30)
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return bool boolean
     */
    public static boolean isRangeTime(String startTime, String endTime) {
        boolean isRangeTime = false;
        String nowDay = formatDate(new Date());
        String start = startTime, end = endTime;
        start = nowDay + " " + start + ":00";
        end = nowDay + " " + end + ":00";
        // 开始时间不在结束时间前,表示时间进行了跨天设置
        if (!isDateBefore(start, end)) {
            // 跨天设置的时间需要当前时间大于结束时间并且小于开始时间
            if (isDateBefore(formatDateTime(new Date()), end)
                    || isDateBefore(start, formatDateTime(new Date()))) {
                isRangeTime = true;
            }
        } else {
            if (isDateBefore(formatDateTime(new Date()), end)
                    && isDateBefore(start, formatDateTime(new Date()))) {
                isRangeTime = true;
            }
        }
        return isRangeTime;
    }

    /**
     * utc时间转为date
     *
     * @param value "2004-08-04T19:09:02.768Z";
     * @return date date
     */
    public static Date parseUTCDateTimeStamp(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        try {
            return UTC_TIME_STAMP_FORMAT.parse(value);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * Format utc date time stamp string.
     *
     * @param date the date
     * @return the string
     */
    public static String formatUTCDateTimeStamp(Date date) {
        return UTC_TIME_STAMP_FORMAT.format(date);
    }

    /**
     * Format utc date time stamp string.
     *
     * @param dateStr yyyy-MM-dd HH:mm:ss
     * @return utc时间 string
     */
    public static String formatUTCDateTimeStamp(String dateStr) {
        Date d = parseDateTime(dateStr);
        return UTC_TIME_STAMP_FORMAT.format(d);
    }

    /**
     * 把日期解析为UTC时间 @param dates the dates
     *
     * @param dates the dates
     * @return the string
     */
    public static String formatUTCDate(Date... dates) {
        Date date = new Date();
        if (dates != null && dates.length > 0) {
            date = dates[0];
        }
        return UTC_FORMAT.format(date);
    }

    /**
     * 把日期解析为GMT时间 @param date the date
     *
     * @param date the date
     * @return the string
     */
    public static String formatGMTDate(Date date) {
        return GMT_FORMAT.format(date);
    }

    /**
     * 获取今天是一周的星期几
     *
     * @return day of week
     */
    public static int getDayOfWeek() {
        Calendar calendar = Calendar.getInstance();
        int day = calendar.get(Calendar.DAY_OF_WEEK);
        // 星期天对应1,星期一从2开始
        if (day == 1) {
            return 7;
        }
        return day - 1;
    }

    /**
     * 获取某年某月的最后一天
     *
     * @param year  年
     * @param month 月
     * @return 最后一天 last day of month
     */
    public static Date getLastDayOfMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DATE));
        return cal.getTime();
    }

    /**
     * 获取某年某月的第一天
     *
     * @param year  年
     * @param month 月
     * @return 第一天 first day of month
     */
    public static Date getFirstDayOfMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getMinimum(Calendar.DATE));
        return cal.getTime();
    }

    /**
     * 获取当前时间
     *
     * @return the current time
     */
    public static String getCurrentTime() {
        Date date = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return formatter.format(date);
    }

    // ============================ 日期操作=============================

    /**
     * 获取开始日期n天前的日期
     *
     * @param date           开始日期为空,则初始化一个当前时间
     * @param daysToSubtract n
     * @return the before days
     */
    public static Date getBeforeDays(Date date, @NotNull Long daysToSubtract) {
        if (Objects.isNull(date)) {
            date = new Date();
        }
        return localDate2date(date2LocalDate(date).minusDays(daysToSubtract));
    }

    public static Date beforeDays(Date date, @NotNull Long daysToSubtract) {
        if (Objects.isNull(date)) {
            date = new Date();
        }
        return localDateTime2Date(date2LocalDateTime(date).minusDays(daysToSubtract));
    }

    /**
     * 获取开始日期n天后的日期.
     *
     * @param date           开始日期为空,则初始化一个当前时间
     * @param daysToSubtract the days to subtract
     * @return the after days
     */
    public static Date getAfterDays(Date date, @NotNull Long daysToSubtract) {
        if (Objects.isNull(date)) {
            date = new Date();
        }
        return localDate2date(date2LocalDate(date).plusDays(daysToSubtract));
    }

    /**
     * Gets before weeks.
     *
     * @param date            the date
     * @param weeksToSubtract the weeks to subtract
     * @return the before weeks
     */
    public static Date getBeforeWeeks(Date date, @NotNull Long weeksToSubtract) {
        if (Objects.isNull(date)) {
            date = new Date();
        }
        return localDate2date(date2LocalDate(date).minusWeeks(weeksToSubtract));
    }

    /**
     * Gets before months.
     *
     * @param date             the date
     * @param monthsToSubtract the months to subtract
     * @return the before months
     */
    public static Date getBeforeMonths(Date date, @NotNull Long monthsToSubtract) {
        if (Objects.isNull(date)) {
            date = new Date();
        }
        return localDate2date(date2LocalDate(date).minusMonths(monthsToSubtract));
    }

    /**
     * Gets before years.
     *
     * @param date            the date
     * @param yearsToSubtract the years to subtract
     * @return the before years
     */
    public static Date getBeforeYears(Date date, @NotNull Long yearsToSubtract) {
        if (Objects.isNull(date)) {
            date = new Date();
        }
        return localDate2date(date2LocalDate(date).minusMonths(yearsToSubtract));
    }

    /**
     * Date 2 local date time local date time.
     *
     * @param date the date
     * @return the local date time
     */
    public static LocalDateTime date2LocalDateTime(@NotNull Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * Date 2 local date local date.
     *
     * @param date the date
     * @return the local date
     */
    public static LocalDate date2LocalDate(@NotNull Date date) {
        return date2LocalDateTime(date).toLocalDate();
    }

    /**
     * Local date time 2 date date.
     *
     * @param localDateTime the local date time
     * @return the date
     */
    public static Date localDateTime2Date(@NotNull LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * Local date 2 date date.
     *
     * @param localDate the local date
     * @return the date
     */
    public static Date localDate2date(@NotNull LocalDate localDate) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * Local date time 2 string string.
     *
     * @param date the date
     * @param fmt  the fmt
     * @return the string
     */
    public static String localDateTime2String(LocalDateTime date, DateTimeFormatter fmt) {
        if (Objects.isNull(fmt)) {
            fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
        }
        return date.format(fmt);
    }

    /**
     * 日期间隔.
     * Period类表示年、月、日时间间隔
     *
     * @param startDateInclusive 开始日期
     * @param endDateExclusive   结束日期
     * @param unit               年/月/日
     * @return 间隔 long
     */
    public static long between(
            LocalDate startDateInclusive, LocalDate endDateExclusive, TemporalUnit unit) {
        return Period.between(startDateInclusive, endDateExclusive).get(unit);
    }

    public static long between(Date start, Date end, TemporalUnit unit) {
        return between(date2LocalDateTime(start), date2LocalDateTime(end), unit);
    }

    /**
     * 时间间隔.
     * LocalDate、LoclaTime、LocalDateTime等都实现了Temporal接口
     * Duration是一个表示秒/纳秒的时间间隔。即Period基于日期值,而Duration基于时间值。 这两个类是为了日期、时间的度量服务的。<br>
     *
     * @param startInclusive the start inclusive
     * @param endExclusive   the end exclusive
     * @param unit           the unit
     * @return long long
     */
    public static long between(Temporal startInclusive, Temporal endExclusive, TemporalUnit unit) {
        return Duration.between(startInclusive, endExclusive).get(unit);
    }

    /**
     * 获取今天最小时间
     *
     * @return local date time
     */
    public static LocalDateTime todayMin() {
        return LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
    }

    /**
     * Day min local date time.
     *
     * @param day the day
     * @return the local date time
     */
    public static LocalDateTime dayMin(LocalDate day) {
        return LocalDateTime.of(day, LocalTime.MIN);
    }

    /**
     * 获取今天最大时间
     *
     * @return local date time
     */
    public static LocalDateTime todayMax() {
        return LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
    }

    /**
     * Day max local date time.
     *
     * @param day the day
     * @return the local date time
     */
    public static LocalDateTime dayMax(LocalDate day) {
        return LocalDateTime.of(day, LocalTime.MAX);
    }


    /**
     * 获取某年某个季度的开始时间
     *
     * @param year    指定年份
     * @param quarter 指定季度(1,2,3,4)
     * @return date
     */
    public static Date getQuarterStartTime(final int year, final int quarter) {
        Calendar c = Calendar.getInstance();
        if (quarter <= 0 || quarter > 4) {
            return null;
        } else if (quarter == 1) {
            c.set(year, Calendar.JANUARY, 1);
        } else if (quarter == 2) {
            c.set(year, Calendar.APRIL, 1);
        } else if (quarter == 3) {
            c.set(year, Calendar.JULY, 1);
        } else {
            c.set(year, Calendar.OCTOBER, 1);
        }
        return c.getTime();
    }

    /**
     * 获取某年某个季度的结束时间
     *
     * @param year    指定年份
     * @param quarter 指定季度(1,2,3,4)
     * @return date
     */
    public static Date getQuarterEndTime(final int year, final int quarter) {
        Calendar c = Calendar.getInstance();
        if (quarter <= 0 || quarter > 4) {
            return null;
        } else if (quarter == 1) {
            c.set(year, Calendar.MARCH, 31);
        } else if (quarter == 2) {
            c.set(year, Calendar.JUNE, 30);
        } else if (quarter == 3) {
            c.set(year, Calendar.SEPTEMBER, 30);
        } else {
            c.set(year, Calendar.DECEMBER, 31);
        }
        return c.getTime();
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值