1 /** Contains functions for converting an exact date to a fuzzy human readable form.
2 */
3 module fuzzydate;
4 
5 import std.datetime : Clock, SysTime, UTC;
6 
7 /** Converts a certain date/time to a fuzzy string.
8 
9 	Params:
10 		time = The point in time to convert to a fuzzy string
11 		now = Optional time value to customize the reference time, i.e. the present
12 		dst = Output range to write the result into
13 
14 	Returns:
15 		The non-range based overloads will return the fuzzy representation as a `string`
16 */
17 string toFuzzyDate(SysTime time)
18 {
19 	return toFuzzyDate(time, Clock.currTime(UTC()));
20 }
21 
22 /// ditto
23 string toFuzzyDate(SysTime time, SysTime now)
24 {
25 	import std.array : appender;
26 	auto app = appender!string();
27 	app.toFuzzyDate(time, now);
28 	return app.data;
29 }
30 
31 /// ditto
32 void toFuzzyDate(R)(ref R dst, SysTime time)
33 {
34 	toFuzzyDate(dst, time, Clock.currTime(UTC()));
35 }
36 
37 /// ditto
38 void toFuzzyDate(R)(ref R dst, SysTime time, SysTime now)
39 {
40 	import core.time : dur;
41 	import std.format : formattedWrite;
42 
43 	auto tm = now - time;
44 	if (tm < dur!"seconds"(0)) dst.put("still going to happen");
45 	else if (tm < dur!"seconds"(10)) dst.put("just now");
46 	else if (tm < dur!"minutes"(1)) dst.put("less than a minute ago");
47 	else if (tm < dur!"minutes"(2)) dst.put("a minute ago");
48 	else if (tm < dur!"hours"(1)) dst.formattedWrite("%s minutes ago", tm.total!"minutes"());
49 	else if (tm < dur!"hours"(2)) dst.put("an hour ago");
50 	else if (tm < dur!"days"(1)) dst.formattedWrite("%s hours ago", tm.total!"hours"());
51 	else if (tm < dur!"days"(2)) dst.put("a day ago");
52 	else if (tm < dur!"weeks"(5)) dst.formattedWrite("%s days ago", tm.total!"days"());
53 	else if (tm < dur!"weeks"(52)) {
54 		auto m1 = time.month;
55 		auto m2 = now.month;
56 		auto months = (now.year - time.year) * 12 + m2 - m1;
57 		if (months == 1) dst.put("a month ago");
58 		else dst.formattedWrite("%s months ago", months);
59 	} else if (now.year - time.year <= 1) dst.put("a year ago");
60 	else dst.formattedWrite("%s years ago", now.year - time.year);
61 }
62 
63 ///
64 unittest {
65 	SysTime tm(string timestr) { return SysTime.fromISOExtString(timestr); }
66 	assert(toFuzzyDate(tm("2015-11-12T19:59:59Z"), tm("2015-11-12T20:00:00Z")) == "just now");
67 	assert(toFuzzyDate(tm("2015-11-12T19:58:55Z"), tm("2015-11-12T20:00:00Z")) == "a minute ago");
68 	assert(toFuzzyDate(tm("2015-11-02T12:00:00Z"), tm("2015-11-12T20:00:00Z")) == "10 days ago");
69 	assert(toFuzzyDate(tm("2014-06-06T12:00:00Z"), tm("2015-11-12T20:00:00Z")) == "a year ago");
70 }