[Last Call] Learn about multicloud storage options and how to improve your company's cloud strategy. Register 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
?
441 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 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

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

In this post we will be converting StringData saved within a text file into a hash table. This can be further used in a PowerShell script for replacing settings that are dynamic in nature from environment to environment.
Sometimes clients can lose connectivity with the Lotus Notes Domino Server, but there's not always an obvious answer as to why it happens.   Read this article to follow one of the first experiences I had with Lotus Notes on a client's machine, my…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

650 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