時刻のみをシリアライズしたい


DateTimeをシリアライズするためのメソッドDateTime.ToBinary()がありますが、  
DateTime型は日付が含まれるため、日付が必要ない場合に無駄になってしまいます。

（Hour以下のTicksの最大値は863,999,999,999なので5バイトで表現可能。  
少なくとも今の私の仕事では8バイトと5バイトの差は大きいです）

時刻のみをシリアライズできるようなライブラリが見当たらなかったため、  
とりあえず作ってみたものが以下になります。

[code]

    class Program
    {
        static void Main()
        {
            var now = DateTime.UtcNow;
            var bytes = TimeSerializer.Serialize(now);
            var now2 = TimeSerializer.Deserialize(bytes, now.Date);
    
            Console.WriteLine(now.ToString("yyyy/MM/dd HH:mm:ss.fffffff"));
            Console.WriteLine(now2.ToString("yyyy/MM/dd HH:mm:ss.fffffff"));
        }
    }
    
    static class TimeSerializer
    {
        public static byte[] Serialize(DateTime time)
        {
            return Serialize((time - time.Date).Ticks);
        }
    
        public static byte[] Serialize(long ticks)
        {
            var bytes = BitConverter.GetBytes(ticks);
            return bytes.Take(5).ToArray();
        }
    
        public static DateTime Deserialize(byte[] bytes, DateTime date)
        {
            return date.AddTicks(Deserialize(bytes));
        }
    
        public static long Deserialize(byte[] bytes)
        {
            var longBytes = new byte[8] { bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], 0, 0, 0 };
            return BitConverter.ToInt64(longBytes, 0);
        }
    }
    
[/code]

これよりマシな方法はありますか？  
またはこういったことができるライブラリはありますか？




> 時刻のデータがかなりの割合を占めるSQLiteのファイルサイズを抑えるため

このコメントで質問内容がほとんど別物になってしまいました。  
SQLite3の`INTEGER`は

> **INTEGER**. The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8
> bytes depending on the magnitude of the value.

と説明されています。`DateTime.TimeOfDay.Ticks`を格納するだけで値に応じた適切なサイズで格納されます。  
（と言っても大半は6バイトになりそうですが。）

* * *

> こういったことができるライブラリはありますか？

直接的にはありません。ある人は５バイトがよく、またある人は６バイト、別の人は精度を減らして４バイト、など環境により求められるものは様々です。結局は必要な人が必要なものを作るのが一番です。

> これよりマシな方法はありますか？

元コードはlittle endianに依存しています。big
endian環境で先頭５バイトを参照しても意味がありません。また`BitConverter`で８バイト配列を作成してから改めて５バイト配列を作り直すのは非効率です。たった５バイトですし直接操作した方が早く、速いはずです。

[code]

    static class TimeSerializer {
        public static byte[] SerializeTime(this DateTime time) {
            var ticks = time.TimeOfDay.Ticks;
            return new byte[] { (byte)ticks, (byte)(ticks >> 8), (byte)(ticks >> 16), (byte)(ticks >> 24), (byte)(ticks >> 32) };
        }
        public static DateTime Deserialize(byte[] bytes, DateTime date) {
            if (bytes == null)
                throw new ArgumentNullException(nameof(bytes));
            if (bytes.Length < 5)
                throw new ArgumentOutOfRangeException(nameof(bytes));
            var ticks = bytes[0] | (uint)bytes[1] << 8 | (uint)bytes[2] << 16 | (uint)bytes[3] << 24 | (long)bytes[4] << 32;
            return date.AddTicks(ticks);
        }
    }
    
[/code]

> 少なくとも今の私の仕事では8バイトと5バイトの差は大きいです

これは大量のデータを扱うことを意味しての発言でしょうか？
その場合は５バイト配列を何度も作ったり、それを結合したりする行為は効率が悪いので、配列とオフセット値でアクセス可能なオーバーロードも用意した方がいいでしょう。




> 時刻のデータがかなりの割合を占めるSQLiteのファイルサイズを抑えるため

このコメントで質問内容がほとんど別物になってしまいました。  
SQLite3の`INTEGER`は

> **INTEGER**. The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8
> bytes depending on the magnitude of the value.

と説明されています。`DateTime.TimeOfDay.Ticks`を格納するだけで値に応じた適切なサイズで格納されます。  
（と言っても大半は6バイトになりそうですが。）

* * *

> こういったことができるライブラリはありますか？

直接的にはありません。ある人は５バイトがよく、またある人は６バイト、別の人は精度を減らして４バイト、など環境により求められるものは様々です。結局は必要な人が必要なものを作るのが一番です。

> これよりマシな方法はありますか？

元コードはlittle endianに依存しています。big
endian環境で先頭５バイトを参照しても意味がありません。また`BitConverter`で８バイト配列を作成してから改めて５バイト配列を作り直すのは非効率です。たった５バイトですし直接操作した方が早く、速いはずです。

[code]

    static class TimeSerializer {
        public static byte[] SerializeTime(this DateTime time) {
            var ticks = time.TimeOfDay.Ticks;
            return new byte[] { (byte)ticks, (byte)(ticks >> 8), (byte)(ticks >> 16), (byte)(ticks >> 24), (byte)(ticks >> 32) };
        }
        public static DateTime Deserialize(byte[] bytes, DateTime date) {
            if (bytes == null)
                throw new ArgumentNullException(nameof(bytes));
            if (bytes.Length < 5)
                throw new ArgumentOutOfRangeException(nameof(bytes));
            var ticks = bytes[0] | (uint)bytes[1] << 8 | (uint)bytes[2] << 16 | (uint)bytes[3] << 24 | (long)bytes[4] << 32;
            return date.AddTicks(ticks);
        }
    }
    
[/code]

> 少なくとも今の私の仕事では8バイトと5バイトの差は大きいです

これは大量のデータを扱うことを意味しての発言でしょうか？
その場合は５バイト配列を何度も作ったり、それを結合したりする行為は効率が悪いので、配列とオフセット値でアクセス可能なオーバーロードも用意した方がいいでしょう。

