33

How can I change System Date, Time, Timezone in Python? Is there any module available for this?

  1. I don't want to execute any system commands
  2. I want one common solution, which should work on both Unix and Windows.
tshepang
  • 12,111
  • 21
  • 91
  • 136
Abhishek Kulkarni
  • 3,693
  • 8
  • 35
  • 42
  • check it out >> http://docs.python.org/library/datetime.html – Hamoudaq Aug 22 '12 at 21:13
  • Given system permissions and authentication, this is harder to do than you think for both Unix and Windows. – the wolf Aug 22 '12 at 21:18
  • @EngHamoud, I don't think the `datetime` module can set the system time. Here are some ways to do it in [Windows](http://stackoverflow.com/questions/3281254/setting-the-system-date-in-python-on-windows) and [Linux](http://stackoverflow.com/questions/2193964/set-the-hardware-clock-in-python). – Ben Hoyt Aug 22 '12 at 21:42
  • @benhoyt : Correct... datetime is not useful in this case.... Thanks for these two solutions.. but I need one combine solution for both the platforms. So this is an inadequate to my requirement.. – Abhishek Kulkarni Aug 22 '12 at 21:46
  • @AbhishekKulkarni: Why don't you just make a function that does that? You have the two solutions for Windows and Linux. – Blender Aug 22 '12 at 22:45
  • @Blender - I wonder there is no such module for system clock manipulation... I was looking for a module. – Abhishek Kulkarni Aug 23 '12 at 09:54
  • @AbhishekKulkarni: Modules are just folders with Python files inside of them. It's easy to make one. – Blender Aug 23 '12 at 14:31
  • 1
    @Blender: Thanks for educating me again :-) I didn't want to waste time in writing my own if anything available ready to use.. – Abhishek Kulkarni Aug 24 '12 at 11:20

6 Answers6

37
import sys
import datetime

time_tuple = ( 2012, # Year
                  9, # Month
                  6, # Day
                  0, # Hour
                 38, # Minute
                  0, # Second
                  0, # Millisecond
              )

def _win_set_time(time_tuple):
    import pywin32
    # http://timgolden.me.uk/pywin32-docs/win32api__SetSystemTime_meth.html
    # pywin32.SetSystemTime(year, month , dayOfWeek , day , hour , minute , second , millseconds )
    dayOfWeek = datetime.datetime(time_tuple).isocalendar()[2]
    pywin32.SetSystemTime( time_tuple[:2] + (dayOfWeek,) + time_tuple[2:])


def _linux_set_time(time_tuple):
    import ctypes
    import ctypes.util
    import time

    # /usr/include/linux/time.h:
    #
    # define CLOCK_REALTIME                     0
    CLOCK_REALTIME = 0

    # /usr/include/time.h
    #
    # struct timespec
    #  {
    #    __time_t tv_sec;            /* Seconds.  */
    #    long int tv_nsec;           /* Nanoseconds.  */
    #  };
    class timespec(ctypes.Structure):
        _fields_ = [("tv_sec", ctypes.c_long),
                    ("tv_nsec", ctypes.c_long)]

    librt = ctypes.CDLL(ctypes.util.find_library("rt"))

    ts = timespec()
    ts.tv_sec = int( time.mktime( datetime.datetime( *time_tuple[:6]).timetuple() ) )
    ts.tv_nsec = time_tuple[6] * 1000000 # Millisecond to nanosecond

    # http://linux.die.net/man/3/clock_settime
    librt.clock_settime(CLOCK_REALTIME, ctypes.byref(ts))


if sys.platform=='linux2':
    _linux_set_time(time_tuple)

elif  sys.platform=='win32':
    _win_set_time(time_tuple)

I don't have a windows machine so I didn't test it on windows... But you get the idea.

tMC
  • 18,105
  • 14
  • 62
  • 98
  • 4
    Use `if sys.platform.startswith('linux')` to work with Python >= 3.3. – Craig McQueen Jun 20 '17 at 05:53
  • 1
    Per @jk987's answer, the correct module name is `win32api`, not `pywin32`. Also, `SetSystemTime` takes 8 arguments; you could add the `*`, or you could do `remote_time = datetime.datetime.fromtimestamp(response.tx_time) + datetime.datetime.utcnow() - datetime.datetime.now()` and then `win32api.SetSystemTime(remote_time.year, remote_time.month, remote_time.isoweekday(), remote_time.day, remote_time.hour, remote_time.minute, remote_time.second, remote_time.microsecond // 1000)`. – wecsam Aug 07 '17 at 13:08
  • Well done! But datetime.datetime() accepts microseconds, not milliseconds, so you should multiply by 1000 not 1000000 in order to convert them to nanoseconds. – daghemo Mar 28 '20 at 10:26
  • 1
    Note that as of python 3.3, the linux part of this code can be simplified using the python [time.clock_settime()](https://docs.python.org/3/library/time.html#time.clock_settime) function, instead of using ctypes to call into the system library. – pavon Feb 26 '21 at 02:08
9

The tMC's answer seems great. However, it was not working for me properly. I figured out it needed some updates, for both Linux and Windows + python 3. Here is my updated module:

import sys
from _datetime import datetime

time_tuple = (2012,  # Year
              9,  # Month
              6,  # Day
              0,  # Hour
              38,  # Minute
              0,  # Second
              0,  # Millisecond
              )


def _win_set_time(time_tuple):
    import win32api
    dayOfWeek = datetime(*time_tuple).isocalendar()[2]
    t = time_tuple[:2] + (dayOfWeek,) + time_tuple[2:]
    win32api.SetSystemTime(*t)


def _linux_set_time(time_tuple):
    import subprocess
    import shlex

    time_string = datetime(*time_tuple).isoformat()

    subprocess.call(shlex.split("timedatectl set-ntp false"))  # May be necessary
    subprocess.call(shlex.split("sudo date -s '%s'" % time_string))
    subprocess.call(shlex.split("sudo hwclock -w"))


if sys.platform == 'linux2' or sys.platform == 'linux':
    _linux_set_time(time_tuple)

elif sys.platform == 'win32':
    _win_set_time(time_tuple)

For Linux read the following answer: Set the hardware clock in Python?

Amir
  • 1,722
  • 22
  • 20
  • As said above, datetime.datetime() seems to accept microseconds, not milliseconds, so you should multiply by 1000 not 1000000 in order to convert them to nanoseconds. – daghemo Mar 28 '20 at 10:28
  • @daghemo What do you mean? Where I am multiplying? – Amir Mar 30 '20 at 19:05
  • please forget about me: maybe social distancing for CODIV-19 here in Italy is driving me crazy! :( You seems to use datetime.isoformat() to convert the date and pass it to the date command, so no multiplication here (it was the example above that used it). Just keep in mind that the last 0 in your time_tuple stays for microseconds, not milliseconds. That's all. Not a real error. :) – daghemo Apr 01 '20 at 08:08
  • 3
    You are executing system commands while OP asked explicitly to avoid that. – drakorg Nov 27 '20 at 20:13
  • You can use `timedatectl` command. This command updates both the system time and the hardware clock. The result it is similar to using both the `date --set` and `hwclock --systohc` commands. – Michael Kazarian Apr 25 '23 at 14:05
6

I had to modify win32 version of tMC's answer little bit:

def _win_set_time(time_tuple):
    import win32api
    dayOfWeek = datetime(*time_tuple).isocalendar()[2]
    t = time_tuple[:2] + (dayOfWeek,) + time_tuple[2:]
    win32api.SetSystemTime(*t)

Eg. when I use it to set time according to old time server (Time protocol, RFC868) I'm doing it aproximately this way:

data = s.recv(4)
remote_time = (ord(data[0])<<24) + (ord(data[1])<<16) + (ord(data[2])<<8) + ord(data[3])
remote_time -= 2208988800
_win_set_time(time.gmtime(remote_time)[0:6] + (0,))
jk987
  • 61
  • 1
  • 1
  • 2
    misses `from datetime import datetime` otherwise working after installing pywin32 – 576i Nov 11 '16 at 19:20
1

This works for me

  • Convert automatically to UTC Time
  • convert string to datetime
  • use date time input
  • it is more readable
def _win_set_time(datetime_obj: datetime):  
    import win32api  
    # http://timgolden.me.uk/pywin32-docs/win32api__SetSystemTime_meth.html  
    # win32api.SetSystemTime(year, month , dayOfWeek , day , hour , minute , second , millisecond )  
    utc_datetime = datetime_obj.astimezone().astimezone(timezone.utc).replace(tzinfo=None)
    day_of_week = utc_datetime.isocalendar()[2]
    win32api.SetSystemTime(utc_datetime.year, utc_datetime.month, day_of_week, 
    utc_datetime.day, utc_datetime.hour, utc_datetime.minute, utc_datetime.second,
    int(utc_datetime.microsecond / 1000))
    
real_time_str = "2020 12 24 13 11 10 321"  
real_time = datetime.strptime(real_time_str, '%Y %m %d %H %M %S %f')  
_win_set_time(real_time)  
0

I have a ds1302 RTC which raspberry pi does not recognize as RTC So i needed to start every session by setting the right time to the system

here is my solution

import subprocess # for calling a program on ystem (sudo...)
import shlex

def SetClock():
    # our clock ds1032 does not work as RTC in here so we set it to the system every time we start
    rtc = pyRPiRTC.DS1302() #https://github.com/sourceperl/rpi.rtc
    dt = rtc.read_datetime()
    print("RTC Time is:", dt)
    st = dt.strftime('%Y-%m-%d %H:%M:%S')
    # mt = time.strptime(st, '%Y-%m-%d %H:%M:%S') # this is how to convert datetime to time
    s = "sudo date -s '" + st + "'"
    subprocess.call(shlex.split(s))
    print("system time:", time.ctime()) 
Oferb
  • 1
-1

well for now i think this is the best solution

import sys,os
def change(s):
    if s == 1:os.system('date -s "2 OCT 2006 18:00:00"')#don't forget to change it , i've used date command for linux 
    elif s == 2:
        try:
          import pywin32
        except ImportError:
          print 'pywin32 module is missing'
          sys.exit(1)
        pywin32.SetSystemTime(year, month , dayOfWeek , day , hour , minute , second , millseconds )# fill all Parameters with int numbers
    else:print 'wrong param'
def check_os():
    if sys.platform=='linux2':change(1)
    elif  sys.platform=='win32':change(2)
    else:print 'unknown system'

for now it's temporary solution , hopefully it's helpful , aslo take a look

http://timgolden.me.uk/pywin32-docs/win32api__SetSystemTime_meth.html

http://timgolden.me.uk/pywin32-docs/win32api__SetLocalTime_meth.html

Hamoudaq
  • 1,490
  • 4
  • 23
  • 42
  • 2
    This does of course create a subprocess to run the `date` system command if the system is linux. Something the OP doesn't want to do. – tMC Sep 06 '12 at 05:56