Solved

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

Posted on 2014-04-10
5
411 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
5 Comments
 
LVL 86

Accepted Solution

by:
jkr earned 500 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

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

No single Antivirus application (despite claims by manufacturers) will catch or protect you from all Virus / Malware or Spyware threats. That doesn't stop you from further protecting yourself however - and this article is to show you how.
I was prompted to write this article after the recent World-Wide Ransomware outbreak. For years now, System Administrators around the world have used the excuse of "Waiting a Bit" before applying Security Patch Updates. This type of reasoning to me …
Windows 8 came with a dramatically different user interface known as Metro. Notably missing from that interface was a Start button and Start Menu. Microsoft responded to negative user feedback of the Metro interface, bringing back the Start button a…
This is used to tweak the memory usage for your computer, it is used for servers more so than workstations but just be careful editing registry settings as it may cause irreversible results. I hold no responsibility for anything you do to the regist…

687 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