class TestCase::Sys::Time {
  use Sys::Time;
  use Sys::Time::Timeval;
  use Sys::Time::Timezone;
  use Sys::Time::Timespec;
  use Sys::Time::Constant as TIME;
  use Sys::Time::Itimerval;
  use Sys::Time::Utimbuf;
  
  static method new_tm : int () {
    my $tm = Sys::Time::Tm->new;
    
    unless ($tm isa Sys::Time::Tm) {
      return 0;
    }
    
    return 1;
  }
  
  static method time : long () {
    my $time = Sys::Time->time;
    
    return $time;
  }
  
  static method localtime : int () {
    {
      my $tm = Sys->localtime(0);
      
      unless ($tm->tm_year + 1900 == 1970) {
        return 0;
      }
    }
    
    {
      my $tm = Sys->localtime;
      
      unless ($tm->tm_year + 1900 > 1970) {
        return 0;
      }
    }
    
    unless (Sys::OS->is_windows) {
      my $tm = Sys->localtime(-(86_400 * 2));
      
      unless ($tm->tm_year + 1900 == 1969) {
        return 0;
      }
    }
    
    # with TZ
    {
      
      # Get the current epoch time and fix it to avoid a 1-second deviation
      my $now = Sys->time;
      
      # 1. Test for JST (Japan Standard Time: UTC+9)
      {
        # Set C<TZ> environment variable to JST-9
        Sys->set_env("TZ", "JST-9");
        
        # Get localtime (JST) and gmtime (UTC) using the same epoch time
        my $lt_jst = Sys->localtime($now);
        my $gt = Sys->gmtime($now);
        
        # Calculate the difference in seconds within a day
        my $jst_seconds = $lt_jst->tm_hour * 3600 + $lt_jst->tm_min * 60 + $lt_jst->tm_sec;
        my $gm_seconds = $gt->tm_hour * 3600 + $gt->tm_min * 60 + $gt->tm_sec;
        
        my $diff = $jst_seconds - $gm_seconds;
        
        # Adjust for cases where the date changes
        if ($diff < 0) {
          $diff += 86400;
        }
        
        # Check if the difference is exactly 9 hours (32400 seconds)
        unless ($diff == 32400) {
          return 0;
        }
      }
      
      # 2. Test for UTC (UTC+0)
      {
        # Set C<TZ> environment variable to UTC0
        Sys->set_env("TZ", "UTC0");
        
        my $lt_utc = Sys->localtime($now);
        my $gt = Sys->gmtime($now);
        
        # Check if localtime matches gmtime under UTC0
        unless ($lt_utc->tm_hour == $gt->tm_hour && $lt_utc->tm_min == $gt->tm_min && $lt_utc->tm_sec == $gt->tm_sec) {
          return 0;
        }
      }
      
    }
    
    return 1;
  }
  
  static method localtime_value : Sys::Time::Tm ($time : long) {
    my $time_info = Sys::Time->localtime(\$time);
    
    return $time_info;
  }
  
  static method gmtime : int () {
    {
      my $tm = Sys->gmtime(0);
      
      unless ($tm->tm_year + 1900 == 1970) {
        return 0;
      }
    }
    
    {
      my $tm = Sys->gmtime;
      
      unless ($tm->tm_year + 1900 > 1970) {
        return 0;
      }
    }
    
    unless (Sys::OS->is_windows) {
      my $tm = Sys->gmtime(-(86_400 * 2));
      
      unless ($tm->tm_year + 1900 == 1969) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method gmtime_value : Sys::Time::Tm ($time : long) {
    my $time_info = Sys::Time->gmtime(\$time);
    
    return $time_info;
  }
  
  static method gettimeofday : int () {
    
    my $tv = Sys::Time::Timeval->new;
    my $tz = Sys::Time::Timezone->new;
    
    my $status = Sys::Time->gettimeofday($tv, $tz);
    
    unless ($status == 0) {
      return 0;
    }
    
    unless ($tv->tv_sec > 0) {
      return 0;
    }
    
    return 1;
  }

  static method clock : int () {
    
    my $tv = Sys::Time::Timeval->new;
    my $tz = Sys::Time::Timezone->new;
    
    my $cpu_time = Sys::Time->clock;
    
    unless ($cpu_time >= 0) {
      return 0;
    }
    
    return 1;
  }
  
  static method clock_gettime : int () {
    
    my $tp = Sys::Time::Timespec->new;
    
    my $status = Sys::Time->clock_gettime(TIME->CLOCK_REALTIME, $tp);
    
    unless ($status >= 0) {
      return 0;
    }
    
    unless ($tp->tv_sec >= 0) {
      return 0;
    }
    
    return 1;
  }
  
  static method clock_getres : int () {
    
    my $tp = Sys::Time::Timespec->new;
    
    my $status = Sys::Time->clock_getres(TIME->CLOCK_REALTIME, $tp);
    
    unless ($status >= 0) {
      return 0;
    }
    
    unless ($tp->tv_sec >= 0) {
      return 0;
    }
    
    return 1;
  }


  static method setitimer : int () {
    
    my $new_tv = Sys::Time::Timeval->new(1, 1);
    
    my $new_value = Sys::Time::Itimerval->new;
    $new_value->set_it_interval($new_tv);
    
    my $old_value = Sys::Time::Itimerval->new;
    
    my $status = Sys::Time->setitimer(TIME->ITIMER_REAL, $new_value, $old_value);
    
    unless ($status == 0) {
      return 0;
    }
    
    return 1;
  }
  
  static method getitimer : int () {
    
    my $curr_value = Sys::Time::Itimerval->new;
    
    my $status = Sys::Time->getitimer(TIME->ITIMER_REAL, $curr_value);
    
    unless ($status == 0) {
      return 0;
    }
    
    return 1;
  }

  static method times : int () {
    
    my $buffer = Sys::Time::Tms->new;
    
    my $tick = Sys::Time->times($buffer);
    
    warn $tick;
    
    unless ($tick >= 0) {
      return 0;
    }

    warn $buffer->tms_utime;
    
    unless ($buffer->tms_utime > 0) {
      return 0;
    }
    
    warn $buffer->tms_stime;
    
    unless ($buffer->tms_stime >= 0) {
      return 0;
    }
    
    warn $buffer->tms_cutime;
    
    unless ($buffer->tms_cutime >= 0) {
      return 0;
    }
    
    warn $buffer->tms_cstime;
    
    unless ($buffer->tms_cstime >= 0) {
      return 0;
    }
    
    # The fields of the Sys::Time::Tms class
    {
      $buffer->set_tms_utime(0);
      $buffer->set_tms_stime(0);
      $buffer->set_tms_cutime(0);
      $buffer->set_tms_cstime(0);
    }
    
    return 1;
  }


  static method clock_nanosleep : int () {
    
    my $start = Sys::Time->time;
    
    my $request = Sys::Time::Timespec->new;
    $request->set_tv_sec(1);
    $request->set_tv_nsec(100_000_000);

    my $remain = Sys::Time::Timespec->new;
    
    my $status = Sys::Time->clock_nanosleep(TIME->CLOCK_REALTIME, 0, $request, $remain);
    
    unless ($status  == 0) {
      return 0;
    }
    
    unless (Sys::Time->time - $start >= 1) {
      return 0;
    }
    
    return 1;
  }

  static method nanosleep : int () {
    
    my $start = Sys::Time->time;
    
    my $ts = Sys::Time::Timespec->new(1, 100_000_000);
    
    my $status = Sys::Time->nanosleep($ts, undef);
    
    unless ($status  == 0) {
      return 0;
    }
    
    unless (Sys::Time->time - $start >= 1) {
      return 0;
    }
    
    return 1;
  }
  
  static method utime : int ($test_dir : string) {
    
    my $file = "$test_dir/ftest/utime.txt";
    
    my $utimbuf = Sys::Time::Utimbuf->new;
    
    my $time = Sys::Time->time;
    
    my $actime = $utimbuf->set_actime($time);
    my $modtime = $utimbuf->set_modtime($time);
    
    my $status = Sys::Time->utime($file, $utimbuf);
    
    unless ($status == 0) {
      return 0;
    }
    
    return 1;
  }
  
  static method utimes : int ($test_dir : string) {
    
    if (Sys::OS->is_windows) {
      eval { Sys::Time->utimes(undef, undef); }
      unless ($@ && eval_error_id is_error Error::NotSupported) {
        return 0;
      }
    }
    else {
      my $file = "$test_dir/ftest/utime.txt";
      
      my $times0 = Sys::Time::Timeval->new;
      Sys::Time->gettimeofday($times0, undef);
      
      my $times1 = Sys::Time::Timeval->new;
      Sys::Time->gettimeofday($times1, undef);
      
      my $times = [$times0, $times1];
      
      my $status = Sys::Time->utimes($file, $times);
      
      unless ($status == 0) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method timespec : int () {
    
    # new
    {
      # check
      {
        
        # Valid values
        eval { Sys::Time::Timespec->new(100L, 500L); };
        if ($@) { return 0; }
        
        # Invalid tv_nsec (too small)
        eval { Sys::Time::Timespec->new(100L, -1L); };
        unless ($@) { return 0; }
        
        # Invalid tv_nsec (too large)
        eval { Sys::Time::Timespec->new(100L, 1_000_000_000L); };
        unless ($@) { return 0; }
      }
    }
    
    # clone
    {
      # 1. Basic clone test
      {
        my $ts = Sys::Time::Timespec->new;
        $ts->set_tv_sec(123456789L);
        $ts->set_tv_nsec(987654321L);
        
        # Perform clone
        my $ts_clone = $ts->clone;
        
        # Check if field values are identical
        unless ($ts_clone->tv_sec == $ts->tv_sec) {
          return 0;
        }
        unless ($ts_clone->tv_nsec == $ts->tv_nsec) {
          return 0;
        }
        
        # Check if it's a different object (not the same memory address)
        # In SPVM, comparing objects with == checks identity
        if ($ts_clone == $ts) {
          return 0;
        }
      }
      
      # 2. Independence test (Modifying original should not affect clone)
      {
        my $ts = Sys::Time::Timespec->new;
        $ts->set_tv_sec(100L);
        
        my $ts_clone = $ts->clone;
        
        # Modify original
        $ts->set_tv_sec(200L);
        
        # The clone's value must remain unchanged
        unless ($ts_clone->tv_sec == 100L) {
          return 0;
        }
      }
    }
    
    # check
    {
      
      # Valid values
      eval { Sys::Time::Timespec->check(100L, 500L); };
      if ($@) { return 0; }
      
      # Invalid tv_nsec (too small)
      eval { Sys::Time::Timespec->check(100L, -1L); };
      unless ($@) { return 0; }
      
      # Invalid tv_nsec (too large)
      eval { Sys::Time::Timespec->check(100L, 1_000_000_000L); };
      unless ($@) { return 0; }
    }
    
    return 1;
  }
  
  static method timeval : int () {
    
    # new
    {
      
      # Valid object creation
      {
        my $tv = Sys::Time::Timeval->new(123456789L, 999999L);
        unless ($tv->tv_sec == 123456789L) { return 0; }
        unless ($tv->tv_usec == 999999L) { return 0; }
      }
      
      # Exception on invalid arguments (verify it calls check internally)
      {
        eval { Sys::Time::Timeval->new(0L, -1L); };
        unless ($@) { return 0; }
        
        eval { Sys::Time::Timeval->new(0L, 1_000_000L); };
        unless ($@) { return 0; }
      }
      
      # Default values
      {
        my $tv = Sys::Time::Timeval->new;
        unless ($tv->tv_sec == 0L && $tv->tv_usec == 0L) { return 0; }
      }
    }
    
    # clone
    {
      # 1. Basic clone test
      {
        my $tv = Sys::Time::Timeval->new;
        $tv->set_tv_sec(123456789L);
        $tv->set_tv_usec(987654L); # Microseconds
        
        # Perform clone
        my $tv_clone = $tv->clone;
        
        # Check if field values are identical
        unless ($tv_clone->tv_sec == $tv->tv_sec) {
          return 0;
        }
        unless ($tv_clone->tv_usec == $tv->tv_usec) {
          return 0;
        }
        
        # Check if it's a different object (not the same memory address)
        if ($tv_clone == $tv) {
          return 0;
        }
      }
      
      # 2. Independence test (Modifying original should not affect clone)
      {
        my $tv = Sys::Time::Timeval->new;
        $tv->set_tv_sec(100L);
        
        my $tv_clone = $tv->clone;
        
        # Modify original
        $tv->set_tv_sec(200L);
        
        # The clone's value must remain unchanged
        unless ($tv_clone->tv_sec == 100L) {
          return 0;
        }
      }
    }
    
    # check
    {
      # Valid values
      {
        eval { Sys::Time::Timeval->check(100L, 500L); };
        if ($@) { return 0; }
      }
      
      # Invalid tv_usec (too small)
      {
        eval { Sys::Time::Timeval->check(100L, -1L); };
        unless ($@) { return 0; }
      }
      
      # Invalid tv_usec (too large)
      {
        eval { Sys::Time::Timeval->check(100L, 1_000_000L); };
        unless ($@) { return 0; }
      }
    }
    
    return 1;
  }
  
  static method tzset : int () {
    
    {
      Sys::Time->tzset;
    }
    
    return 1;
  }
}