Solved

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

Posted on 2014-04-10
5
368 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 500 total points
Comment Utility
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 28

Author Comment

by:pepr
Comment Utility
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 28

Author Comment

by:pepr
Comment Utility
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 28

Author Comment

by:pepr
Comment Utility
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 28

Author Comment

by:pepr
Comment Utility
@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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Our Group Policy work started with Small Business Server in 2000. Microsoft gave us an excellent OU and GPO model in subsequent SBS editions that utilized WMI filters, OU linking, and VBS scripts. These are some of experiences plus our spending a lo…
Join Greg Farro and Ethan Banks from Packet Pushers (http://packetpushers.net/podcast/podcasts/pq-show-93-smart-network-monitoring-paessler-sponsored/) and Greg Ross from Paessler (https://www.paessler.com/prtg) for a discussion about smart network …
Windows 8 comes with a dramatically different user interface known as Metro. Notably missing from the new interface is a Start button and Start Menu. Many users do not like it, much preferring the interface of earlier versions — Windows 7, Windows X…
The Task Scheduler is a powerful tool that is built into Windows. It allows you to schedule tasks (actions) on a recurring basis, such as hourly, daily, weekly, monthly, at log on, at startup, on idle, etc. This video Micro Tutorial is a brief intro…

772 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now