Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File.mtime and friends return a date incorrect by an hour if created half a year ago #2404

Closed
hakanai opened this issue Jan 1, 2015 · 12 comments

Comments

@hakanai
Copy link

hakanai commented Jan 1, 2015

This is a reopen of JRUBY-7064 after verifying that it still occurs on JRuby 1.7.18. The issue appears to be specific to Windows somehow, as noted by someone commenting on the original report. It also only occurs if you live in a region with daylight savings.

Essentially, files created in a different zone offset from the current offset show the wrong time when File.mtime is used. Timestamps on files in NTFS are stored in UTC, so somewhere the wrong time zone is being applied here.

To reproduce, first you must ensure that your time zone is set to one which uses daylight savings (we use Australia/Sydney, because that's where we are.)

Create two files, one during daylight savings and one not. In my case, I just used "touch" to do this:

Z:\Data\JRuby>touch -t "201208311416" "created during standard time.txt"
Z:\Data\JRuby>touch -t "201212311416" "created during daylight time.txt"

Verify that the dates have been set correctly. You can verify this in Explorer. "dir" has an off by an hour bug as well, so you should avoid that. I used "ls" since I had it handy:

-rw-rw-rw-  1 Tester 0 0 2012-12-31 14:16 created during daylight time.txt
-rw-rw-rw-  1 Tester 0 0 2012-08-31 14:16 created during standard time.txt

When running JRuby's irb, you get the wrong time for one of the files (which one is wrong probably depends on the current date):

Z:\Data\JRuby>java -jar z:\Data\jruby-complete-1.7.18.jar -S irb
irb(main):001:0> puts File.mtime('created during standard time.txt')
2012-08-31 15:16:00 +1000
=> nil
irb(main):002:0> puts File.mtime('created during daylight time.txt')
2012-12-31 14:16:00 +1100
=> nil

Notice how it somehow got the offset correct, even though it gets the actual date wrong... that's very weird. Usually you would expect it to get either both wrong or both right. This makes it particularly hard for a script to detect that this has occurred and compensate for it. (I'm not aware of a workaround, in fact.)

MRI (2.0.0 used here, as it's what I happened to have on my Windows install) gets both right:

C:\Data\JRuby>irb
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> File.mtime('created during standard time.txt')
=> 2012-08-31 14:16:00 +1000
irb(main):003:0> File.mtime('created during daylight time.txt')
=> 2012-12-31 14:16:00 +1100

This relatively simple Java program I whipped up to check if it was Java's fault also gets them right:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.ZoneId;

public class FileTimesDemo {
    public static void main(String[] args) throws Exception {
        ZoneId zone = ZoneId.systemDefault();
        System.out.println(Files.getLastModifiedTime(Paths.get("created during standard time.txt")).toInstant().atZone(zone));
        System.out.println(Files.getLastModifiedTime(Paths.get("created during daylight time.txt")).toInstant().atZone(zone));
    }
}

Output of that:

C:\Data\JRuby>java FileTimesDemo
2012-08-31T14:16+10:00[Australia/Sydney]
2012-12-31T14:16+11:00[Australia/Sydney]
@hakanai
Copy link
Author

hakanai commented Jan 1, 2015

Narrowing down the issue, I wrote a test program only using the JNR POSIX stuff:

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

import jnr.posix.FileStat;
import jnr.posix.POSIX;
import jnr.posix.POSIXFactory;
import jnr.posix.util.DefaultPOSIXHandler;

public class TestPosixFileTimes {
    public static void main(String[] args) throws Exception {
        printMTime("C:\\Users\\Daniel\\created during standard time.txt");
        printMTime("C:\\Users\\Daniel\\created during daylight time.txt");
    }

    private static void printMTime(String filePath) {
        // Gives the wrong result:
        POSIX posix = POSIXFactory.getPOSIX();
        // Gives the right result:
        //POSIX posix = POSIXFactory.getJavaPOSIX(new DefaultPOSIXHandler());
        FileStat stat = posix.stat(filePath);
        System.out.println("stat.mtime(): " + stat.mtime());
        System.out.println(" => " + Instant.ofEpochSecond(stat.mtime()).atZone(ZoneId.systemDefault()));
    }
}

Output confirms the wrong times:

stat.mtime(): 1346390160
 => 2012-08-31T15:16+10:00[Australia/Sydney]
stat.mtime(): 1356923760
 => 2012-12-31T14:16+11:00[Australia/Sydney]

Using the pure Java POSIX code, you get the right result:

stat.mtime(): 1346386560
 => 2012-08-31T14:16+10:00[Australia/Sydney]
stat.mtime(): 1356923760
 => 2012-12-31T14:16+11:00[Australia/Sydney]

@headius
Copy link
Member

headius commented Feb 24, 2015

I was not able to reproduce this on JRuby 1.7 HEAD:

C:\Users\headius\ruby (trunk)
λ jruby -e "puts File.mtime('created during standard time.txt')"
2012-08-31 14:16:00 UTC

C:\Users\headius\ruby (trunk)
λ jruby -e "puts File.mtime('created during daylight time.txt')"
2012-12-31 14:16:00 UTC

C:\Users\headius\ruby (trunk)
λ jruby -v
jruby 1.7.20-SNAPSHOT (1.9.3p551) 2015-02-24 258be73 on Java HotSpot(TM) 64-Bit Server VM 1.8.0_31-b13 +jit [Windows 7-amd64]

I am in US Central Time, which is a daylight savings zone. Does DST need to be active for this to fail?

@hakanai
Copy link
Author

hakanai commented Feb 25, 2015

That I'm not sure. I have a vague memory of verifying it in both directions, but here I have only reported the case of doing the test during DST. I can probably retest against the same version (and trunk) the next time I have access to the Windows computer.

@hakanai
Copy link
Author

hakanai commented Feb 26, 2015

1.7.18 when clock is set to May 1 (outside DST):

C:\Users\Tester>java -jar z:\data\jruby-complete-1.7.18.jar -S irb
irb(main):001:0> puts File.mtime('created during standard time.txt')
2012-08-31 14:16:00 +1000
=> nil
irb(main):002:0> puts File.mtime('created during daylight time.txt')
2012-12-31 13:16:00 +1100
=> nil

So yep, both directions. Trying newer builds next.

@hakanai
Copy link
Author

hakanai commented Feb 26, 2015

Same on 1.7.19, except for an additional warning on the console:

C:\Users\Tester>java -jar z:\data\jruby-complete-1.7.19.jar -S irb
io/console not supported; tty will not be manipulated
irb(main):001:0> puts File.mtime('created during standard time.txt')
2012-08-31 14:16:00 +1000
=> nil
irb(main):002:0> puts File.mtime('created during daylight time.txt')
2012-12-31 13:16:00 +1100
=> nil
irb(main):003:0>

9.0.0.0.pre1 fails to start altogether:

C:\Users\Tester>java -jar z:\data\jruby-complete-9.0.0.0.pre1.jar -S irb
Errno::EPIPE: Broken pipe - uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/irb/lc/error.rb
      rewind at org/jruby/RubyIO.java:1726
        open at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/irb/magic-file.rb:13
   real_load at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/irb/locale.rb:129
        load at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/irb/locale.rb:109
  init_error at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/irb/init.rb:120
       setup at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/irb/init.rb:17
       start at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/irb.rb:378
      (root) at uri:classloader:/META-INF/jruby.home/bin/jirb:13
        load at org/jruby/RubyKernel.java:969
      (root) at file:/Z:/Data/jruby-complete-9.0.0.0.pre1.jar!/jruby/commands.rb:1
  __script__ at jirb:1

@hakanai
Copy link
Author

hakanai commented Apr 28, 2017

Witnessed on 9.0.5.0 as well. Granted, that version is out of date.

@enebo
Copy link
Member

enebo commented May 16, 2017

@trejkaz can you try 9.1.9.0 for us?

@hakanai
Copy link
Author

hakanai commented May 17, 2017

Pushing an attempted update into the pipeline to see. Unfortunately I don't have a quick way to run automated tests on Windows and I don't use Windows myself either. When we tried updating to 9.1.6.0, supposedly some clash against Jython caused things to fail, but I'm not sure that was the case so I'm trying anyway.

If only travis-ci would add Windows support, I could make a little test project and put the test in it :/

@enebo
Copy link
Member

enebo commented May 17, 2017

@trejkaz you can use appveyor for windows CI and it is free for OSS stuff.

@hakanai
Copy link
Author

hakanai commented May 18, 2017

appveyor looks doable actually. Though that's Windows-only, and I would have preferred to run everything on one CI to get the results in one place. :(

Anyway, the overnight run appears to have worked correctly. I get no skew for either of the two files being tested. Hopefully we don't get blocked for updating it by other libraries... (Jython is the most likely trouble-maker because they have a poor policy for keeping their dependencies up to date.)

@hakanai
Copy link
Author

hakanai commented May 18, 2017

Ah, I get a new failure in some other File-related test.

@Test
public void testResolveClasspath() throws Exception
{
    ScriptEngine engine = createEngine();

    String path = "classpath:/path/to/this/TestRubyFile.class";
    String result = (String) engine.eval("File.realpath('" + path + "')");
    assertThat(result, is(equalTo(path)));
}

This, too, works on my box when I run it, so it must be another Windows-specific thing.

Failure is:

Errno::ENOENT: No such file or directory - classpath:BY\path\to\this\TestRubyFile.class

File another ticket, I guess, and keep waiting? :/

@hakanai
Copy link
Author

hakanai commented May 30, 2017

9.1.10.0 update fixed the latter issue I got, and my File test still passes, so let's call it done.

@hakanai hakanai closed this as completed May 30, 2017
@kares kares added this to the JRuby 9.1.9.0 milestone May 30, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants