Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some HFS timestamps are off by an hour #56

Open
fadden opened this issue Jan 13, 2023 · 0 comments
Open

Some HFS timestamps are off by an hour #56

fadden opened this issue Jan 13, 2023 · 0 comments
Labels

Comments

@fadden
Copy link
Owner

fadden commented Jan 13, 2023

Not entirely sure what's going on. On Linux, this:

    time_t tester = 0x2cd9a98c;
    struct tm* ptm = localtime(&tester);
    printf("hour is %d\n", ptm->tm_hour);

Outputs "hour is 17". Running in CiderPress, the equivalent code outputs "hour is 18". Being off by an hour is often the result of a difference of opinion about daylight saving time. Some dates are correct, some are not.

This affects HFS timestamps, which use a time in seconds from a point specified in local time. The Linux version agrees with an equivalent C# .NET interpretation, so I think CiderPress is wrong. My best guess is that a timezone value is not getting configured correctly somewhere in the CiderPress environment.

Expected results for a time_t, printed with ctime() in PST (GMT-8):

time1 2cd9a98c: Thu Nov  4 17:17:00 1993
time2 3f183f6c: Fri Jul 18 11:41:48 2003

Update 1:
I'm testing with the A2 Romulan CD-ROM image, which is an HFS volume. The files I'm looking at are .DS_Store in the root directory, and ASCII.ART:COWS.1. Loading the volume into KEGS and examining the files in the GS/OS Finder shows "Thu, Nov 4, 1993, 5:17 PM" and "Fri, Jul 18, 2003, 10:41AM". hfsutils on Linux shows 17:17 and 11:41. CiderPress, which uses libhfs, shows 18:17 and 11:41.

I'm fairly convinced that this has something to do with the definition of daylight saving time changing over the years, so it's possible that GS/OS has the wrong time for the 2003 date because of some internal issue. GS/OS interpretation of timestamps is generally inconsistent, with a number of both old and new files being off by an hour from CiderPress and C# .NET.

I believe the Linux and C# .NET interpretation (17:17 and 11:41) is likely the correct one, as time values can be converted to a date/time struct and back to an integer with no loss, and the linux/.NET libraries should be up to date for the various calculations. It's also the only pair of implementations that agree with each other; everything else agrees sometimes and disagrees at others.

Update 2:
There are 86400 seconds in a day. All timestamps are offsets from midnight local time. The HFS timestamps examined above are 0xa8fee98c and 0xbb3d7f6c. 0xa8fee98c mod 86400 is 62220, which is 17 hours 17 minutes. 0xbb3d7f6c mod 86400 is 38508, which is 10 hours 41 minutes. So the Apple IIgs times are correct, and everybody else is wrong.

This:

            long unixSec = when - HFS_UNIX_TIME_OFFSET;
            unixSec -= (int)TimeZoneInfo.Local.BaseUtcOffset.TotalSeconds;
            DateTimeOffset dtOff = DateTimeOffset.FromUnixTimeSeconds(unixSec);
            return dtOff.LocalDateTime;

yields 17:17 and 11:41. The equation is similar to what libhfs does. This:

            long unixSec = when - HFS_UNIX_TIME_OFFSET;
            DateTimeOffset dtOff = DateTimeOffset.FromUnixTimeSeconds(unixSec);
            return dtOff.DateTime;

yields 17:17 and 10:41 instead.

Update 3:
Here's the correct formula for C#:

            long unixSec = when - HFS_UNIX_TIME_OFFSET;
            DateTimeOffset dtOff = DateTimeOffset.FromUnixTimeSeconds(unixSec);
            return DateTime.SpecifyKind(dtOff.DateTime, DateTimeKind.Local);

We compute the date/time as if it were UTC, and then declare that it's actually local time, bypassing any consideration of time zones or daylight saving. The reverse equation is:

            DateTime utcDT = DateTime.SpecifyKind(when, DateTimeKind.Utc);
            DateTimeOffset dtoff = new DateTimeOffset(utcDT);
            long unixSec = dtoff.ToUnixTimeSeconds();
            long hfsSec = unixSec + HFS_UNIX_TIME_OFFSET;

I think we can do something similar with the C library by using gmtime() to pull the timestamp apart, and then handing the values to mktime() to form the time_t.


The crux of the problem is that we're trying to map a UTC timestamp to a local timestamp by adjusting for the current timezone, and then feeding the modified value into code that takes UTC seconds. For example, 10am is stored as 10 * 3600. For UTC, that works. For Pacific Standard I need to adjust that by 8 hours before handing it to the time functions, for Pacific Daylight I need to adjust by 7 hours, but the adjustment is different for July 1 at 10am than it is for Jan 1 at 10am. CiderPress and libhfs are basing the adjustment on the current date/time, not the target date/time, so it's off by an hour whenever the current and target DST values are different.

CiderPress goes the extra mile and is frequently off by an hour, though it gets 0xba214379 (14-Dec-02 20:22) right.

@fadden fadden added the bug label Jan 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant