ื›ืชื™ื‘ืช ืœืงื•ื— NTP ืคืฉื•ื˜

ืฉืœื•ื ืœืคื•ื’ืขื™ื. ื”ื™ื•ื ืื ื™ ืจื•ืฆื” ืœื“ื‘ืจ ืขืœ ืื™ืš ืœื›ืชื•ื‘ ืœืงื•ื— NTP ืคืฉื•ื˜ ืžืฉืœืš. ื‘ืขืฆื, ื”ืฉื™ื—ื” ืชืคื ื” ืœืžื‘ื ื” ื”ื—ื‘ื™ืœื” ื•ืœืื•ืคืŸ ืขื™ื‘ื•ื“ ื”ืชื’ื•ื‘ื” ืžืฉืจืช ื”-NTP. ื”ืงื•ื“ ื™ื™ื›ืชื‘ ื‘-python, ื›ื™ ืœื“ืขืชื™, ืคืฉื•ื˜ ืื™ืŸ ืฉืคื” ื˜ื•ื‘ื” ื™ื•ืชืจ ืœื“ื‘ืจื™ื ื›ืืœื”. ืื ื™ื ื™ ื˜ืขื ื™ืฉื™ืžื• ืœื‘ ืœื“ืžื™ื•ืŸ ืฉืœ ื”ืงื•ื“ ืขื ืงื•ื“ ntplib โ€“ "ืฉืื‘ืชื™ ื”ืฉืจืื”" ืžืžื ื•.

ืื– ืžื” ื–ื” ื‘ื›ืœืœ NTP? NTP ื”ื•ื ืคืจื•ื˜ื•ืงื•ืœ ืœืชืงืฉื•ืจืช ืขื ืฉืจืชื™ ื–ืžืŸ. ืคืจื•ื˜ื•ืงื•ืœ ื–ื” ืžืฉืžืฉ ื‘ืžื›ื•ื ื•ืช ืžื•ื“ืจื ื™ื•ืช ืจื‘ื•ืช. ืœื“ื•ื’ืžื”, ืฉื™ืจื•ืช w32tm ื‘ื—ืœื•ื ื•ืช.

ื™ืฉื ืŸ 5 ื’ืจืกืื•ืช ืฉืœ ืคืจื•ื˜ื•ืงื•ืœ NTP ื‘ืกืš ื”ื›ืœ. ื”ืจืืฉื•ื ื”, ื’ืจืกื” 0 (1985, RFC958) ื ื—ืฉื‘ืช ื›ื™ื•ื ืœืžื™ื•ืฉื ืช. ืžืฉืชืžืฉื™ื ื—ื“ืฉื™ื ื™ื•ืชืจ ื›ื™ื•ื, 1 (1988, RFC1059), 2 (1989, RFC1119), 3 (1992, RFC1305) ื•-4 (1996, RFC2030). ื’ืจืกืื•ืช 1-4 ืชื•ืืžื•ืช ื–ื• ืœื–ื•, ื”ืŸ ื ื‘ื“ืœื•ืช ืจืง ื‘ืืœื’ื•ืจื™ืชืžื™ื ืฉืœ ื”ืฉืจืชื™ื.

ืคื•ืจืžื˜ ืžื ื•ืช

ื›ืชื™ื‘ืช ืœืงื•ื— NTP ืคืฉื•ื˜

ืžื—ื•ื•ืŸ ื–ื™ื ื•ืง (ืžื—ื•ื•ืŸ ืชื™ืงื•ืŸ) ื”ื•ื ืžืกืคืจ ื”ืžืฆื™ื™ืŸ ืืช ืื–ื”ืจืช ื”ื–ื™ื ื•ืง ืฉื ื™ื™ื”. ืžึทืฉืึฐืžึธืขื•ึผืช:

  • 0 - ืื™ืŸ ืชื™ืงื•ืŸ
  • 1 - ื”ื“ืงื” ื”ืื—ืจื•ื ื” ืฉืœ ื”ื™ื•ื ืžื›ื™ืœื” 61 ืฉื ื™ื•ืช
  • 2 - ื”ื“ืงื” ื”ืื—ืจื•ื ื” ืฉืœ ื”ื™ื•ื ืžื›ื™ืœื” 59 ืฉื ื™ื•ืช
  • 3 - ื›ืฉืœ ื‘ืฉืจืช (ื–ืžืŸ ืžื—ื•ืฅ ืœืกื ื›ืจื•ืŸ)

ืžืกืคืจ ื’ืจืกื” (ืžืกืคืจ ื’ืจืกื”) โ€“ ืžืกืคืจ ื’ืจืกื” ืฉืœ ืคืจื•ื˜ื•ืงื•ืœ NTP (1-4).

ืžืฆื‘ (ืžืฆื‘) - ืžืฆื‘ ืคืขื•ืœื” ืฉืœ ืฉื•ืœื— ื”ื—ื‘ื™ืœื”. ืขืจืš ืž-0 ืขื“ 7, ื”ื ืคื•ืฅ ื‘ื™ื•ืชืจ:

  • 3 - ืœืงื•ื—
  • 4 - ืฉืจืช
  • 5 - ืžืฆื‘ ืฉื™ื“ื•ืจ

ืฉื›ื‘ื” (ืจืžืช ืฉื›ื‘ื•ืช) - ืžืกืคืจ ืฉื›ื‘ื•ืช ื”ื‘ื™ื ื™ื™ื ื‘ื™ืŸ ื”ืฉืจืช ืœืฉืขื•ืŸ ื”ื™ื™ื—ื•ืก (1 - ื”ืฉืจืช ืœื•ืงื— ื ืชื•ื ื™ื ื™ืฉื™ืจื•ืช ืžืฉืขื•ืŸ ื”ื™ื™ื—ื•ืก, 2 - ื”ืฉืจืช ืœื•ืงื— ื ืชื•ื ื™ื ืžื”ืฉืจืช ืขื ืจืžื” 1 ื•ื›ื•').
ื‘ืจื›ื” ื”ื•ื ืžืกืคืจ ืฉืœื ื‘ืกื™ืžืŸ ื”ืžื™ื™ืฆื’ ืืช ื”ืžืจื•ื•ื— ื”ืžืจื‘ื™ ื‘ื™ืŸ ื”ื•ื“ืขื•ืช ืขื•ืงื‘ื•ืช. ืœืงื•ื— ื”-NTP ืžืฆื™ื™ืŸ ื›ืืŸ ืืช ื”ืžืจื•ื•ื— ืฉื‘ื• ื”ื•ื ืžืฆืคื” ืœืกืงืจ ืืช ื”ืฉืจืช, ื•ืฉืจืช ื”-NTP ืžืฆื™ื™ืŸ ืืช ื”ืžืจื•ื•ื— ืฉื‘ื• ื”ื•ื ืžืฆืคื” ืœื”ื™ืฉืืœ. ื”ืขืจืš ืฉื•ื•ื” ืœืœื•ื’ืจื™ืชื ื”ื‘ื™ื ืืจื™ ืฉืœ ืฉื ื™ื•ืช.
ื“ื™ื•ืง (ื“ื™ื•ืง) ื”ื•ื ืžืกืคืจ ืฉืœื ื‘ืกื™ืžืŸ ื”ืžื™ื™ืฆื’ ืืช ื”ื“ื™ื•ืง ืฉืœ ืฉืขื•ืŸ ื”ืžืขืจื›ืช. ื”ืขืจืš ืฉื•ื•ื” ืœืœื•ื’ืจื™ืชื ื”ื‘ื™ื ืืจื™ ืฉืœ ืฉื ื™ื•ืช.
ืขื™ื›ื•ื‘ ืฉื•ืจืฉ (ื”ืฉื”ื™ื™ืช ืฉืจืช) ื”ื™ื ื”ื–ืžืŸ ืฉืœื•ืงื— ืœืฉืขื•ืŸ ืœื”ื’ื™ืข ืœืฉืจืช NTP, ื›ืžืกืคืจ ืฉื ื™ื•ืช ืงื‘ื•ืข.
ืคื™ื–ื•ืจ ืฉื•ืจืฉื™ื (ืคื™ื–ื•ืจ ืฉืจืช) - ื”ืคื™ื–ื•ืจ ืฉืœ ืฉืขื•ืŸ ืฉืจืช NTP ื›ืžืกืคืจ ืฉื ื™ื•ืช ื‘ื ืงื•ื“ื” ืงื‘ื•ืขื”.
ืชืขื•ื“ืช ื–ื”ื•ืช (ืžื–ื”ื” ืžืงื•ืจ) - ืžื–ื”ื” ืฉืขื•ืŸ. ืื ืœืฉืจืช ื™ืฉ ืฉื›ื‘ื” 1, ืื– ref id ื”ื•ื ื”ืฉื ืฉืœ ื”ืฉืขื•ืŸ ื”ืื˜ื•ืžื™ (4 ืชื•ื•ื™ ASCII). ืื ื”ืฉืจืช ืžืฉืชืžืฉ ื‘ืฉืจืช ืื—ืจ, ืื–ื™ ื”-ref id ืžื›ื™ืœ ืืช ื”ื›ืชื•ื‘ืช ืฉืœ ืฉืจืช ื–ื”.
4 ื”ืฉื“ื•ืช ื”ืื—ืจื•ื ื™ื ื”ื ื”ื–ืžืŸ - 32 ืกื™ื‘ื™ื•ืช - ื”ื—ืœืง ื”ืฉืœื, 32 ืกื™ื‘ื™ื•ืช - ื”ื—ืœืง ื”ืฉื‘ืจื™ืจื™.
ื”ืชื™ื™ื—ืกื•ืช - ื”ืฉืขื•ืŸ ื”ืขื“ื›ื ื™ ื‘ื™ื•ืชืจ ื‘ืฉืจืช.
ืœึดื ึฐื‘ึผื•ึผืขึท - ื–ืžืŸ ืฉืœื™ื—ืช ื”ื—ื‘ื™ืœื” (ืžื•ืœื ืขืœ ื™ื“ื™ ื”ืฉืจืช - ืขื•ื“ ืขืœ ื›ืš ื‘ื”ืžืฉืš).
ืœึฐืงึทื‘ึผึตืœ - ื–ืžืŸ ืฉื‘ื• ื”ื—ื‘ื™ืœื” ื”ืชืงื‘ืœื” ืขืœ ื™ื“ื™ ื”ืฉืจืช.
ืœืฉื“ืจ โ€“ ื–ืžืŸ ืฉื‘ื• ื”ื—ื‘ื™ืœื” ื ืฉืœื—ื” ืžื”ืฉืจืช ืœืœืงื•ื— (ืžื•ืœื ืขืœ ื™ื“ื™ ื”ืœืงื•ื—, ืขืœ ื›ืš ื‘ื”ืžืฉืš).

ืฉื ื™ ื”ืฉื“ื•ืช ื”ืื—ืจื•ื ื™ื ืœื ื™ื™ื—ืฉื‘ื•.

ื‘ื•ืื• ื ื›ืชื•ื‘ ืืช ื”ื—ื‘ื™ืœื” ืฉืœื ื•:

ืงื•ื“ ื—ื‘ื™ืœื”

class NTPPacket:
    _FORMAT = "!B B b b 11I"

    def __init__(self, version_number=2, mode=3, transmit=0):
        # Necessary of enter leap second (2 bits)
        self.leap_indicator = 0
        # Version of protocol (3 bits)
        self.version_number = version_number
        # Mode of sender (3 bits)
        self.mode = mode
        # The level of "layering" reading time (1 byte)
        self.stratum = 0
        # Interval between requests (1 byte)
        self.pool = 0
        # Precision (log2) (1 byte)
        self.precision = 0
        # Interval for the clock reach NTP server (4 bytes)
        self.root_delay = 0
        # Scatter the clock NTP-server (4 bytes)
        self.root_dispersion = 0
        # Indicator of clocks (4 bytes)
        self.ref_id = 0
        # Last update time on server (8 bytes)
        self.reference = 0
        # Time of sending packet from local machine (8 bytes)
        self.originate = 0
        # Time of receipt on server (8 bytes)
        self.receive = 0
        # Time of sending answer from server (8 bytes)
        self.transmit = transmit

ื›ื“ื™ ืœืฉืœื•ื— (ื•ืœืงื‘ืœ) ื—ื‘ื™ืœื” ืœืฉืจืช, ืขืœื™ื ื• ืœื”ื™ื•ืช ืžืกื•ื’ืœื™ื ืœื”ืคื•ืš ืื•ืชื” ืœืžืขืจืš ืฉืœ ื‘ืชื™ื.
ืขื‘ื•ืจ ืคืขื•ืœื” ื–ื• (ื•ื”ืคื•ื›ื”), ื ื›ืชื•ื‘ ืฉืชื™ ืคื•ื ืงืฆื™ื•ืช - pack() ื•-unpack():

ืคื•ื ืงืฆื™ื™ืช ื—ื‘ื™ืœื”

def pack(self):
        return struct.pack(NTPPacket._FORMAT,
                (self.leap_indicator << 6) + 
                    (self.version_number << 3) + self.mode,
                self.stratum,
                self.pool,
                self.precision,
                int(self.root_delay) + get_fraction(self.root_delay, 16),
                int(self.root_dispersion) + 
                    get_fraction(self.root_dispersion, 16),
                self.ref_id,
                int(self.reference),
                get_fraction(self.reference, 32),
                int(self.originate),
                get_fraction(self.originate, 32),
                int(self.receive),
                get_fraction(self.receive, 32),
                int(self.transmit),
                get_fraction(self.transmit, 32))

ืคื•ื ืงืฆื™ื™ืช unpack

def unpack(self, data: bytes):
        unpacked_data = struct.unpack(NTPPacket._FORMAT, data)

        self.leap_indicator = unpacked_data[0] >> 6  # 2 bits
        self.version_number = unpacked_data[0] >> 3 & 0b111  # 3 bits
        self.mode = unpacked_data[0] & 0b111  # 3 bits

        self.stratum = unpacked_data[1]  # 1 byte
        self.pool = unpacked_data[2]  # 1 byte
        self.precision = unpacked_data[3]  # 1 byte

        # 2 bytes | 2 bytes
        self.root_delay = (unpacked_data[4] >> 16) +
            (unpacked_data[4] & 0xFFFF) / 2 ** 16
         # 2 bytes | 2 bytes
        self.root_dispersion = (unpacked_data[5] >> 16) +
            (unpacked_data[5] & 0xFFFF) / 2 ** 16 

        # 4 bytes
        self.ref_id = str((unpacked_data[6] >> 24) & 0xFF) + " " + 
                      str((unpacked_data[6] >> 16) & 0xFF) + " " +  
                      str((unpacked_data[6] >> 8) & 0xFF) + " " +  
                      str(unpacked_data[6] & 0xFF)

        self.reference = unpacked_data[7] + unpacked_data[8] / 2 ** 32  # 8 bytes
        self.originate = unpacked_data[9] + unpacked_data[10] / 2 ** 32  # 8 bytes
        self.receive = unpacked_data[11] + unpacked_data[12] / 2 ** 32  # 8 bytes
        self.transmit = unpacked_data[13] + unpacked_data[14] / 2 ** 32  # 8 bytes

        return self

ืœืขืฆืœื ื™ื, ื›ืืคืœื™ืงืฆื™ื” - ืงื•ื“ ืฉื”ื•ืคืš ืืช ื”ื—ื‘ื™ืœื” ืœืžื—ืจื•ื–ืช ื™ืคื”

def to_display(self):
        return "Leap indicator: {0.leap_indicator}n" 
                "Version number: {0.version_number}n" 
                "Mode: {0.mode}n" 
                "Stratum: {0.stratum}n" 
                "Pool: {0.pool}n" 
                "Precision: {0.precision}n" 
                "Root delay: {0.root_delay}n" 
                "Root dispersion: {0.root_dispersion}n" 
                "Ref id: {0.ref_id}n" 
                "Reference: {0.reference}n" 
                "Originate: {0.originate}n" 
                "Receive: {0.receive}n" 
                "Transmit: {0.transmit}"
                .format(self)

ืฉืœื™ื—ืช ื—ื‘ื™ืœื” ืœืฉืจืช

ืฉืœื— ื—ื‘ื™ืœื” ืขื ืฉื“ื•ืช ืžืœืื™ื ืœืฉืจืช ื’ึดืจึฐืกึธื”, ืžืฆื‘ ะธ ืœืฉื“ืจ. ื‘ืชื•ืš ืœืฉื“ืจ ืขืœื™ืš ืœืฆื™ื™ืŸ ืืช ื”ืฉืขื” ื”ื ื•ื›ื—ื™ืช ื‘ืžื›ื•ื ื” ื”ืžืงื•ืžื™ืช (ืžืกืคืจ ืฉื ื™ื•ืช ืžืื– 1 ื‘ื™ื ื•ืืจ 1900), ื’ืจืกื” - ื›ืœ ืื—ืช ืž-1-4, ืžืฆื‘ - 3 (ืžืฆื‘ ืœืงื•ื—).

ื”ืฉืจืช, ืœืื—ืจ ืฉืงื™ื‘ืœ ืืช ื”ื‘ืงืฉื”, ืžืžืœื ืืช ื›ืœ ื”ืฉื“ื•ืช ื‘ื—ื‘ื™ืœืช ื”-NTP, ืžืขืชื™ืง ืœืฉื“ื” ืœึดื ึฐื‘ึผื•ึผืขึท ืขืจืš ืž ืœืฉื“ืจ, ืฉื”ื’ื™ืขื” ื‘ื‘ืงืฉื”. ืชืขืœื•ืžื” ื‘ืขื™ื ื™ื™ ืžื“ื•ืข ื”ืœืงื•ื— ืื™ื ื• ื™ื›ื•ืœ ืœืžืœื ืžื™ื“ ืืช ืขืจืš ื–ืžื ื• ื‘ืฉื˜ื— ืœึดื ึฐื‘ึผื•ึผืขึท. ื›ืชื•ืฆืื” ืžื›ืš, ื›ืืฉืจ ื”ื—ื‘ื™ืœื” ื—ื•ื–ืจืช, ืœืœืงื•ื— ื™ืฉ 4 ืขืจื›ื™ ื–ืžืŸ - ื”ื–ืžืŸ ืฉื‘ื• ื ืฉืœื—ื” ื”ื‘ืงืฉื” (ืœึดื ึฐื‘ึผื•ึผืขึท), ื”ื–ืžืŸ ืฉื‘ื• ื”ืฉืจืช ืงื™ื‘ืœ ืืช ื”ื‘ืงืฉื” (ืœึฐืงึทื‘ึผึตืœ), ื”ื–ืžืŸ ืฉื‘ื• ื”ืฉืจืช ืฉืœื— ืืช ื”ืชื’ื•ื‘ื” (ืœืฉื“ืจ) ื•ืžื•ืขื“ ืงื‘ืœืช ืชื’ื•ื‘ื” ืขืœ ื™ื“ื™ ื”ืœืงื•ื— - ืœื”ื’ื™ืข (ืœื ื‘ื—ื‘ื™ืœื”). ื‘ืขื–ืจืช ืขืจื›ื™ื ืืœื• ื ื•ื›ืœ ืœื”ื’ื“ื™ืจ ืืช ื”ืฉืขื” ื”ื ื›ื•ื ื”.

ืงื•ื“ ืฉืœื™ื—ื” ื•ืงื‘ืœื” ืฉืœ ื—ื‘ื™ืœื”

# Time difference between 1970 and 1900, seconds
FORMAT_DIFF = (datetime.date(1970, 1, 1) - datetime.date(1900, 1, 1)).days * 24 * 3600
# Waiting time for recv (seconds)
WAITING_TIME = 5
server = "pool.ntp.org"
port = 123
    
packet = NTPPacket(version_number=2, mode=3, transmit=time.time() + FORMAT_DIFF)
answer = NTPPacket()
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.settimeout(WAITING_TIME)
    s.sendto(packet.pack(), (server, port))
    data = s.recv(48)
    arrive_time = time.time() + FORMAT_DIFF
    answer.unpack(data)

ืขื™ื‘ื•ื“ ื ืชื•ื ื™ื ืžื”ืฉืจืช

ืขื™ื‘ื•ื“ ื”ื ืชื•ื ื™ื ืžื”ืฉืจืช ื“ื•ืžื” ืœืคืขื•ืœื•ืชื™ื• ืฉืœ ื”ื’'ื ื˜ืœืžืŸ ื”ืื ื’ืœื™ ืžื”ื‘ืขื™ื” ื”ื™ืฉื ื” ืฉืœ ืจื™ื™ืžื•ื ื“ ืž. ืกืžื•ืœื™ืืŸ (1978): "ืœืื“ื ืื—ื“ ืœื ื”ื™ื” ืฉืขื•ืŸ ื™ื“, ืื‘ืœ ื”ื™ื” ืฉืขื•ืŸ ืงื™ืจ ืžื“ื•ื™ืง ื‘ื‘ื™ืช, ืฉื”ื•ื ืœืคืขืžื™ื ืฉื›ื—ื• ืœืจื•ื—. ื™ื•ื ืื—ื“, ื›ืฉืฉื›ื— ืœื”ืคืขื™ืœ ืืช ื”ืฉืขื•ืŸ ืฉื•ื‘, ื”ืœืš ืœื‘ืงืจ ืืช ื—ื‘ืจื•, ื‘ื™ืœื” ืื™ืชื• ืืช ื”ืขืจื‘, ื•ื›ืฉื—ื–ืจ ื”ื‘ื™ืชื” ื”ืฆืœื™ื— ืœื›ื•ื•ืŸ ืืช ื”ืฉืขื•ืŸ ื ื›ื•ืŸ. ืื™ืš ื”ื•ื ื”ืฆืœื™ื— ืœืขืฉื•ืช ื–ืืช ืื ื–ืžืŸ ื”ื ืกื™ืขื” ืœื ื”ื™ื” ื™ื“ื•ืข ืžืจืืฉ? ื”ืชืฉื•ื‘ื” ื”ื™ื: "ื‘ื™ืฆื™ืื” ืžื”ื‘ื™ืช, ืื“ื ืžืขืœื” ืืช ื”ืฉืขื•ืŸ ื•ื–ื•ื›ืจ ืืช ืžื™ืงื•ื ื”ืžื—ื•ื’ื™ื. ื‘ื‘ื•ืื• ืœื—ื‘ืจ ื•ืžืฉืื™ืจ ืืช ื”ืื•ืจื—ื™ื, ื”ื•ื ืžืฆื™ื™ืŸ ืืช ืฉืขืช ื”ื’ืขืชื• ื•ื™ืฆื™ืืชื•. ื–ื” ืžืืคืฉืจ ืœื• ืœื’ืœื•ืช ื›ืžื” ื–ืžืŸ ื”ื•ื ื ืขื“ืจ. ื—ื•ื–ืจ ื”ื‘ื™ืชื” ื•ืžืกืชื›ืœ ื‘ืฉืขื•ืŸ, ืื“ื ืงื•ื‘ืข ืืช ืžืฉืš ื”ื™ืขื“ืจื•ืชื•. ื‘ื”ืคื—ืชืช ื–ืžืŸ ื–ื” ืืช ื”ื–ืžืŸ ืฉื‘ื™ืœื” ื‘ื‘ื™ืงื•ืจ, ื”ืื“ื ืžื’ืœื” ืืช ื–ืžืŸ ื”ืฉื”ื•ืช ื‘ื›ื‘ื™ืฉ ื”ืœื•ืš ื•ืฉื•ื‘. ืขืœ ื™ื“ื™ ื”ื•ืกืคืช ืžื—ืฆื™ืช ืžื–ืžืŸ ื”ืฉื”ื™ื™ื” ื‘ื“ืจื›ื™ื ืœื–ืžืŸ ื”ืฉืืจืช ื”ืื•ืจื—ื™ื, ื”ื•ื ืžืงื‘ืœ ื”ื–ื“ืžื ื•ืช ืœื‘ืจืจ ืืช ืฉืขืช ื”ื”ื’ืขื” ื”ื‘ื™ืชื” ื•ืœื”ืชืื™ื ืืช ืžื—ื•ื’ื™ ื”ืฉืขื•ืŸ ืฉืœื• ื‘ื”ืชืื.

ืžืฆื ืืช ื”ื–ืžืŸ ืฉื‘ื• ื”ืฉืจืช ืขื‘ื“ ืขืœ ื”ื‘ืงืฉื”:

  1. ืžืฆื™ืืช ื–ืžืŸ ื”ื ืกื™ืขื” ืฉืœ ืžื ื•ืช ืžื”ืœืงื•ื— ืœืฉืจืช: ((ื”ื’ืขื” - ืžืงื•ืจ) - (ืฉื™ื“ื•ืจ - ืงื‘ืœื”)) / 2
  2. ืžืฆื ืืช ื”ื”ื‘ื“ืœ ื‘ื™ืŸ ื–ืžืŸ ื”ืœืงื•ื— ืœืฉืจืช:
    ืงื‘ืœื” - ืžืงื•ืจ - ((ื”ื’ืขื” - ืžืงื•ืจ) - (ืฉื™ื“ื•ืจ - ืงื‘ืœื”)) / 2 =
    2 * ืงื‘ืœื” - 2 * ืžืงื•ืจ - ื”ื’ืขื” + ืžืงื•ืจ + ืฉื™ื“ื•ืจ - ืงื‘ืœื” =
    ืงื‘ืœื” - ืžืงื•ืจ - ื”ื’ืขื” + ืฉื™ื“ื•ืจ

ืื ื—ื ื• ืžื•ืกื™ืคื™ื ืืช ื”ืขืจืš ื”ืžืชืงื‘ืœ ืœื–ืžืŸ ื”ืžืงื•ืžื™ ื•ื ื”ื ื™ื ืžื”ื—ื™ื™ื.

ืคืœื˜ ืชื•ืฆืื”

time_different = answer.get_time_different(arrive_time)
result = "Time difference: {}nServer time: {}n{}".format(
    time_different,
    datetime.datetime.fromtimestamp(time.time() + time_different).strftime("%c"),
    answer.to_display())
print(result)

ืžื•ึนืขึดื™ืœ ืงืฉืจ.

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”