aboutsummaryrefslogtreecommitdiff
path: root/src/date.c
blob: f81b69f28316c4ec3ac0bcd925f84b1b31d1b05c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <time.h>

#include <posthaste/date.h>

ph_date_t date_from_string(const char str[static 11])
{
	unsigned year  = (str[0] - '0') * 1000
	                 + (str[1] - '0') * 100
	                 + (str[2] - '0') * 10
	                 + (str[3] - '0');

	unsigned month = (str[5] - '0') * 10
	                 + (str[6] - '0');

	unsigned day   = (str[8] - '0') * 10
	                 + (str[9] - '0');

	return date_from_numbers(year, month, day);
}

ph_date_t date_from_numbers(unsigned year, unsigned month, unsigned day)
{
	return year << 9 | month << 5 | day;
}

struct tm tm_from_date(ph_date_t date)
{
	unsigned year = 0;
	unsigned month = 0;
	unsigned day = 0;
	date_split(date, &year, &month, &day);

	struct tm time = {.tm_year = year - 1900, .tm_mon = month - 1,
		          .tm_mday = day};
	mktime(&time);
	return time;
}

ph_date_t date_from_tm(struct tm time)
{
	unsigned year = time.tm_year + 1900;
	unsigned month = time.tm_mon + 1;
	unsigned day = time.tm_mday;
	return date_from_numbers(year, month, day);
}

ph_date_t current_date()
{
	time_t t = time(NULL);
	struct tm *time = localtime(&t);
	unsigned year = time->tm_year + 1900;
	unsigned month = time->tm_mon + 1;
	unsigned day = time->tm_mday;
	return date_from_numbers(year, month, day);
}

void date_split(ph_date_t date, unsigned *year, unsigned *month, unsigned *day)
{
	if (year) *year  = date >> 9;
	if (month) *month = (date >> 5) & 0xf;
	if (day) *day   = date & 0x1f;
}

void date_to_string(char str[static 11], ph_date_t date)
{
	unsigned year, month, day;
	date_split(date, &year, &month, &day);

	str[0] = '0' + (year / 1000) % 10;
	str[1] = '0' + (year / 100) % 10;
	str[2] = '0' + (year / 10) % 10;
	str[3] = '0' + year % 10;
	str[4] = '-';
	str[5] = '0' + (month / 10) % 10;
	str[6] = '0' + month % 10;
	str[7] = '-';
	str[8] = '0' + (day / 10) % 10;
	str[9] = '0' + day % 10;
	str[10] = '\0';
}

bool date_valid(ph_date_t date)
{
	unsigned year, month, day;
	date_split(date, &year, &month, &day);

	struct tm tm = {0};
	tm.tm_year = year;
	/* goddammit */
	tm.tm_mon = month - 1;
	tm.tm_mday = day;

	time_t r = mktime(&tm);
	/* if mktime fails to represent our time, it returns -1. mktime somewhat
	 * curiously accepts dates outside regular ranges, like negative time,
	 * but adjusts the tm structure to be 'correct', so we can check if any
	 * of the times were modified to see if this is a valid time. */
	if (r == -1 || (int)year != tm.tm_year
	    || (int)month != tm.tm_mon + 1|| (int)day != tm.tm_mday)
		return false;

	return true;
}