]> Pileus Git - lackey/blob - src/date.c
Add support for timezones
[lackey] / src / date.c
1 /*
2  * Copyright (C) 2012 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /* Time Keeping Bugs Abound! */
19
20 #define _POSIX_C_SOURCE 200112L
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "util.h"
28 #include "conf.h"
29 #include "date.h"
30
31 /* Global data */
32 date_t  NOW;
33 date_t  SEL;
34
35 /* Initialize */
36 void date_init(void)
37 {
38         /* Sync current time */
39         NOW = get_date(0);
40
41         /* Sync selection */
42         SEL.year  = NOW.year;
43         SEL.month = NOW.month;
44         SEL.day   = NOW.day;
45 }
46
47 void date_config(const char *group, const char *name, const char *key, const char *value)
48 {
49         if (match(group, "date") && match(key, "timezone") && value)
50                 setenv("TZ", get_string(value), 1);
51 }
52
53 void date_sync(void)
54 {
55         NOW = get_date(0);
56 }
57
58 /* Time functions */
59 int is_leap_year(year_t year)
60 {
61         return (year % 400 == 0) ? 1 :
62                (year % 100 == 0) ? 0 :
63                (year % 4   == 0) ? 1 : 0;
64 }
65
66 int days_in_year(year_t year)
67 {
68         return 365 + is_leap_year(year);
69 }
70
71 int days_in_month(year_t year, month_t month)
72 {
73         static int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
74         int days = mdays[month];
75         if (month == FEB)
76                 days += is_leap_year(year);
77         return days;
78 }
79
80 int weeks_in_month(year_t year, month_t month)
81 {
82         int start = start_of_month(year, month);
83         int days  = days_in_month(year, month);
84         return ((start + days)-1) / 7 + 1;
85 }
86
87 wday_t day_of_week(year_t year, month_t month, day_t day)
88 {
89         static int tmp[] = {0, 3, 2, 5, 0, 3,
90                             5, 1, 4, 6, 2, 4};
91         if (month < MAR)
92                 year--;
93         int start = year + year / 4
94                          - year / 100
95                          + year / 400
96                          + tmp[month];
97         return (start + day + 1) % 7;
98 }
99
100 wday_t start_of_month(year_t year, month_t month)
101 {
102         return day_of_week(year, month, 0);
103 }
104
105 void add_days(year_t *year, month_t *month, day_t *day, int days)
106 {
107         date_t date = {
108                 .year  = *year,
109                 .month = *month,
110                 .day   = *day,
111                 .hour  = 12
112         };
113         stamp_t stamp = get_stamp(date);
114         date_t  then  = get_date(stamp + days*24*60*60);
115         *year  = then.year;
116         *month = then.month;
117         *day   = then.day;
118 }
119
120 void add_months(year_t *year, month_t *month, int months)
121 {
122         int total = *year*12 + *month + months;
123         *year  = total / 12;
124         *month = total % 12;
125 }
126
127 /* Date functions */
128 date_t get_date(stamp_t stamp)
129 {
130         time_t t = stamp ? stamp : time(NULL);
131         struct tm *tm = localtime(&t);
132         date_t date = {
133                 .year  = tm->tm_year+1900,
134                 .month = tm->tm_mon,
135                 .day   = tm->tm_mday-1,
136                 .hour  = tm->tm_hour,
137                 .min   = tm->tm_min,
138                 .sec   = tm->tm_sec,
139         };
140         return date;
141 }
142
143 stamp_t get_stamp(date_t date)
144 {
145         struct tm tm = {
146                 .tm_year = date.year-1900,
147                 .tm_mon  = date.month,
148                 .tm_mday = date.day+1,
149                 .tm_hour = date.hour,
150                 .tm_min  = date.min,
151                 .tm_sec  = date.sec,
152         };
153         time_t t = mktime(&tm);
154         return (stamp_t)t;
155 }
156
157 int get_mins(date_t *start, date_t *end)
158 {
159         return (get_stamp(*end)-get_stamp(*start))/60;
160 }
161
162 int compare(date_t *a, date_t *b)
163 {
164         int rval = a->year  < b->year  ? -1 : a->year  > b->year  ? 1 :
165                    a->month < b->month ? -1 : a->month > b->month ? 1 :
166                    a->day   < b->day   ? -1 : a->day   > b->day   ? 1 :
167                    a->hour  < b->hour  ? -1 : a->hour  > b->hour  ? 1 :
168                    a->min   < b->min   ? -1 : a->min   > b->min   ? 1 : 0;
169         return rval;
170 }
171
172 int same_day(date_t *a, date_t *b)
173 {
174         return a->year  == b->year  &&
175                a->month == b->month &&
176                a->day   == b->day;
177 }
178
179 int before(date_t *start, int year, int month, int day, int hour, int min)
180 {
181         return compare(start, &(date_t){year, month, day, hour, min}) < 0;
182 }
183
184 int all_day(date_t *start, date_t *end)
185 {
186         date_t test = *start;
187         add_days(&test.year, &test.month, &test.day, 1);
188         return compare(&test, end) <= 0;
189 }
190
191 int no_date(date_t *date)
192 {
193         return date->year == 0;
194 }
195
196 /* Debug functions */
197 const char *month_to_str(month_t month)
198 {
199         static const char *map[] =
200                 { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
201                   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", };
202         return map[month%12];
203 }
204 const char *month_to_string(month_t month)
205 {
206         static const char *map[] =
207                 { "January",   "February", "March",    "April",
208                   "May",       "June",     "July",     "August",
209                   "September", "October",  "November", "December" };
210         return map[month%12];
211 }
212
213 const char *day_to_st(wday_t day)
214 {
215         static const char *map[] =
216                 { "Su","Mo", "Tu", "We", "Th", "Fr", "Sa" };
217         return map[day%7];
218 }
219 const char *day_to_str(wday_t day)
220 {
221         static const char *map[] =
222                 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
223         return map[day%7];
224 }
225 const char *day_to_string(wday_t day)
226 {
227         static const char *map[] =
228                 { "Sunday",   "Monday", "Tuesday", "Wednesday",
229                   "Thursday", "Friday", "Saturday" };
230         return map[day%7];
231 }
232
233 /* Test functions */
234 void date_test(void)
235 {
236         setenv("TZ", "US/Central", 1);
237
238         time_t  timet = time(NULL);
239         date_t  date  = get_date(timet);
240         stamp_t stamp = get_stamp(date);
241
242         printf("Time\n");
243         printf("  time  %ld\n",  timet);
244         printf("  stamp %lld\n", stamp);
245         printf("  date  %04d-%02d-%02d %02d:%02d %02ds\n",
246                         date.year, date.month, date.day,
247                         date.hour, date.min,   date.sec);
248
249         printf("Info\n");
250         printf("  Year   Month       Start   Weeks   Days\n");
251         for (int y = 2012; y <= 2012; y++)
252         for (int m = JAN;  m <= DEC;  m++) {
253                 printf("  %-5d",  y);
254                 printf("  %-10s", month_to_string(m));
255                 printf("  %-6s",  day_to_str(start_of_month(y,m)));
256                 printf("  %-6d",  weeks_in_month(y,m));
257                 printf("  %-2d",  days_in_month(y,m));
258                 printf("\n");
259         }
260 }