关键词:libfaketime、时间模拟

在测试时间相关逻辑(证书过期、订单超时、定时任务、跨年问题)时,很多人第一反应是:能不能只改一个程序/进程的时间,而不影响宿主机?

结论很明确:

不能直接改。

在容器/Pod 环境中也存在同样的需求,甚至需求更强烈,因为 Kubernetes 中,宿主机往往不会只运行一个业务程序,修改系统时间所造成的影响可能是无法预估的。

Linux 内核只有一份系统时间,容器共享内核,不存在所谓的 Time Namespace

于是,libfaketime 成为了容器环境中最常被提及的“替代方案”。本文将结合主流编程语言获取时间的 Demo 来验证 libfaketime 的实际效果:

  • libfaketime 能做什么、不能做什么
  • 为什么有的语言生效、有的完全无效
  • Java 为什么必须额外配置

libfaketime 是什么,它解决了什么问题

libfaketime 并不是修改系统时间,而是:

通过 LD_PRELOAD 劫持部分 libc 的时间函数,让进程“认为”时间发生了变化

典型被劫持的函数包括:

  • time()
  • gettimeofday()
  • clock_gettime(CLOCK_REALTIME)

这意味着:

  • 宿主机时间 不会变
  • 其他容器 不受影响
  • 只对当前进程有效

这正好契合了容器 / Pod 级别的测试需求。


环境准备

export LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1
export FAKETIME="2026-01-01 00:00:00" # FAKETIME 还支持很多模拟时间的表达式,请参考 https://github.com/wolfcw/libfaketime

在 Java 应用中,必须额外加一句

export FAKETIME_DONT_FAKE_MONOTONIC=1

后文会详细解释原因。


主流编程语言行为对照

1. Shell

date

输出结果:

Thu Jan  1 00:00:00 CST 2026

✅ 生效


2. C 语言

#include <stdio.h>
#include <time.h>
#include <sys/time.h>

int main() {
    time_t t = time(NULL);
    struct timeval tv;
    gettimeofday(&tv, NULL);

    struct tm *localTime;
    localTime = localtime(&t);
    printf("time():        %04d-%02d-%02d %02d:%02d:%02d\n",
           localTime->tm_year + 1900,
           localTime->tm_mon + 1,
           localTime->tm_mday,
           localTime->tm_hour,
           localTime->tm_min, 
           localTime->tm_sec);
    localTime = localtime(&tv.tv_sec);
    printf("gettimeofday():%04d-%02d-%02d %02d:%02d:%02d\n",
           localTime->tm_year + 1900,
           localTime->tm_mon + 1,
           localTime->tm_mday,
           localTime->tm_hour,
           localTime->tm_min,
           localTime->tm_sec);
    return 0;
}

输出结果:

time():        2026-01-01 00:00:00
gettimeofday():2026-01-01 00:00:00

✅ 生效


3. Python

import time
from datetime import datetime

time_time = time.time()
datetime_now = datetime.now()

print("time.time():", time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time_time)))
print("datetime.now():", datetime_now.strftime("%Y-%m-%d %H:%M:%S"))

输出结果:

time.time():    2026-01-01 00:00:00
datetime.now(): 2026-01-01 00:00:00

✅ 生效


4. Node.js

function formatWithNative(date = new Date()) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');
  
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

console.log(formatWithNative());

输出结果:

2026-01-01 00:00:00

✅ 生效


5. Java

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

public class Demo {
    public static void main(String[] args) {
        System.out.println("System.currentTimeMillis():    " +
            System.currentTimeMillis());

        System.out.println("Instant.now():                 " +
            Instant.now());

        System.out.println("LocalDateTime.now():           " +
            LocalDateTime.now());

        System.out.println("LocalDateTime.systemDefault(): " +
            LocalDateTime.now(ZoneId.systemDefault()));
    }
}

输出结果:

System.currentTimeMillis(): 1767196800000
Instant.now(): 2025-12-31T16:00:00Z
LocalDateTime.now(): 2026-01-01T00:00
LocalDateTime.systemDefault(): 2026-01-01T00:00

✅ 生效


6. Go

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("time.Now():", time.Now())
    fmt.Println("Unix:     ", time.Now().Unix())
}

输出结果:

time.Now(): 2026-01-14 02:01:21.162059381 +0000 UTC m=+0.000047738
Unix:       1768356081

❌ 不生效


行为汇总表

语言faketime 生效
Shell / C
Python / Node
Java
Go

为什么 Java 需要 FAKETIME_DONT_FAKE_MONOTONIC=1

1. Java 有两种“时间”

  • 墙上时间(Wall Clock)

    • System.currentTimeMillis()
    • 可回拨,用于业务时间
  • 单调时间(Monotonic Clock)

    • System.nanoTime()
    • 只递增,用于:

      • Thread.sleep
      • 锁 / 条件变量
      • 定时器
      • GC / JIT

2. libfaketime 的默认行为

默认情况下,libfaketime 会连单调时间一起 fake

这会直接破坏 JVM 的基本假设,导致应用卡死。

3. 正确做法

export FAKETIME_DONT_FAKE_MONOTONIC=1

结语

本文只针对简单的场景进行了测试,测试结果表明 C、Python、NodeJS、Java 等语言都可以使用 libfaketime 来修改/模拟时间,Go 语言因为其静态编译的特性,不依赖 libc,因此不支持用 libfaketime 来修改/模拟时间。

标签: none

添加新评论