I made std.time for Phobos, please review my code.

Michel Fortin michel.fortin at michelf.com
Tue Apr 27 07:09:32 PDT 2010


On 2010-04-27 04:55:28 -0400, SHOO <zan77137 at nifty.com> said:

> By these reasons, I made std.time module as the first step of the
> contribution for Phobos.

Looks nice. I think defining structs as you did to handle date and time 
is the way to go.

I've done something similar in the past to store dates and times which 
I like a lot. What I did was just a storage format, but templates 
allowed me to store dates and time spans with any precision while 
exposing always the same public API.

I'm pasting my code below in case it can inspire you (note that this 
code predates the new operator overloading syntax), and feel free to 
use any of this under the Boost license if applicable.


import std.date : d_time, ticksPerMs;


template isDateTime(T) {
	enum bool isDateTime = __traits(compiles, T.tick);
}

template isTimeSpan(T) {
	enum bool isTimeSpan = __traits(compiles, T.ticks);
}

enum Unit {
	// Need to be from higher precision to lower precision
	tick = 1,
	millisecond = ticksPerMs,
	second = 1000 * millisecond,
	minute = 60 * second,
	hour = 60 * minute,
	day = 24 * hour,
}

private Unit smaller(Unit a, Unit b) {
	return a < b ? a : b;
}

struct TimeSpan(T = d_time, Unit _unit = Unit.ticks) {
	// Milliseconds since 1970-01-01 00:00:00.0 UTC
	private T units = 0;
	enum Unit unit = _unit;
	
	void opAssign(T)(const T other) if (is(typeof(other.unit) == Unit) && 
smaller(unit, other.unit) == unit) {
		units = other.units * (other.unit / unit);
	}
	
	bool opEquals(ref const TimeSpan value) const {
		return units == value.units;
	}
//	bool opEquals(const TimeSpan value) const {
//		return units == value.units;
//	}
	
	TimeSpan opNeg() const {
		TimeSpan t;
		t.units = -units;
		return t;
	}
	
	void opAddAssign(T)(const T other) if (is(typeof(other.unit) == Unit) 
&& smaller(unit, other.unit) == unit) {
		units += other.units * (other.unit / unit);
	}
	auto opAdd(T)(const T other) const {
		TimeSpan!(typeof(units + other.units), smaller(unit, other.unit)) t;
		t = this;
		t += other;
		return t;
	}
	
	void opSubAssign(T)(const T other) if (is(typeof(other.unit) == Unit) 
&& smaller(unit, other.unit) == unit) {
		units -= other.units * (other.unit / unit);
	}
	auto opSub(T)(const T other) const {
		TimeSpan!(typeof(units - other.units), smaller(unit, other.unit)) t;
		t = this;
		t -= other;
		return t;
	}
	
	void opMulAssign(const T value) {
		units *= value;
	}
	TimeSpan opMul(const T value) const {
		TimeSpan t = this;
		t *= value;
		return t;
	}
	
	void opDivAssign(const T value) {
		units /= value;
	}
	TimeSpan opDiv(const T value) const {
		TimeSpan t = this;
		t /= value;
		return t;
	}
	
	@property:
	T ticks() {
		static if (unit == Unit.tick)
			return units;
		else static if (unit < Unit.tick)
			static assert(0);
		else static if (unit > Unit.tick)
			return cast(T)(milliseconds * ticksPerMs);
	}
	static if (unit == Unit.tick) {
		void ticks(T value) {
			static if (unit == Unit.tick)
				units = value;
			else static if (unit < Unit.tick)
				static assert(0);
			else static if (unit > Unit.tick)
				milliseconds = value / ticksPerMs;
		}
	}
	
	T milliseconds() {
		static if (unit == Unit.millisecond)
			return units;
		else static if (unit < Unit.millisecond)
			return cast(T)(ticks / ticksPerMs);
		else static if (unit > Unit.millisecond)
			return cast(T)(seconds * 1000);
	}
	static if (unit <= Unit.millisecond) {
		void milliseconds(T value) {
			static if (unit == Unit.millisecond)
				units = value;
			else static if (unit < Unit.millisecond)
				ticks = value * ticksPerMs;
			else static if (unit > Unit.millisecond)
				seconds = value / 1000;
		}
	}
	
	T seconds() {
		static if (unit == Unit.second)
			return units;
		else static if (unit < Unit.second)
			return cast(T)(milliseconds / 1000);
		else static if (unit > Unit.second)
			return cast(T)(minutes * 60);
	}
	static if (unit <= Unit.second) {
		void seconds(T value) {
			static if (unit == Unit.second)
				units = value;
			else static if (unit < Unit.second)
				milliseconds = value * 1000;
			else static if (unit > Unit.second)
				minutes = value / 60;
		}
	}
	
	T minutes() {
		static if (unit == Unit.minute)
			return units;
		else static if (unit < Unit.minute)
			return cast(T)(seconds / 60);
		else static if (unit > Unit.minute)
			return cast(T)(hours * 60);
	}
	static if (unit <= Unit.minute) {
		void minutes(T value) {
			static if (unit == Unit.minute)
				units = value;
			else static if (unit < Unit.minute)
				seconds = value * 60;
			else static if (unit > Unit.minute)
				hours = value / 60;
		}
	}
	
	T hours() {
		static if (unit == Unit.hour)
			return units;
		else static if (unit < Unit.hour)
			return cast(T)(minutes / 60);
		else static if (unit > Unit.hour)
			return cast(T)(days * 24);
	}
	static if (unit <= Unit.hour) {
		void hours(T value) {
			static if (unit == Unit.hour)
				units = value;
			else static if (unit < Unit.hour)
				minutes = cast(T)(value * 60);
			else static if (unit > Unit.hour)
				days = cast(T)(value / 24);
		}
	}
	
	T days() {
		static if (unit == Unit.day)
			return units;
		else static if (unit < Unit.day)
			return cast(T)(hours / 24);
		else static if (unit > Unit.day)
			static assert(0);
	}
	static if (unit <= Unit.day) {
		void days(T value) {
			static if (unit == Unit.day)
				units = value;
			else static if (unit < Unit.day)
				hours = cast(T)(value * 24);
			else static if (unit > Unit.day)
				static assert(0);
		}
	}
}


TimeSpan!(T, Unit.millisecond) milliseconds(T)(T count) {
	TimeSpan!(T, Unit.millisecond) t;
	t.milliseconds = count;
	return t;
}
TimeSpan!(T, Unit.second) seconds(T)(T count) {
	TimeSpan!(T, Unit.second) t;
	t.seconds = count;
	return t;
}
TimeSpan!(T, Unit.minute) minutes(T)(T count) {
	TimeSpan!(T, Unit.minute) t;
	t.minutes = count;
	return t;
}
TimeSpan!(T, Unit.hour) hours(T)(T count) {
	TimeSpan!(T, Unit.hour) t;
	t.hours = count;
	return t;
}
TimeSpan!(T, Unit.day) days(T)(T count) {
	TimeSpan!(T, Unit.day) t;
	t.days = count;
	return t;
}

unittest {
//	assert(seconds(10) + minutes(1) == seconds(70));
	
	auto t = seconds(70);
	assert(seconds(10) + minutes(1) == t);
	assert(minutes(1) + seconds(10) == t);
	
	auto t2 = seconds(50);
	assert(minutes(1) - seconds(10) == t2);
}


struct UtcDateTime(T = d_time, Unit unit = Unit.tick) {
	// Time span since epoch
	TimeSpan!(T, unit) span;
	
	// The length in each unit since epoch.
	alias span.ticks ticks;
	alias span.milliseconds milliseconds;
	alias span.seconds seconds;
	alias span.minutes minutes;
	alias span.hours hours;
	alias span.days days;
	
	// The truncated value for each unit (seconds can never exceed 60)
	T millisecond() { return span.milliseconds % 1000; }
	T second() { return span.seconds % 60; }
	T minute() { return span.minutes % 60; }
	T hour() { return span.hours % 24; }
	T day() { return span.days; }
	
	// UTC Offset, always zero.
	enum offset = TimeSpan!(short, Unit.minute)();
}

struct DateTime(T = d_time, Unit unit = Unit.tick) {
	// Time span since epoch
	TimeSpan!(T, unit) span;
	
	// The length in each unit since epoch.
	alias span.ticks ticks;
	alias span.milliseconds milliseconds;
	alias span.seconds seconds;
	alias span.minutes minutes;
	alias span.hours hours;
	alias span.days days;
	
	// The truncated value for each unit (seconds can never exceed 60)
	T millisecond() { return span.milliseconds % 1000; }
	T second() { return span.seconds % 60; }
	T minute() { return span.minutes % 60; }
	T hour() { return span.hours % 24; }
	T day() { return span.days; }
	
	// UTC Offset
	TimeSpan!(short, Unit.minute) offset;
}

alias UtcDateTime!(int, Unit.tick) UtcTime;
alias DateTime!(int, Unit.tick) Time;

alias UtcDateTime!(int, Unit.day) UtcDate;
alias DateTime!(int, Unit.day) Date;

-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/



More information about the Digitalmars-d-announce mailing list