WangHan
2024-09-12 d5855a4926926698b740bc6c7ba489de47adb68b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package tech.powerjob.server.common.utils;
 
import tech.powerjob.common.RemoteConstant;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ntp.NTPUDPClient;
import org.apache.commons.net.ntp.NtpV3Packet;
import org.apache.commons.net.ntp.TimeInfo;
 
import java.net.InetAddress;
import java.util.List;
 
/**
 * 时间工具
 *
 * @author tjq
 * @since 2020/5/19
 */
@Slf4j
public class TimeUtils {
 
    /**
     * NTP 授时服务器(阿里云 -> 交大 -> 水果)
     */
    private static final List<String> NTP_SERVER_LIST = Lists.newArrayList("ntp.aliyun.com", "ntp.sjtu.edu.cn", "time1.apple.com");
    /**
     * 最大误差 5S
     */
    private static final long MAX_OFFSET = 5000;
 
    /**
     * 根据蔡勒公式计算任意一个日期是星期几
     * @param year 年
     * @param month 月
     * @param day 日
     * @return 中国星期
     */
    public static int calculateWeek(int year, int month, int day) {
        if (month == 1) {
            month = 13;
            year--;
        }
        if (month == 2) {
            month = 14;
            year--;
        }
        int y = year % 100;
        int c = year /100 ;
        int h = (y + (y / 4) + (c / 4) - (2 * c) + ((26 * (month + 1)) / 10) + day - 1) % 7;
        //可能是负值,因此计算除以7的余数之后需要判断是大于等于0还是小于0,如果小于0则将余数加7。
        if (h < 0){
            h += 7;
        }
 
        // 国内理解中星期日为 7
        if (h == 0) {
            return 7;
        }
        return h;
    }
 
    public static void check() throws TimeCheckException {
 
        NTPUDPClient timeClient = new NTPUDPClient();
 
        try {
            timeClient.setDefaultTimeout((int) RemoteConstant.DEFAULT_TIMEOUT_MS);
            for (String address : NTP_SERVER_LIST) {
                try {
                    TimeInfo t = timeClient.getTime(InetAddress.getByName(address));
                    NtpV3Packet ntpV3Packet = t.getMessage();
                    log.info("[TimeUtils] use ntp server: {}, request result: {}", address, ntpV3Packet);
                    // RFC-1305标准:https://tools.ietf.org/html/rfc1305
                    // 忽略传输误差吧...也就几十毫秒的事(阿里云给力啊!)
                    long local = System.currentTimeMillis();
                    long ntp = ntpV3Packet.getTransmitTimeStamp().getTime();
                    long offset =  local - ntp;
                    if (Math.abs(offset) > MAX_OFFSET) {
                        String msg = String.format("inaccurate server time(local:%d, ntp:%d), please use ntp update to calibration time", local, ntp);
                        throw new TimeCheckException(msg);
                    }
                    return;
                }catch (Exception ignore) {
                    log.warn("[TimeUtils] ntp server: {} may down!", address);
                }
            }
            throw new TimeCheckException("no available ntp server, maybe alibaba, sjtu and apple are both collapse");
        }finally {
            timeClient.close();
        }
    }
 
 
 
    public static final class TimeCheckException extends RuntimeException {
        public TimeCheckException(String message) {
            super(message);
        }
    }
}