Discussion:
utime() and GMT
(too old to reply)
neilsolent
2009-10-28 10:00:32 UTC
Permalink
Hi All

How can I read/write 4-byte wide timestamps to files in GMT, ignoring
any local timezone?
i.e. I want versions of utime() and stat() that work only in GMT.

To demonstrate what I mean..

Run this program in the UK at 01:59 on 25/10/09 (when the local
timezone is GMT+1):

<code>
#include "stdafx.h"
#include <sys/utime.h>

int main(int argc, char* argv[])
{
struct _utimbuf ver;
ver.modtime = 0;
ver.actime = 0;
_utime("test", &ver);
return 0;
}
</code>

C:\temp>dir test
Volume in drive C has no label.
Volume Serial Number is 5028-7113

Directory of C:\temp

01/01/1970 00:00 7 test
1 File(s) 7 bytes
0 Dir(s) 34,768,842,752 bytes free



Wait 2 minutes (local timezone changes to GMT+0) and:



C:\temp>dir test
Volume in drive C has no label.
Volume Serial Number is 5028-7113

Directory of C:\temp

31/12/1969 23:00 7 test
1 File(s) 7 bytes
0 Dir(s) 34,768,842,752 bytes free




This differs from how utime() works on UNIX. The UNIX man pages say:

" The times contained in the members of the utimbuf structure
are measured in seconds since 00:00:00 UTC, January 1, 1970. "

The Windows version seems to be taking into account the current
timezone when these commands are run. I need to turn this behaviour
off.


Thanks,
Neil
David Lowndes
2009-10-28 12:54:24 UTC
Permalink
Post by neilsolent
The Windows version seems to be taking into account the current
timezone when these commands are run. I need to turn this behaviour
off.
Neil,

What do you get if you run the program again when at the UTC time
zone?

I think you're just seeing a quirk. On NTFS the file timestamps are
stored as UTC, and the facilities of the OS (usually) display the
value as local time - that's why the timestamp displays differently
when viewed from different time zones.

Dave
neilsolent
2009-10-28 13:07:59 UTC
Permalink
Dave
Thanks for the reply.
Post by David Lowndes
What do you get if you run the program again when at the UTC time
zone?
It sets the time to "01/01/1970 00:00" - as I would expect.

I am happy with the output - I can see why it is what it is. I don't
think it is a bug - it is just not the behaviour I want!

When the program sets the time to "0" with the local timezone set to
GMT+1, it is setting "01/01/1970 00:00" in the local timezone - which
is actually "31/12/1969 23:00" in GMT (as GMT is one hour behind).
The output of dir is correct as well - again reporting dates and times
in the current timezone.
I just want to tell utime() to interpret 0 as "0 GMT" NOT "0
LOCALTIME". But I can't think of a neat way of doing it!

I don't want to look up the current timezone and add correction
factors - apart from being a load of messy code - it would be hard to
avoid a race condition if the code is run close to the moment a
timezone is changed. I naturally assume Windows does everything
natively in GMT (UCT) internally anyway - so there should be a simpler
way
David Lowndes
2009-10-28 14:06:20 UTC
Permalink
Post by neilsolent
I just want to tell utime() to interpret 0 as "0 GMT" NOT "0
LOCALTIME". But I can't think of a neat way of doing it!
I'd use the Win32 SetFileTime API instead and convert between local
and system time as appropriate using LocalFileTimeToFileTime &
FileTimeToLocalFileTime .

Dave
neilsolent
2009-10-28 19:01:32 UTC
Permalink
Post by David Lowndes
Post by neilsolent
I just want to tell utime() to interpret 0 as "0 GMT" NOT "0
LOCALTIME". But I can't think of a neat way of doing it!
I'd use the Win32 SetFileTime API instead and convert between local
and system time as appropriate using LocalFileTimeToFileTime &
FileTimeToLocalFileTime .
Dave
I start with the time in "seconds since midnight, Jan 1st 1970" format
(time_t) - I don't think there is any simple way to convert to/from
FILETIME.
And there's still the issue of avoiding nasty outcomes at the moment
that the timezone changes.
Filesystems are always in UTC - seems annoying that I can't just read/
write the time directly in UTC without the complication of the local
time zone getting in the way
Nathan Mates
2009-10-28 20:24:36 UTC
Permalink
Post by neilsolent
I start with the time in "seconds since midnight, Jan 1st 1970" format
(time_t) - I don't think there is any simple way to convert to/from
FILETIME.
Yes there is.
http://msdn.microsoft.com/en-us/library/ms724228(VS.85).aspx shows how
to go from time_t to FILETIME. If you look closely, it's basically
filetime = (X * time) + Y. Reversing the operation is simple algebra.

Nathan Mates
--
<*> Nathan Mates - personal webpage http://www.visi.com/~nathan/
# Programmer at Pandemic Studios -- http://www.pandemicstudios.com/
# NOT speaking for Pandemic Studios. "Care not what the neighbors
# think. What are the facts, and to how many decimal places?" -R.A. Heinlein
David Lowndes
2009-10-28 20:39:25 UTC
Permalink
Post by neilsolent
I start with the time in "seconds since midnight, Jan 1st 1970" format
(time_t) - I don't think there is any simple way to convert to/from
FILETIME.
You can do it, but it may take a couple of hops. The COleDateTime
class may wrap enough of this functionality to make it look simple.
However...
Post by neilsolent
And there's still the issue of avoiding nasty outcomes at the moment
that the timezone changes.
Filesystems are always in UTC - seems annoying that I can't just read/
write the time directly in UTC without the complication of the local
time zone getting in the way
The way it normally works is this:

A user (normally) likes to work in their local time.
The internals work in UTC.

... so wherever there's a transition between the 2 you perform the
appropriate local/utc conversion.

If the user has entered the time initially, convert it to UTC then
save it. When reading back to present to the user you go from UTC to
local.

Have a look at the MSDN knowledge base article 932955 "How to handle
dates and times that include DST" - that seems to explain the
situation.

Dave
neilsolent
2009-10-30 09:57:13 UTC
Permalink
ALL

Thanks for the posts. I had overlooked that SetFileTime() and
GetFileTime() naturally work in GMT, which simplifies things and I
agree is th best approach.
My code now works.

One further question - is there an inverse function to this:

void TimetToFileTime(time_t t, LPFILETIME pft)

.. I need to convert FILETIME back to time_t in another part of the
code.

Thanks again
David Lowndes
2009-10-30 10:48:16 UTC
Permalink
Post by neilsolent
Thanks for the posts. I had overlooked that SetFileTime() and
GetFileTime() naturally work in GMT, which simplifies things and I
agree is th best approach.
My code now works.
Good :)
Post by neilsolent
void TimetToFileTime(time_t t, LPFILETIME pft)
.. I need to convert FILETIME back to time_t in another part of the
code.
I can't immediately find one to copy, but that example on MSDN is only
simple maths, so just to the inverse operations.

Dave
neilsolent
2009-10-30 18:43:23 UTC
Permalink
Post by David Lowndes
I can't immediately find one to copy, but that example on MSDN is only
simple maths, so just to the inverse operations.
Just to wrap this thread up - and for future ref - here's my
implementation of this function:

void FileTimeToTimet(LPFILETIME pft, time_t* t)
{
*t = (((pft->dwHighDateTime - 27111902) << 32) - 3577643008 + pft-
Post by David Lowndes
dwLowDateTime) / 10000000;
}
neilsolent
2009-10-31 07:24:56 UTC
Permalink
Post by David Lowndes
Post by neilsolent
void TimetToFileTime(time_t t, LPFILETIME pft)
.. I need to convert FILETIME back to time_t in another part of the
code.
I can't immediately find one to copy, but that example on MSDN is only
simple maths, so just to the inverse operations.
Here's my working implementation - just to wrap this thread up:

[code]
void FileTimeToTimet(LPFILETIME pft, time_t* t)
{
*t = (((pft->dwHighDateTime - 27111902) << 32) - 3577643008 + pft-
Post by David Lowndes
dwLowDateTime) / 10000000;
}
[/code]
neilsolent
2009-10-31 23:32:10 UTC
Permalink
Post by David Lowndes
Post by neilsolent
.. I need to convert FILETIME back to time_t in another part of the
code.
I can't immediately find one to copy, but that example on MSDN is only
simple maths, so just to the inverse operations.
Dave
Here's my implementation (removed previous post - had overflow error
with larger dates):

[code]

time_t FileTimeToTimet(LPFILETIME pft)
{
ULONGLONG ull = UInt32x32To64((pft->dwHighDateTime - 27111902) * 2,
2147483648) + pft->dwLowDateTime - 3577643008;
return (time_t) (ull / 10000000);
}

[/code]
Nathan Mates
2009-10-28 20:24:36 UTC
Permalink
Post by neilsolent
I start with the time in "seconds since midnight, Jan 1st 1970" format
(time_t) - I don't think there is any simple way to convert to/from
FILETIME.
Yes there is.
http://msdn.microsoft.com/en-us/library/ms724228(VS.85).aspx shows how
to go from time_t to FILETIME. If you look closely, it's basically
filetime = (X * time) + Y. Reversing the operation is simple algebra.

Nathan Mates
--
<*> Nathan Mates - personal webpage http://www.visi.com/~nathan/
# Programmer at Pandemic Studios -- http://www.pandemicstudios.com/
# NOT speaking for Pandemic Studios. "Care not what the neighbors
# think. What are the facts, and to how many decimal places?" -R.A. Heinlein
David Lowndes
2009-10-28 20:39:25 UTC
Permalink
Post by neilsolent
I start with the time in "seconds since midnight, Jan 1st 1970" format
(time_t) - I don't think there is any simple way to convert to/from
FILETIME.
You can do it, but it may take a couple of hops. The COleDateTime
class may wrap enough of this functionality to make it look simple.
However...
Post by neilsolent
And there's still the issue of avoiding nasty outcomes at the moment
that the timezone changes.
Filesystems are always in UTC - seems annoying that I can't just read/
write the time directly in UTC without the complication of the local
time zone getting in the way
The way it normally works is this:

A user (normally) likes to work in their local time.
The internals work in UTC.

... so wherever there's a transition between the 2 you perform the
appropriate local/utc conversion.

If the user has entered the time initially, convert it to UTC then
save it. When reading back to present to the user you go from UTC to
local.

Have a look at the MSDN knowledge base article 932955 "How to handle
dates and times that include DST" - that seems to explain the
situation.

Dave
neilsolent
2009-10-28 19:01:32 UTC
Permalink
Post by David Lowndes
Post by neilsolent
I just want to tell utime() to interpret 0 as "0 GMT" NOT "0
LOCALTIME". But I can't think of a neat way of doing it!
I'd use the Win32 SetFileTime API instead and convert between local
and system time as appropriate using LocalFileTimeToFileTime &
FileTimeToLocalFileTime .
Dave
I start with the time in "seconds since midnight, Jan 1st 1970" format
(time_t) - I don't think there is any simple way to convert to/from
FILETIME.
And there's still the issue of avoiding nasty outcomes at the moment
that the timezone changes.
Filesystems are always in UTC - seems annoying that I can't just read/
write the time directly in UTC without the complication of the local
time zone getting in the way
David Lowndes
2009-10-28 14:06:20 UTC
Permalink
Post by neilsolent
I just want to tell utime() to interpret 0 as "0 GMT" NOT "0
LOCALTIME". But I can't think of a neat way of doing it!
I'd use the Win32 SetFileTime API instead and convert between local
and system time as appropriate using LocalFileTimeToFileTime &
FileTimeToLocalFileTime .

Dave

neilsolent
2009-10-28 13:07:59 UTC
Permalink
Dave
Thanks for the reply.
Post by David Lowndes
What do you get if you run the program again when at the UTC time
zone?
It sets the time to "01/01/1970 00:00" - as I would expect.

I am happy with the output - I can see why it is what it is. I don't
think it is a bug - it is just not the behaviour I want!

When the program sets the time to "0" with the local timezone set to
GMT+1, it is setting "01/01/1970 00:00" in the local timezone - which
is actually "31/12/1969 23:00" in GMT (as GMT is one hour behind).
The output of dir is correct as well - again reporting dates and times
in the current timezone.
I just want to tell utime() to interpret 0 as "0 GMT" NOT "0
LOCALTIME". But I can't think of a neat way of doing it!

I don't want to look up the current timezone and add correction
factors - apart from being a load of messy code - it would be hard to
avoid a race condition if the code is run close to the moment a
timezone is changed. I naturally assume Windows does everything
natively in GMT (UCT) internally anyway - so there should be a simpler
way
David Lowndes
2009-10-28 12:54:24 UTC
Permalink
Post by neilsolent
The Windows version seems to be taking into account the current
timezone when these commands are run. I need to turn this behaviour
off.
Neil,

What do you get if you run the program again when at the UTC time
zone?

I think you're just seeing a quirk. On NTFS the file timestamps are
stored as UTC, and the facilities of the OS (usually) display the
value as local time - that's why the timestamp displays differently
when viewed from different time zones.

Dave
Loading...