5

Im trying to convert a time structure to a FAT timestamp. My code looks like:

unsigned long Fat(tm_struct pTime)
{
    unsigned long FatTime = 0;

    FatTime |= (pTime.seconds / 2) >> 1;
    FatTime |= (pTime.minutes) << 5;
    FatTime |= (pTime.hours) << 11;
    FatTime |= (pTime.days) << 16;
    FatTime |= (pTime.months) << 21;
    FatTime |= (pTime.years + 20) << 25;

    return FatTime;
}

Does someone have the correct code?

Maestro
  • 9,046
  • 15
  • 83
  • 116

3 Answers3

11
The DOS date/time format is a bitmask:

               24                16                 8                 0
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|Y|Y|Y|Y|Y|Y|Y|M| |M|M|M|D|D|D|D|D| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s|
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
 \___________/\________/\_________/ \________/\____________/\_________/
    year        month       day      hour       minute        second

The year is stored as an offset from 1980. 
Seconds are stored in two-second increments. 
(So if the "second" value is 15, it actually represents 30 seconds.)

I dont know the tm_struct you are using but if it's http://www.cplusplus.com/reference/ctime/tm/ then

unsigned long FatTime = ((pTime.tm_year - 80) << 25) | 
                        ((pTime.tm_mon + 1) << 21) |
                        (pTime.tm_mday << 16) |
                        (pTime.tm_hour << 11) |
                        (pTime.tm_min << 5) |
                        (pTime.tm_sec >> 1);

Edit: I added the +1 in month as mentioned in the comments

Lefteris E
  • 2,806
  • 1
  • 24
  • 23
  • Some typecasts necessary if `sizeof (int)` is 2 (which can be the case here if the code is for DOS real mode). – Patrick Schlüter Apr 02 '13 at 13:02
  • 1
    And btw it would be also better to cast to an `unsigned` type, as shifting of `signed` types is not completely portable. – Patrick Schlüter Apr 02 '13 at 13:04
  • Is FAT timestamp in UTC or local time zone? – rustyx Apr 22 '16 at 07:57
  • It used to be in whatever the system time was set to, i.e. local time zone. But FAT is very widely adopted format, behavior of different implementations may vary in details like this. – Yirkha Oct 28 '17 at 00:48
  • Be careful, tm_mon starts counting months from 0, so `(pTime.tm_mon << 21)` should be changed to `((pTime.tm_mon+1) << 21)` I think, like in other answers below. – rel Jun 13 '20 at 21:55
4

Lefteris E gave almost correct answer but here's a little mistake

you should add 1 to tm_mon, because tm struct keeps month as number from 0 to 11 (struct tm), but DOS date/time from 1 to 12 (FileTimeToDosDateTime). so correctly is

unsigned long FatTime = ((pTime.tm_year - 80) << 25) | 
    ((pTime.tm_mon+1) << 21) |
    (pTime.tm_mday << 16) |
    (pTime.tm_hour << 11) |
    (pTime.tm_min << 5) |
    (pTime.tm_sec >> 1);
1

Also you can use bit fields and library time.h. In my project I convert UNIX timestamp to FatFs time.

        #include <time.h>

        #pragma pack (push,1)
        typedef struct{
            unsigned Sec :5;
            unsigned Min :6;
            unsigned Hour :5;
            unsigned Day :5;
            unsigned Month :4;
            unsigned Year :7;
        }FatDate_t;
        #pragma pack (pop)

        DWORD get_fattime(void){ 
            time_t ts=GetDateTimeNow();
            struct tm * tmInfo = gmtime( &ts );
            uint32_t Result=0;
            FatDate_t *fatDate;
            fatDate=(FatDate_t *)&Result;
            fatDate->Year=tmInfo->tm_year-80;
            fatDate->Month=tmInfo->tm_mon+1;
            fatDate->Day=tmInfo->tm_mday;

            fatDate->Hour=tmInfo->tm_hour;
            fatDate->Min=tmInfo->tm_min;
            fatDate->Sec=tmInfo->tm_sec>>1;

            return Result;
        }
Vladimir
  • 109
  • 1
  • 2