Как установить системное время на Java?


Можно ли изменить системное время в Java?

Он должен работать под управлением Windows и Linux. Я пробовал это с классом Runtime, но есть проблема с разрешениями.

Вот мой код:

String cmd="date -s ""+datetime.format(ntp_obj.getDest_Time())+""";
try {
    Runtime.getRuntime().exec(cmd);
} catch (IOException e1) {
// TODO Auto-generated catch block
  e1.printStackTrace();
}
System.out.println(cmd);

Выход cmd таков:

date -s "06/01/2011 17:59:01"
Но системное время такое же, как и раньше.

Я установлю время, потому что я пишу NTP-клиент, и там я получаю время от NTP-сервера и установлю его.

7 17

7 ответов:

Java не имеет API для этого.

Большинство системных команд для этого требуют прав администратора, поэтому Runtime не может помочь, если вы не запустите весь процесс от имени администратора / root или не используете runas/sudo.

В зависимости от того, что вам нужно, вы можете заменить System.currentTimeMillis(). Существует два подхода к этому:

  1. Замените все вызовы System.currentTimeMillis() вызовом собственного статического метода, который вы можете заменить:

    public class SysTime {
        public static SysTime INSTANCE = new SysTime();
    
        public long now() {
            return System.currentTimeMillis();
        }
    }
    

    Для тестов можно перезаписать экземпляр с помощью что возвращает другой раз. Добавьте дополнительные методы для создания Date и подобных объектов.

  2. Если не весь код находится под вашим контролем, установите ClassLoader, который возвращает другую реализацию для System. Это гораздо проще, чем вы думаете:

    @Override
    public Class<?> loadClass( String name, boolean resolve ) {
        if ( "java.lang.System".equals( name ) ) {
            return SystemWithDifferentTime.class;
        }
    
        return super.loadClass( name, resolve );
    }
    

Одним из способов было бы использование собственных команд.

Для Windows требуются две команды (дата и время):

Runtime.getRuntime().exec("cmd /C date " + strDateToSet); // dd-MM-yy
Runtime.getRuntime().exec("cmd /C time " + strTimeToSet); // hh:mm:ss

Для linux одна команда обрабатывает как дату, так и время:

Runtime.getRuntime().exec("date -s " + strDateTimeToSet); // MMddhhmm[[yy]yy]

Вы можете установить системное время, только запустив средство командной строки от имени root или Adminstrator. Команды разные, но вы можете сначала проверить ОС и выполнить соответствующую команду для этой ОС.

Вы можете использовать JNI для установки системного времени. Это будет работать на Windows. Вам нужно знать JNI и C.

Это функция JNI, прототип будет сгенерирован утилитой javah

JNIEXPORT void JNICALL Java_TimeSetter_setSystemTime
  (JNIEnv *env, jobject obj, jshort hour, jshort minutes) {

    SYSTEMTIME st;
    GetLocalTime(&st);  
    st.wHour = hour;      
    st.wMinute = minutes;  
    SetLocalTime(&st);   
}

Оболочка Java JNI будет

class TimeSetter {

    public native void setSystemTime( short hour, short minutes);

    static {
        System.loadLibrary("TimeSetter");
    }
}

И, наконец, использовать его

public class JNITimeSetter {

    public static void main(String[] args) {

        short hour = 8;
        short minutes = 30;

        // Set the system at 8h 30m

        TimeSetter ts = new TimeSetter();
        ts.setSystemTime(hour, minutes);
    }
}

Бывают случаи, когда процесс не запускается с правами администратора, но у него все еще есть разрешения на установку системного времени. Можно использовать Java Native Access для изменения системного времени и иметь все необходимые источники в Java (проще по сравнению с JNI).

package github.jna;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinBase.SYSTEMTIME;
import com.sun.jna.win32.StdCallLibrary;

/**
 * Provides access to the Windows SetSystemTime native API call.
 * This class is based on examples found in
 * <a href="https://github.com/twall/jna/blob/master/www/GettingStarted.md">JNA Getting Started</a>
 */
public class WindowsSetSystemTime {

    /**
     * Kernel32 DLL Interface.
     * kernel32.dll uses the __stdcall calling convention (check the function 
     * declaration for "WINAPI" or "PASCAL"), so extend StdCallLibrary
     * Most C libraries will just extend com.sun.jna.Library,
     */
    public interface Kernel32 extends StdCallLibrary {

        boolean SetLocalTime(SYSTEMTIME st);

        Kernel32 instance = (Kernel32) Native.loadLibrary("kernel32.dll", Kernel32.class);

    }

    public boolean SetLocalTime(SYSTEMTIME st) {
        return Kernel32.instance.SetLocalTime(st);
    }

    public boolean SetLocalTime(short wYear, short wMonth, short wDay, short wHour, short wMinute, short wSecond) {
        SYSTEMTIME st = new SYSTEMTIME();
        st.wYear = wYear;
        st.wMonth = wMonth;
        st.wDay = wDay;
        st.wHour = wHour;
        st.wMinute = wMinute;
        st.wSecond = wSecond;
        return SetLocalTime(st);
    }       
}
package myTestProject;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class LocalTimeChangeTest {

    private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        try {
            String value = "2014-12-12 00:26:14";
            Date date = dateFormat.parse(value);
            value = dateFormat.format(date);
            final Process dateProcess = Runtime.getRuntime().exec("cmd /c date "+value.substring(0, value.lastIndexOf(' ')));
            dateProcess.waitFor();
            dateProcess.exitValue();
            final Process timeProcess = Runtime.getRuntime().exec("cmd /c time "+value.substring(value.lastIndexOf(' ')+1));
            timeProcess.waitFor();
            timeProcess.exitValue();
        } catch (Exception exception) {
            throw new RuntimeException(exception);
        }
    }
}

Запустите этот код в модели администратора windows.

package com.test;

public class Exec {

    public static void main(String[] args) {
        try {
            String[] cmd = {"/bin/bash","-c","echo yourPassword | sudo -S date --set='2017-05-13 21:59:10'"};
            Runtime.getRuntime().exec(cmd);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}