And of course a 7-segment display, who needs a clockface without a 7-segment display.
Ok I’ll stop doing this now, it’s a bit crazy 
using System;
using UnityEngine;
using TimeUtil = UnityEngine.Time;
using Debug = UnityEngine.Debug;
public class ReliableTime : MonoBehaviour {
[SerializeField] [Range(0f, 100f)] float _timeScale = 1f;
private const int SIXTY = 60;
private const double TOLERANCE = 1E-2;
static ReliableTime _instance;
static public ReliableTime Instance => _instance;
float _lastTime; // in seconds
float _measTime; // in seconds
int _minRegister; // in minutes
public float Time => SIXTY * _minRegister + _measTime;
public double TimeAsDouble => SIXTY * (double)_minRegister + (double)_measTime;
public (int h, int m, float s) GetTime()
=> ( (int)(_minRegister / (float)SIXTY),
_minRegister % SIXTY,
_measTime );
void Awake() {
_instance = this;
setValues(TimeUtil.timeAsDouble);
_lastTime = _measTime;
}
void setValues(double absTime) {
_measTime = (float)(absTime % SIXTY);
_minRegister = (int)(absTime / SIXTY);
}
void Update() {
TimeUtil.timeScale = _timeScale;
_measTime += TimeUtil.deltaTime;
if(abs(_measTime - _lastTime) >= 1f) {
_lastTime = floor(_measTime);
if(_timeScale <= 1f) {
var time = GetTime();
Debug.Log($"{time.h}:{time.m}:{time.s:F3}");
}
var measured = TimeAsDouble;
var lapsed = TimeUtil.timeAsDouble;
if(Math.Abs(measured - lapsed) > TOLERANCE) {
var next = (measured + lapsed) / 2f;
Debug.Log($"correction: was {measured:F4} now {lapsed:F4}");
setValues(lapsed);
}
if(_measTime >= SIXTY) {
_measTime -= SIXTY;
_minRegister++;
}
}
}
//----------------------------------------
static readonly float D90 = .5f * MathF.PI;
static readonly float TAU = 4f * D90;
void OnDrawGizmos() {
var t = Time;
var pos = transform.position;
drawClockMarks(pos, 1f, Color.cyan, Color.white);
var sec = (int)t % 60f;
var min = (int)t / 60f % 60f;
var hrs = (int)t / 3600f % 12f;
draw7SegmentValue(new Vector3(.12f, .35f, 0f), .02f, .07f, .01f, (int)hrs, Color.yellow);
draw7SegmentValue(new Vector3(.34f, .35f, 0f), .02f, .07f, .01f, (int)min, Color.yellow);
draw7SegmentValue(new Vector3(.56f, .35f, 0f), .02f, .07f, .01f, (int)sec, Color.yellow);
drawDial(pos, 1f, 0f, .55f, D90 - TAU / 12f * hrs, Color.white);
drawDial(pos, 1f, 0f, .8f, D90 - TAU / 60f * min, Color.blue);
drawDial(pos, 1f, 0f, .9f, D90 - TAU / 60f * easedSecondsDial(t), Color.red);
var msc = frac(t) * 1000f;
var microDial = new Vector3(-1f, 1f, 0f) / 3f;
drawDial(pos + microDial, 1f, 0f, .22f, D90 - TAU / 1000f * msc, Color.white);
drawCircle(pos + microDial, .22f, segments: 24);
drawPlrSine(pos + .1f * Vector3.up, new Vector3(2f, -.8f, 1.2f), pos + new Vector3(-.6f, -.333f, 0f), new Vector2(.6f, .1f), 6f * t, 6f * t + 12f * TAU, segments: 96, color: Color.yellow);
drawPlrSine(pos, new Vector3(TAU * 5f / 6f, .2f, 0f), pos + new Vector3(-.6f, -.333f, 0f), new Vector2(.6f, .1f), -6f * t, -6f * t + 6f * TAU, segments: 24, color: Color.gray);
drawCircle(pos, 1f, color: Color.cyan);
}
void drawPlrSine(Vector3 o, Vector3 s, Vector3 c, Vector2 scale, float min, float max, int segments = 48, Color? color = null) {
if(color.HasValue) Gizmos.color = color.Value;
var last = Vector3.zero;
var step = (max - min) / segments;
for(int i = 0; i <= segments; i++) {
var next = mad(1f, new Vector3(2f * scale.x / segments * i, scale.y * sin(i * step + min), 0f), c);
if(i > 0) drawSeg(polar(ref o, ref s, last), polar(ref o, ref s, next));
last = next;
}
static Vector3 polar(ref Vector3 c, ref Vector3 scale, Vector3 p) {
p -= c; return mad(scale.y * (p.y + scale.z), trig(scale.x * p.x + D90), c);
}
}
// void drawSine(Vector3 c, Vector2 scale, float min, float max, int segments = 48, Color? color = null) {
// if(color.HasValue) Gizmos.color = color.Value;
// var last = Vector3.zero;
// var step = (max - min) / segments;
// for(int i = 0; i <= segments; i++) {
// var next = mad(1f, new Vector3(2f * scale.x / segments * i, scale.y * sin(i * step + min), 0f), c);
// if(i > 0) drawSeg(last, next);
// last = next;
// }
// }
void drawClockMarks(Vector3 c, float r, Color color1, Color color2) {
for(int i = 0; i < SIXTY; i++) {
Color col;
float t1;
if(i % 5 == 0) {
col = (i % 3) == 0? color2 : color1;
t1 = (i % 3) == 0? .7f : .9f;
} else {
col = color1;
t1 = .96f;
}
drawDial(c, r, t1, 1f, TAU / SIXTY * i, col);
}
}
void drawDial(Vector3 c, float r, float t1, float t2, float rad, Color? color = null) {
if(color.HasValue) Gizmos.color = color.Value;
var q = mad(r, trig(rad), c);
drawSeg(lerp(c, q, t1), lerp(c, q, t2));
}
void drawCircle(Vector3 c, float r, int segments = 48, Color? color = null) {
if(color.HasValue) Gizmos.color = color.Value;
var last = Vector3.zero;
var step = TAU / segments;
for(int i = 0; i <= segments; i++) {
var next = mad(r, trig(i * step), c);
if(i > 0) drawSeg(last, next);
last = next;
}
}
void drawSeg(Vector3 a, Vector3 b, Color? color = null) {
if(color.HasValue) Gizmos.color = color.Value;
Gizmos.DrawLine(a, b);
}
static float easedSecondsDial(float n) {
n = mod(n + .7f - 1f, 60f);
var f = frac(n);
return floor(n) + (f < .5f? 0f : easeInOutBack(2f * (f - .5f)));
}
static float easeInOutBack(float n) {
const float c1 = 1.70158f;
const float c2 = c1 * 1.525f;
return n < .5f? (sqr(2f * n) * ((c2 + 1f) * 2f * n - c2)) * .5f
: (sqr(2f * n - 2f) * ((c2 + 1f) * (n * 2f - 2f) + c2) + 2f) * .5f;
}
void draw7SegmentValue(Vector3 p, float space, float scale, float slant, int value, Color color, int digits = 2) {
for(int i = digits - 1; i >= 0; i--) {
int pw = pow(10, i);
int digit = value / pw;
draw7SegmentDigit(p, scale, digit, slant, color);
value -= digit * pw;
p.x += scale + space;
}
}
// 0
// ___
// 5 | | 1
// +---+
// 4 | 6 | 2
// '---'
// 3
static Vector3[] _ppts;
static int[] _pieces = new int[] { 0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f };
void draw7SegmentDigit(Vector3 p, float scale, int digit, float slant, Color? color = null) {
if(_ppts is null) {
_ppts = new Vector3[6];
for(int y = 0; y < 3; y++)
for(int x = 0; x < 2; x++)
_ppts[2*y+x] = new Vector3(x, -y, 0f);
}
drawSeg(pt(0), pt(1), test(0));
drawSeg(pt(1), pt(3), test(1));
drawSeg(pt(3), pt(5), test(2));
drawSeg(pt(4), pt(5), test(3));
drawSeg(pt(2), pt(4), test(4));
drawSeg(pt(0), pt(2), test(5));
drawSeg(pt(2), pt(3), test(6));
Vector3 pt(int index) => p + scale * _ppts[index] + new Vector3(slant * _ppts[index].y, 0f);
Color test(int bit) => (color.HasValue && (_pieces[digit] & (1 << bit)) != 0)? color.Value
: default(Color);
}
static float abs(float n) => Math.Abs(n);
static float floor(float n) => MathF.Floor(n);
static float frac(float n) => n % 1f;
static float sin(float rad) => MathF.Sin(rad);
static float cos(float rad) => MathF.Cos(rad);
static Vector3 mad(float a, Vector3 b, Vector3 c) => a * b + c;
static Vector3 lerp(Vector3 a, Vector3 b, float t) => (1f - t) * a + t * b;
static Vector3 trig(float rad) => new Vector3(cos(rad), sin(rad), 0f);
static float sqr(float n) => n * n;
static float mod(float n, float m) => (n %= m) < 0f? m + n : n;
static int pow(float b, float p) => (int)MathF.Pow(b, p);
static float sat(float n) => Mathf.Clamp01(n);
}