]> git.sven.stormbind.net Git - sven/fuse-exfat.git/blobdiff - libexfat/time.c
New upstream version 1.3.0+git20220115
[sven/fuse-exfat.git] / libexfat / time.c
index b264a999a9836f437080ade4c8a5f2043f306e8f..e2a3b23d631fd7bad127ee979bf62ffe50d8f963 100644 (file)
@@ -3,7 +3,7 @@
        exFAT file system implementation library.
 
        Free exFAT implementation.
-       Copyright (C) 2010-2014  Andrew Nayenko
+       Copyright (C) 2010-2018  Andrew Nayenko
 
        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
@@ -53,7 +53,8 @@ static const time_t days_in_year[] =
        0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334
 };
 
-time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec)
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec,
+               uint8_t tzoffset)
 {
        time_t unix_time = EPOCH_DIFF_SEC;
        uint16_t ndate = le16_to_cpu(date);
@@ -100,13 +101,18 @@ time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec)
        unix_time += centisec / 100;
 
        /* exFAT stores timestamps in local time, so we correct it to UTC */
-       unix_time += exfat_timezone;
+       if (tzoffset & 0x80)
+               /* lower 7 bits are signed timezone offset in 15 minute increments */
+               unix_time -= (int8_t)(tzoffset << 1) * 15 * 60 / 2;
+       else
+               /* timezone offset not present, assume our local timezone */
+               unix_time += exfat_timezone;
 
        return unix_time;
 }
 
 void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
-               uint8_t* centisec)
+               uint8_t* centisec, uint8_t* tzoffset)
 {
        time_t shift = EPOCH_DIFF_SEC + exfat_timezone;
        uint16_t day, month, year;
@@ -146,13 +152,22 @@ void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
        *time = cpu_to_le16(twosec | (min << 5) | (hour << 11));
        if (centisec)
                *centisec = (unix_time % 2) * 100;
+
+       /* record our local timezone offset in exFAT (15 minute increment) format */
+       *tzoffset = (uint8_t)(-exfat_timezone / 60 / 15) | 0x80;
 }
 
 void exfat_tzset(void)
 {
        time_t now;
+       struct tm* utc;
 
        tzset();
        now = time(NULL);
-       exfat_timezone = mktime(gmtime(&now)) - now;
+       utc = gmtime(&now);
+       /* gmtime() always sets tm_isdst to 0 because daylight savings never
+          affect UTC. Setting tm_isdst to -1 makes mktime() to determine whether
+          summer time is in effect. */
+       utc->tm_isdst = -1;
+       exfat_timezone = mktime(utc) - now;
 }