【问题标题】:How can I set the System Time in Java?如何在 Java 中设置系统时间?
【发布时间】:2011-09-06 10:14:39
【问题描述】:

是否可以在 Java 中更改系统时间?

它应该在 Windows 和 Linux 下运行。我用Runtime Class 试过了,但是权限有问题。

这是我的代码:

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 服务器获取时间并设置它。

【问题讨论】:

  • 为什么需要设置系统时间?
  • 还包括当前的相关代码和遇到的错误/问题(例如权限“错误”是什么?)
  • @SSZero 因为系统时间不正确/不准确?
  • 您需要在 Windows 上使用单独的时间命令。 Runtime.getRuntime().exec("cmd /C 时间" + strTimeToSet); // hh:mm:ss

标签: java systemtime


【解决方案1】:

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();
        }
    }
    

    对于测试,您可以用其他时间返回的内容覆盖 INSTANCE。添加更多方法来创建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 );
    }
    

【讨论】:

  • 安装具有java.lang.* 名称的类加载器总是有效吗?
  • 是的,就类加载而言,java.lang 并没有什么神奇之处。就 JIT 和 VM 而言,应该没有什么神奇之处 - 如果您将 String.class 替换为其他内容,这应该可以工作,因为该类的加载方式与其他任何内容一样。
  • 我正在测试方法号。 2并得到:java.lang.SecurityException:禁止的包名称:java.lang所以我认为java.lang实际上有一些“神奇”。
  • @BoneGoat:不,没有。但是你已经安装了SecurityManager,然后,这样的事情就不再可能了。请改用joda-time
【解决方案2】:

一种方法是使用本机命令。

对于 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]

9 年后更新

这不是设置系统时间的好方法,而不是使用java.util.Clock 获取当前时间并在需要的地方提供模拟实现来伪造时间。

【讨论】:

  • @Pacerier 不确定Mac中是否有不同的命令/实用程序,您可以在Mac的终端上试用一下
  • @JigarJoshi 问题是我没有 Mac
  • (在 Windows 上)顺便说一句 date 只是改变日期,我们还需要执行命令 time 来改变系统时间。
  • @msanjay 寻找设置时间的命令,我猜 date 应该这样做
  • exec() 为每个调用创建一个新进程,我建议像这样连接两个 Windows 调用:Runtime.getRuntime().exec("cmd /C date " + strDateToSet + "&amp; time " + strTimeToSet);
【解决方案3】:

您只能通过以 root 或管理员身份运行命令行工具来设置系统时间。命令不同,但您可以先检查操作系统,然后针对该操作系统运行相应的命令。

【讨论】:

    【解决方案4】:

    您可以使用JNI 设置系统时间。这适用于 Windows。你需要知道JNIC

    这是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);
        }
    }
    

    【讨论】:

      【解决方案5】:

      在某些情况下,进程没有以管理员权限运行,但它仍然具有设置系统时间的权限。可以使用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);
          }       
      }
      

      【讨论】:

        【解决方案6】:
        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管理员模式下运行此代码。

        【讨论】:

          【解决方案7】:
          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();
                  }
              }
          }
          

          【讨论】:

            【解决方案8】:

            您可以使用以下代码更改日期或在当前日期添加日期。它在 Windows 中完美运行:

            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.DATE, 1);
            SimpleDateFormat s = new SimpleDateFormat("MM-dd-yyyy");    
            String strExpectedDate = s.format(new Date(cal.getTimeInMillis()));
            Runtime rt = Runtime.getRuntime();
            rt.exec("cmd /C date " + strExpectedDate);
            

            在上面的代码中,我在当前日期添加了 1 天,您可以为 strExpectedDate 传递任何日期,这仅适用于 Windows

            【讨论】:

              【解决方案9】:

              我用快捷方式做到了。快捷方式通过命令链接到cmd,更改时间或将时间与服务器时间同步。由于更改系统设置快捷方式应在管理员权限下调用,因此有一种方法自动设置快捷方式标志Run as administrator。为了确保同步成功,有一种方法safeSynchronize 将时间更改为假时间,然后才询问服务器时间。对我来说效果很好。

              package system;
              
              import mslinks.ShellLink;
              import org.apache.commons.io.FileUtils;
              import org.apache.commons.lang3.time.DateUtils;
              
              import java.io.*;
              import java.nio.file.Files;
              import java.text.DateFormat;
              import java.text.SimpleDateFormat;
              import java.time.Instant;
              import java.util.Calendar;
              import java.util.Date;
              import java.util.Random;
              
              public class TimeSynchronizer {
              
                  Random random = new Random();
              
                  private int WAIT_LAG = 1000;
              
                  private DateFormat dateFormat = new SimpleDateFormat("dd-MM-yy");
                  private DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
              
                  public void synchronize() throws IOException, InterruptedException {
                      File file = getFile();
                      makeShortcut(file, "/c net start w32time");
                      callShortcut(file);
                      makeShortcut(file, "/c w32tm /resync");
                      callShortcut(file);
                      if (file.exists()) file.delete();
                  }
              
                  public void safeSynchronize() throws IOException, InterruptedException {
                      Calendar rightNow = Calendar.getInstance();
                      int minute = rightNow.get(Calendar.MINUTE);
                      boolean isForward = minute < 30;
                      Date date = DateUtils.addMinutes(Date.from(Instant.now()), 10 * (isForward ? 1 : -1));
              
                      setTime(date);
                      synchronize();
                  }
              
                  public void setTime(Date date) throws IOException, InterruptedException {
                      setTime(date, false);
                  }
              
                  public void setTime(Date date, boolean withDate) throws IOException, InterruptedException {
                      File file = getFile();
                      if (withDate) {
                          makeShortcut(file, "/c date " + dateFormat.format(date));
                          callShortcut(file);
                      }
                      makeShortcut(file, "/c time " + timeFormat.format(date));
                      callShortcut(file);
                      if (file.exists()) file.delete();
                  }
              
                  private void callShortcut(File file) throws IOException, InterruptedException {
                      Process process = Runtime.getRuntime().exec(
                          getSystem32Path() + "\\cmd.exe /c start /wait \"\" \"" + file.getAbsolutePath() + "\""
                      );
                      process.waitFor();
                      process.exitValue();
                  }
              
                  private String getSystem32Path() {
                      return System.getenv("SystemRoot") + "\\system32";
                  }
              
                  private File getFile() {
                      return new File(random.nextInt() + "shortcut.lnk");
                  }
              
                  private void makeShortcut(File file, String command) throws IOException, InterruptedException {
                      String system32Path = getSystem32Path();
                      ShellLink s = new ShellLink();
                      s.setTarget(system32Path + "\\cmd.exe");
                      s.setCMDArgs(command);
                      s.saveTo(file.getAbsolutePath());
                      Thread.sleep(WAIT_LAG);
              
                      setRunAsAdmin(file);
                  }
              
                  private void setRunAsAdmin(File file) throws IOException {
                      byte[] fileContent = Files.readAllBytes(file.toPath());
                      fileContent[21] = (char)32;
                      FileUtils.writeByteArrayToFile(file, fileContent);
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2017-05-27
                • 1970-01-01
                • 2013-05-07
                • 2016-02-11
                • 1970-01-01
                • 2010-11-28
                • 2012-01-24
                相关资源
                最近更新 更多