1 ///
2 module dpq.serialisers.systime;
3 
4 import std.datetime : SysTime, DateTime;
5 import std.typecons : Nullable;
6 import std.bitmanip;
7 import core.time;
8 import dpq.meta;
9 import dpq.serialisation;
10 import dpq.connection : Connection;
11 import libpq.libpq : Oid;
12 import dpq.value : Type;
13 
14 enum POSTGRES_EPOCH = DateTime(2000, 1, 1);
15 
16 struct SysTimeSerialiser
17 {
18 	static bool isSupportedType(T)()
19 	{
20 		return is(T == SysTime);
21 	}
22 
23 	static Nullable!(ubyte[]) serialise(T)(T val)
24 	{
25 		static assert (
26 				isSupportedType!T,
27 				"'%s' is not supported by SysTimeSerialiser".format(T.stringof));
28 
29 		alias RT = Nullable!(ubyte[]);
30 
31 		if (isAnyNull(val))
32 			return RT.init;
33 
34 		// stdTime is in hnsecs, psql wants microsecs
35 		long diff = val.stdTime - SysTime(POSTGRES_EPOCH).stdTime;
36 		return RT(nativeToBigEndian(diff / 10).dup);
37 	}
38 
39 	static T deserialise(T)(const (ubyte)[] bytes)
40 	{
41 		static assert (
42 				isSupportedType!T,
43 				"'%s' is not supported by SysTimeSerialiser".format(T.stringof));
44 
45 		return SysTime(fromBytes!long(bytes, long.sizeof) * 10 + SysTime(POSTGRES_EPOCH).stdTime);
46 	}
47 
48 	static Oid oidForType(T)()
49 	{
50 		return Type.TIMESTAMP;
51 	}
52 
53 	static string nameForType(T)()
54 	{
55 		return "TIMESTAMP";
56 	}
57 
58 	static void ensureExistence(T)(Connection c) 
59 	{
60 		return;
61 	}
62 }
63 
64 unittest
65 {
66 	import std.stdio;
67 	import std.datetime;
68 
69 	writeln(" * SysTimeSerialiser");
70 
71 	// In reality, we should probably only check up to msec accuracy,
72 	// and I'm not sure how much SysTimes == checks.
73 	SysTime time = Clock.currTime;
74 	auto serialised = SysTimeSerialiser.serialise(time);
75 
76 	assert(SysTimeSerialiser.deserialise!SysTime(serialised).toUnixTime == time.toUnixTime);
77 }