[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Bug when reading the mtime of the file that was just set via _utime

Posted on 2014-04-10
5
Medium Priority
?
465 Views
Last Modified: 2014-04-12
Hi,

Writing unit tests, I was surprised by the behaviour described below, and I cannot see where the bug is. I am just calling the _utime() function to set the modification time for the file. When trying to get the modification time back via _stat(), the returned modification time is not the same.

#include <sys/types.h> 
#include <sys/stat.h>
#include <sys/utime.h>

#include <cassert>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main()
{
    string fname = { "test.data" };

    ofstream f(fname);
    f << "test content";
    f.close();

    // Check the existence of the created file.
    struct _stat stat;
    if (_stat(fname.c_str(), &stat)) {
        cout << "_stat() error";
        return 1;
    }
    assert((stat.st_mode & _S_IFREG) != 0);

    // The time to be used for testing.
    time_t christmas2003_t = 1072263600;   // 2003-12-23 11:00

    // Set the times of the file.
    struct _utimbuf ut;
    ut.actime = christmas2003_t;
    ut.modtime = christmas2003_t;

    if (_utime(fname.c_str(), &ut) == -1) {
        cout << "_utime() error";
        return 1;
    }

    // Get the modification time of the file (back).
    struct _stat stat_struct;
    if (_stat(fname.c_str(), &stat_struct))
    {
        cout << "_stat() error";
        return 1;
    }
    time_t mtime = stat_struct.st_mtime;

    // Compare the two times.
    if (mtime == christmas2003_t)
        cout << "OK\n";
    else
        cout << "failed (" << mtime << " is not equal to " << christmas2003_t << ")\n";
    return 0;
}

Open in new window

It prints
failed (1072260000 is not equal to 1072263600)

Open in new window

It means that the times are off exactly 3600 seconds, that is exactly one hour.

Where is the bug? I thought that both functions work with UTC time, or at least with the same way (with local time). How can I fix it?

I am using Windows 7, Visual Studio C++ 2013, and the file system is NTFS. I am from Czech that should have GMT+1, and Daylight Saving Time is applied.

Thanks,
  Petr
0
Comment
Question by:pepr
  • 4
5 Comments
 
LVL 86

Accepted Solution

by:
jkr earned 2000 total points
ID: 39993062
Stupid question: Since you're on Windows, why don't you use 'SetFileTime()' (http://msdn.microsoft.com/en-us/library/windows/desktop/ms724933(v=vs.85).aspx) instead?
0
 
LVL 29

Author Comment

by:pepr
ID: 39993452
There are several reasons. The code was actually cut from a library that wraps the time functions. This is because I want to make it portable.

Another reason is that for the file, the function should return comparable value both on Unix and Windows.

So, the question remains. Why do I observe the one hour difference? Is the bug in my code or is it on Windows side?

I would almost swear that the unit test passed earlier. I know it was last tested before switching to DST this year. Because of that I suspect that I should make a correction when DST is used. But this is just a blind guess.
0
 
LVL 29

Author Comment

by:pepr
ID: 39993687
The same code with simplified testing (asserts only; should be more readable) and the corrected comment:
#include <cassert>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main()
{
    string fname = { "test.data" };

    ofstream f(fname);
    f << "test content";
    f.close();

    // Check the existence of the created file.
    struct _stat stat;
    int result = _stat(fname.c_str(), &stat);
    assert(result == 0);
    assert((stat.st_mode & _S_IFREG) != 0);

    // The time to be used for testing.
    time_t christmas2003_t = 1072263600;   // 2003-12-24 11:00

    // Set the times of the file.
    struct _utimbuf ut;
    ut.actime = christmas2003_t;
    ut.modtime = christmas2003_t;

    result = _utime(fname.c_str(), &ut);
    assert(result != -1);

    // Get the modification time of the file (back).
    result = _stat(fname.c_str(), &stat);
    assert(result == 0);
    time_t mtime = stat.st_mtime;

    // Compare the two times.
    if (mtime == christmas2003_t)
        cout << "OK\n";
    else
        cout << "failed (" << mtime << " is not equal to " << christmas2003_t << ")\n";
    return 0;
}

Open in new window

It returns the same
failed (1072260000 is not equal to 1072263600)

Open in new window

0
 
LVL 29

Author Comment

by:pepr
ID: 39993908
Apparently, I am not the only one that has problems with the interpretation. See the captured screens. The cmd and Total Commander think differently about the time for the test.data file. However, the a.bak that was created manuall via an editor just now shows the same time. And it is a local time! (It was exactly 14:00 of the local time -- 00 minutes is only a pure coincidence. It really was tha time, and it is GMT + 2 because of the DST.)
Different times for test.data in cmd and TotalCommander, the same for a.bak.
0
 
LVL 29

Author Comment

by:pepr
ID: 39995941
@jkr: Thanks.

I have reimplemented my os::utime() function using the SetFileTime() function.

    void utime(const std::string & path, time_t actime, time_t modtime)
    {
        FILETIME ftLastAccessTime;
        timet_to_filetime(actime, ftLastAccessTime);

        FILETIME ftLastWriteTime;
        timet_to_filetime(actime, ftLastWriteTime);

        HANDLE hFile = CreateFile(path.c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ,
            nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);

        if (hFile == INVALID_HANDLE_VALUE) {
            ostringstream ostr;
            ostr << __FUNCTION__ "(): CreateFile failed with " << GetLastError() << "\n";
            throw runtime_error(ostr.str());
        }

        if (!SetFileTime(hFile, nullptr, &ftLastAccessTime, &ftLastWriteTime)) {
            ostringstream ostr;
            ostr << __FUNCTION__ "(): SetFileTime failed with " << GetLastError() << "\n";
            CloseHandle(hFile);
            throw runtime_error(ostr.str());
        }

        CloseHandle(hFile);
    }

Open in new window

The timet_to_filetime() conversion function was found elsewhere:
    void timet_to_filetime(time_t t, FILETIME& ft)
    {
        LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
        ft.dwLowDateTime = (DWORD) ll;
        ft.dwHighDateTime = ll >> 32;
    }

Open in new window

It seems that the problem was fixed. I will see a half year later when Daylight Saving Time is switched again.

If the implementation is correct, then Total commander shows a correct local time for the file (a it whas Christmas, the time was GMT+1). The dir utility shows wrong local time 13:00 instead of 12:00. Well, it depends on how we look at the problem.
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Compliance and data security require steps be taken to prevent unauthorized users from copying data.  Here's one method to prevent data theft via USB drives (and writable optical media).
Windows Server 2003 introduced persistent Volume Shadow Copies and made 2003 a must-do upgrade.  Since then, it's been a must-implement feature for all servers doing any kind of file sharing.
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
In this video, viewers will be given step by step instructions on adjusting mouse, pointer and cursor visibility in Microsoft Windows 10. The video seeks to educate those who are struggling with the new Windows 10 Graphical User Interface. Change Cu…

825 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question