1 /**
2  * Proleptic Gregorian calendar: time
3  *
4  * Copyright: Maxim Freck, 2016–2017.
5  * Authors:   Maxim Freck <maxim@freck.pp.ru>
6  * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  */
8 module pgc.time;
9 
10 public import pgc.exception;
11 
12 alias Time = uint;
13 
14 /***********************************
15  * Returns: Time uint for a given time value (hour, minute, second and split)
16  *
17  * Params:
18  *  hour   = the number of the hour
19  *  minute = the number of the minute
20  *  second = the number of the second
21  *  split  = the number of the split (1/32767 of a second)
22  */
23 pure Time mkTime(int hour, int minute, int second, int split = 0) @safe
24 {
25 	assertTime(hour, minute, second, split);
26 	return (((hour & 0x1F) << 27) | ((minute & 0x3F) << 21) | ((second & 0x3F) << 15) | (split & 0x3FFF));
27 }
28 
29 /***********************************
30  * Checks the validity of a time
31  *
32  * Params:
33  *  hour  = the number of the hour
34  *  month = the number of the month
35  *  year  = the number of the year
36  */
37 pure private void assertTime(int hour, int minute, int second, int split = 0) @safe
38 {
39 	import std.conv: to;
40 	if (hour < 0 || hour > 23) throw new PgcException("Invalid hour value: "~to!string(hour));
41 	if (minute < 0 || minute > 59) throw new PgcException("Invalid minute value: "~to!string(minute));
42 	if (second < 0 || second > 59) throw new PgcException("Invalid second value: "~to!string(second));
43 	if (split < 0 || split > 32_768) throw new PgcException("Split "~to!string(split)~" is out of bound [0..32768]");
44 }
45 
46 /***********************************
47  * Returns: the hour of a given time
48  *
49  * Params:
50  *  t = time
51  */
52 pure nothrow ubyte hour(Time t) @safe @nogc
53 {
54 	return cast(ubyte)((t >> 27) & 0x1F);
55 }
56 
57 /***********************************
58  * Returns: the minute of a given time
59  *
60  * Params:
61  *  t = time
62  */
63 pure nothrow ubyte minute(Time t) @safe @nogc
64 {
65 	return cast(ubyte)((t >> 21) & 0x3F);
66 }
67 
68 /***********************************
69  * Returns: the second of a given time
70  *
71  * Params:
72  *  t = time
73  */
74 pure nothrow ubyte second(Time t) @safe @nogc
75 {
76 	return cast(ubyte)((t >> 15) & 0x3F);
77 }
78 
79 /***********************************
80  * Returns: the split (1/32767) of a given time
81  *
82  * Params:
83  *  t = time
84  */
85 pure nothrow ushort split(Time t) @safe @nogc
86 {
87 	return cast(ushort)(t & 0x7FFF);
88 }
89 
90 version(Posix) {
91 	import core.sys.posix.sys.time: gettimeofday, gmtime, localtime, timeval;
92 
93 	/***********************************
94 	 * Returns: the current time in UTC
95 	 */
96 	Time nowUTC()
97 	{
98 		timeval time;
99 		gettimeofday(&time, null);
100 		auto tm = gmtime(&time.tv_sec);
101 
102 		return mkTime(tm.tm_hour, tm.tm_min, tm.tm_sec, cast(int)(time.tv_usec/31));
103 	}
104 
105 	/***********************************
106 	 * Returns: the current local time
107 	 */
108 	Time nowLocal()
109 	{
110 		timeval time;
111 		gettimeofday(&time, null);
112 		auto tm = localtime(&time.tv_sec);
113 
114 		return mkTime(tm.tm_hour, tm.tm_min, tm.tm_sec, cast(int)(time.tv_usec/31));
115 	}
116 }
117 
118 version(Windows) {
119 	import core.sys.windows.windows: GetLocalTime, GetSystemTime, SYSTEMTIME;
120 
121 	/***********************************
122 	 * Returns: the current time in UTC
123 	 */
124 	Time nowUTC()
125 	{
126 		SYSTEMTIME time;
127 		GetSystemTime(&time);
128 		return mkTime(time.wHour, time.wMinute, time.wSecond, time.wMilliseconds*16);
129 	}
130 
131 	/***********************************
132 	 * Returns: the current local time
133 	 */
134 	Time nowLocal()
135 	{
136 		SYSTEMTIME time;
137 		GetLocalTime(&time);
138 		return mkTime(time.wHour, time.wMinute, time.wSecond, time.wMilliseconds*16);
139 	}
140 }