Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: oldheating gps motorhome heating
Diff: tm/tm.c
- Revision:
- 35:ba9f575aa3c6
- Parent:
- 31:f6ff7fdb9c67
- Child:
- 37:330b844f54b6
diff -r aeb58975e61a -r ba9f575aa3c6 tm/tm.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tm/tm.c Sat Dec 01 15:43:59 2018 +0000
@@ -0,0 +1,309 @@
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define STD_OFFSET 0
+#define DST_OFFSET 1
+
+static bool isLeapYear(int year)
+{
+ year += 1900;
+ bool leapYear = !(year & 0x3);
+ if (year >= 2100)
+ {
+ if (year % 100 == 0) leapYear = false;
+ if (year % 400 == 0) leapYear = true;
+ }
+ return leapYear;
+
+}
+static int monthLength(int year, int month)
+{
+ static char monthlengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ int daysInMonth = monthlengths[month];
+ if (month == 1 && isLeapYear(year)) daysInMonth++; //February is month 1 of months 0 to 11
+ return daysInMonth;
+}
+static bool isDst(int year, int month, int dayOfMonth, int dayOfWeek, int hours)
+{
+ //Find the last Sunday in the month
+ int lastDayOfMonth = monthLength(year, month);
+ int daysToEndOfMonth = lastDayOfMonth - dayOfMonth;
+ int dayOfWeekOfLastDayOfMonth = (dayOfWeek + daysToEndOfMonth) % 7;
+ int lastSundayDayOfMonth = lastDayOfMonth - dayOfWeekOfLastDayOfMonth;
+
+ //Check each month
+ if (month <= 1) return false; //Jan, Feb
+ if (month == 2) //Mar - DST true after 1am UTC on the last Sunday in March
+ {
+ if (dayOfMonth < lastSundayDayOfMonth) return false;
+ if (dayOfMonth == lastSundayDayOfMonth) return hours >= 1;
+ if (dayOfMonth > lastSundayDayOfMonth) return true;
+ }
+ if (month >= 3 && month <= 8) return true; //Apr, May, Jun, Jul, Aug, Sep
+ if (month == 9) //Oct - DST false after 1am UTC on the last Sunday in October
+ {
+ if (dayOfMonth < lastSundayDayOfMonth) return true;
+ if (dayOfMonth == lastSundayDayOfMonth) return hours < 1;
+ if (dayOfMonth > lastSundayDayOfMonth) return false;
+ }
+ if (month >= 10) return false; //Nov, Dec
+ return false;
+}
+static void calculateDayOfYearAndWeek(int thisYear, int thisMonth, int thisMonthDay, int* pDayOfYear, int* pDayOfWeek)
+{
+ int dayOfYear = 0; //1 Jan is day 0
+ int dayOfWeek = 4; //1 Jan 1970 is a Thursday
+
+ //Add days of each whole year
+ for (int y = 70; y < thisYear; y++)
+ {
+ int lengthOfYear = isLeapYear(y) ? 366 : 365;
+ dayOfWeek += lengthOfYear;
+ }
+
+ //Add days of each whole month
+ for (int m = 0; m < thisMonth; m++)
+ {
+ int lengthOfMonth = monthLength(thisYear, m);
+ dayOfYear += lengthOfMonth;
+ dayOfWeek += lengthOfMonth;
+ }
+
+ //Add days of part month
+ thisMonthDay--; //thisMonthDay is 01 to 31 where we need 00 to 30
+ dayOfYear += thisMonthDay;
+ dayOfWeek += thisMonthDay;
+
+ //Update the day of year and day of week parts of the struct tm
+ *pDayOfYear = dayOfYear; // 0 --> 365
+ *pDayOfWeek = dayOfWeek % 7; // 0 --> 6
+}
+void TmIncrement(struct tm* ptm)
+{
+ ptm->tm_sec++;
+ if (ptm->tm_sec > 59)
+ {
+ ptm->tm_sec = 0;
+ ptm->tm_min++;
+ }
+ if (ptm->tm_min > 59)
+ {
+ ptm->tm_min = 0;
+ ptm->tm_hour++;
+ }
+ if (ptm->tm_hour > 23)
+ {
+ ptm->tm_hour = 0;
+ ptm->tm_wday++;
+ if (ptm->tm_wday > 6) ptm->tm_wday = 0;
+ ptm->tm_yday++;
+ ptm->tm_mday++;
+ if (ptm->tm_mday > monthLength(ptm->tm_year, ptm->tm_mon))
+ {
+ ptm->tm_mon++;
+ if (ptm->tm_mon > 11)
+ {
+ ptm->tm_year++;
+ ptm->tm_yday = 0;
+ ptm->tm_mon = 0;
+ }
+ ptm->tm_mday = 1;
+ }
+ }
+}
+static void normalise(int* pHours, int* pDayOfWeek, int* pDayOfMonth, int* pMonth, int * pDayOfYear, int* pYear)
+{
+ if (*pHours > 23)
+ {
+ *pHours -= 24;
+ ++*pDayOfWeek;
+ if (*pDayOfWeek > 6) *pDayOfWeek = 0;
+ ++*pDayOfYear;
+ ++*pDayOfMonth;
+ if (*pDayOfMonth > monthLength(*pYear, *pMonth))
+ {
+ ++*pMonth;
+ if (*pMonth > 11)
+ {
+ ++*pYear;
+ *pDayOfYear = 0;
+ *pMonth = 0;
+ }
+ *pDayOfMonth = 1;
+ }
+ }
+
+ if (*pHours < 0)
+ {
+ *pHours += 24;
+ --*pDayOfWeek;
+ if (*pDayOfWeek < 0) *pDayOfWeek = 6;
+ --*pDayOfYear;
+ --*pDayOfMonth;
+ if (*pDayOfMonth < 1)
+ {
+ --*pMonth;
+ if (*pMonth < 0)
+ {
+ --*pYear;
+ *pDayOfYear = isLeapYear(*pYear) ? 365 : 364;
+ *pMonth = 11;
+ }
+ *pDayOfMonth = monthLength(*pYear, *pMonth);
+ }
+ }
+}
+static void addYears(int* pYear, int* pDayOfWeek, int* pDaysLeft)
+{
+ while(1)
+ {
+ //See if it is a leap year
+ int leapYear = isLeapYear(*pYear);
+
+ //Find the number of days in this year
+ int daysInYear = leapYear ? 366 : 365;
+
+ //Stop if this is the final year
+ if (*pDaysLeft < daysInYear) break;
+
+ //Calculate the current day of the week at the start of the year
+ *pDayOfWeek += leapYear ? 2 : 1;
+ if (*pDayOfWeek >= 7) *pDayOfWeek -= 7;
+
+ //Move on to the next year
+ *pDaysLeft -= daysInYear;
+ ++*pYear;
+ }
+}
+static void addMonths(int year, int* pMonth, int* pDaysLeft)
+{
+ while(1)
+ {
+ int daysInMonth = monthLength(year, *pMonth);
+
+ //Stop if this is the last month
+ if (*pDaysLeft < daysInMonth) break;
+
+ //Move onto next month
+ *pDaysLeft -= daysInMonth;
+ ++*pMonth;
+ }
+}
+static void timeToTm(time_t t, struct tm* ptm, bool local)
+{
+ //Extract the seconds, minutes, hours and days from the time_t t
+ div_t divres;
+ divres = div( t, 60); int seconds = divres.rem;
+ divres = div(divres.quot, 60); int minutes = divres.rem;
+ divres = div(divres.quot, 24); int hours = divres.rem;
+ int daysLeft = divres.quot;
+
+ //Add a year at a time while there is more than a year of days left
+ int year = 70; //Unix epoch is 1970
+ int dayOfWeek = 4; //1 Jan 1970 is a Thursday
+ addYears(&year, &dayOfWeek, &daysLeft);
+
+ //Days left contains the days left from the start (1 Jan) of the current year
+ int dayOfYear = daysLeft;
+ dayOfWeek += daysLeft;
+ dayOfWeek %= 7;
+
+ //Add a month at a time while there is more than a month of days left
+ int month = 0;
+ addMonths(year, &month, &daysLeft);
+
+ //Days left contains the days left from the start (1st) of the current month
+ int dayOfMonth = daysLeft + 1;
+
+ //Deal with local time offsets
+ int dst;
+ if (local)
+ {
+ //Work out if Daylight Saving Time applies
+ dst = isDst(year, month, dayOfMonth, dayOfWeek, hours);
+
+ //Adjust for the timezone
+ hours += dst ? DST_OFFSET : STD_OFFSET;
+ normalise(&hours, &dayOfWeek, &dayOfMonth, &month, &dayOfYear, &year);
+ }
+ else
+ {
+ dst = -1;
+ }
+
+ //Set up the broken time TM structure
+ ptm->tm_sec = seconds; // 00 --> 59
+ ptm->tm_min = minutes; // 00 --> 59
+ ptm->tm_hour = hours; // 00 --> 23
+ ptm->tm_mday = dayOfMonth; // 01 --> 31
+ ptm->tm_mon = month; // 00 --> 11
+ ptm->tm_year = year; // Years since 1900
+ ptm->tm_wday = dayOfWeek; // 0 --> 6 where 0 == Sunday
+ ptm->tm_yday = dayOfYear; // 0 --> 365
+ ptm->tm_isdst = dst; // +ve if DST, 0 if not DSTime, -ve if the information is not available. Note that 'true' evaluates to +1.
+}
+void TmUtcFromTimeT(time_t time, struct tm* ptm)
+{
+ timeToTm(time, ptm, false);
+}
+void TmLocalFromTimeT(time_t time, struct tm* ptm)
+{
+ timeToTm(time, ptm, true);
+}
+time_t TmUtcToTimeT(struct tm* ptm)
+{
+ int days = 0;
+
+ for (int y = 70; y < ptm->tm_year; y++) days += isLeapYear(y) ? 366 : 365;
+
+ days += ptm->tm_yday;
+
+ return days * 86400 +
+ ptm->tm_hour * 3600 +
+ ptm->tm_min * 60 +
+ ptm->tm_sec;
+}
+int TmSecondsBetween(struct tm* ptmLater, struct tm* ptmEarlier)
+{
+ int days = 0;
+
+ if (ptmLater->tm_year > ptmEarlier->tm_year) for (int y = ptmEarlier->tm_year; y < ptmLater->tm_year; y++) days += isLeapYear(y) ? 366 : 365;
+ else for (int y = ptmEarlier->tm_year; y > ptmLater->tm_year; y--) days -= isLeapYear(y) ? 366 : 365;
+
+ days += ptmLater->tm_yday - ptmEarlier->tm_yday;
+
+ return days * 86400 +
+ (ptmLater->tm_hour - ptmEarlier->tm_hour) * 3600 +
+ (ptmLater->tm_min - ptmEarlier->tm_min ) * 60 +
+ (ptmLater->tm_sec - ptmEarlier->tm_sec );
+}
+
+void TmUtcToLocal(struct tm* ptm)
+{
+ //Establish DST
+ ptm->tm_isdst = isDst(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, ptm->tm_wday, ptm->tm_hour);
+
+ //Adjust for the timezone
+ ptm->tm_hour += ptm->tm_isdst ? DST_OFFSET : STD_OFFSET;
+ normalise(&ptm->tm_hour, &ptm->tm_wday, &ptm->tm_mday, &ptm->tm_mon, &ptm->tm_yday, &ptm->tm_year);
+}
+
+void TmFromAsciiDateTime(const char* pDate, const char* pTime, struct tm* ptm) // Convert compile time to system time
+{
+ //__DATE__ The string constant contains eleven characters and looks like "Feb 12 1996". If the day of the month is less than 10, it is padded with a space on the left.
+ char month[5];
+ sscanf(pDate, "%s %d %d", month, &ptm->tm_mday, &ptm->tm_year); ptm->tm_year -= 1900;
+
+ // Find where month is in month_names. Deduce month value.
+ static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+ ptm->tm_mon = (strstr(month_names, month) - month_names) / 3;
+
+ //__TIME__ The string constant contains eight characters and looks like "23:59:01".
+ sscanf(pTime, "%2d %*c %2d %*c %2d", &ptm->tm_hour, &ptm->tm_min, &ptm->tm_sec);
+
+ //Fill the day of week and the day of year part of the tm structure
+ calculateDayOfYearAndWeek(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, &ptm->tm_yday, &ptm->tm_wday);
+}