.NET DateTimeからミリ秒を切り捨てる方法


334

着信リクエストからのタイムスタンプをデータベースに保存された値と比較しようとしています。SQL Serverはもちろん、時間の精度をミリ秒単位で維持します。.NETDateTimeに読み込まれると、それらのミリ秒が含まれます。ただし、システムへの着信要求はその精度を提供しないため、ミリ秒を単にドロップする必要があります。

明らかなものがないように感じますが、それを行うためのエレガントな方法が見つかりません(C#)。


回答の20%以来(第3回試して...)(123)を省略する方法を説明したり、フォーマットからコンポーネントミリ秒を取り除くstringの表現はDateTime、おそらく編集は、それが「切り捨て」にすることを明確にする必要があります/「ドロップ」ミリ秒の手段「が生じるDateTimeすべての日付/時刻コンポーネントは除いて同じである値TimeOfDay.TotalMillisecondsです0。」もちろん、人々は読むことはしませんが、あいまいさをなくすためだけに読みます。
BACON、

回答:


557

以下は、ミリ秒未満のDateTimeで機能し、Kindプロパティ(Local、Utc、またはUndefined)も保持します。

DateTime dateTime = ... anything ...
dateTime = new DateTime(
    dateTime.Ticks - (dateTime.Ticks % TimeSpan.TicksPerSecond), 
    dateTime.Kind
    );

または同等でより短い:

dateTime = dateTime.AddTicks( - (dateTime.Ticks % TimeSpan.TicksPerSecond));

これは拡張メソッドに一般化できます:

public static DateTime Truncate(this DateTime dateTime, TimeSpan timeSpan)
{
    if (timeSpan == TimeSpan.Zero) return dateTime; // Or could throw an ArgumentException
    if (dateTime == DateTime.MinValue || dateTime == DateTime.MaxValue) return dateTime; // do not modify "guard" values
    return dateTime.AddTicks(-(dateTime.Ticks % timeSpan.Ticks));
}

これは次のように使用されます。

dateTime = dateTime.Truncate(TimeSpan.FromMilliseconds(1)); // Truncate to whole ms
dateTime = dateTime.Truncate(TimeSpan.FromSeconds(1)); // Truncate to whole second
dateTime = dateTime.Truncate(TimeSpan.FromMinutes(1)); // Truncate to whole minute
...

あなたが技術的に正しいのでこれをあげますが、SQL Serverからデータを読んでいる人がいくつかの分散データ(私の場合はWebベースのリクエスト)と比較するために、この量の解決は必要ありません。
ジェフ・プッツ

1
いいね。明らかに、この種の優れたコーディングが再利用されるように、誰かがDateTimeクラスにいくつかの拡張メソッドを指定して、最も近いものに丸める必要があります。
chris.w.mclean 2009年

これは非常にありそうもありませんが、ティック= 0のときにこのアプローチは壊れませんか?
adotout 2013

@ adotout、timeSpanパラメータがゼロの場合、上記のTruncateメソッドはDivideByZeroExceptionをスローします。これは、「ティック= 0のときにアプローチが壊れる」という意味ですか?timeSpanがゼロの場合は、ArgumentExceptionをスローすることをお勧めします。
Joe

145
var date = DateTime.Now;

date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind);

34
明確で単純です。「、date.Kind」をコンストラクタの最後に追加して、重要な情報を失わないようにしてください。
JMcDaniel

9
パフォーマンスに敏感なコードでは、このソリューションに注意してください。私のアプリはSystem.DateTime.GetDatePartでCPU時間の12%を費やしていました。
大佐パニック

3
シンプルですが、ベストアンサーとしてマークされた質問よりも時間がかかります。これがボトルネックになることはありませんが、約7〜8倍遅くなります。
ジョナス

「はるかに遅い」ステートメントは正確ではありません。違いはランタイムによって50%から100%の間です。正味4.7.2:0.35µs対0.62 µsおよびコア3.1:0.18 µs対0.12 µsマイクロ秒(10 ^ -6秒)
juwens

62

これは、以前の回答に基づいた拡張メソッドであり、任意の解像度に切り捨てることができます...

使用法:

DateTime myDateSansMilliseconds = myDate.Truncate(TimeSpan.TicksPerSecond);
DateTime myDateSansSeconds = myDate.Truncate(TimeSpan.TicksPerMinute)

クラス:

public static class DateTimeUtils
{
    /// <summary>
    /// <para>Truncates a DateTime to a specified resolution.</para>
    /// <para>A convenient source for resolution is TimeSpan.TicksPerXXXX constants.</para>
    /// </summary>
    /// <param name="date">The DateTime object to truncate</param>
    /// <param name="resolution">e.g. to round to nearest second, TimeSpan.TicksPerSecond</param>
    /// <returns>Truncated DateTime</returns>
    public static DateTime Truncate(this DateTime date, long resolution)
    {
        return new DateTime(date.Ticks - (date.Ticks % resolution), date.Kind);
    }
}

1
これは本当に柔軟で再利用可能なソリューションであり、過度に冗長になることなく簡潔で表現力豊かです。最善の解決策としての私の投票。
Jaans 2015

2
実際には、%オペランドを括弧で囲む必要はありません。
ErikE 2016

8
..しかし、私の意見では、括弧は明快さを追加します。
orion elenzil

28
DateTime d = DateTime.Now;
d = d.AddMilliseconds(-d.Millisecond);

70
-1:DateTime値にミリ秒の端数が含まれていない場合にのみ機能します。
Joe

7
この方法を使用すると、一部の単体テストが失敗しました。予想:2010-05-05 15:55:49.000しかし、それは2010-05-05 15:55:49.000でした。私はジョーがミリ秒の端数について述べたことに推測している。
Seth Reno

6
シリアル化では機能しません。たとえば、2010-12-08T11:20:03.000099 + 15:00は出力であり、ミリ秒を完全に切り落としません。
joedotnot

5
このMillisecondプロパティは、0〜999の整数を指定します。したがって、操作前の時刻がであった場合、23:48:49.1234567その整数は123となり、操作後の時刻はになり23:48:49.0004567ます。そのため、整数秒に切り捨てられていません。
Jeppe Stig Nielsen

11

年や月など、カレンダーに基づくものに切り捨てたい場合があります。以下は、任意の解像度を選択できる拡張メソッドです。

public enum DateTimeResolution
{
    Year, Month, Day, Hour, Minute, Second, Millisecond, Tick
}

public static DateTime Truncate(this DateTime self, DateTimeResolution resolution = DateTimeResolution.Second)
{
    switch (resolution)
    {
        case DateTimeResolution.Year:
            return new DateTime(self.Year, 1, 1, 0, 0, 0, 0, self.Kind);
        case DateTimeResolution.Month:
            return new DateTime(self.Year, self.Month, 1, 0, 0, 0, self.Kind);
        case DateTimeResolution.Day:
            return new DateTime(self.Year, self.Month, self.Day, 0, 0, 0, self.Kind);
        case DateTimeResolution.Hour:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerHour));
        case DateTimeResolution.Minute:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMinute));
        case DateTimeResolution.Second:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerSecond));
        case DateTimeResolution.Millisecond:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMillisecond));
        case DateTimeResolution.Tick:
            return self.AddTicks(0);
        default:
            throw new ArgumentException("unrecognized resolution", "resolution");
    }
}

9

ミリ秒を落として比較するのではなく、違いを比較してみませんか?

DateTime x; DateTime y;
bool areEqual = (x-y).TotalSeconds == 0;

または

TimeSpan precision = TimeSpan.FromSeconds(1);
bool areEqual = (x-y).Duration() < precision;

3
TotalSecondsはdoubleであるため、最初のオプションは機能しません。また、ミリ秒も返します。
Jowen

1
違いを比較しても、切り捨てて比較するのと同じ結果にはなりません。たとえば、5.900と6.100は1秒未満の間隔なので、メソッドと同等に比較できます。ただし、切り捨てられた値5と6は異なります。どちらが適切かは、要件によって異なります。
Joe

7

それほど明白ではありませんが、2倍以上高速です。

// 10000000 runs

DateTime d = DateTime.Now;

// 484,375ms
d = new DateTime((d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

// 1296,875ms
d = d.AddMilliseconds(-d.Millisecond);

3
2番目のオプションはd.AddMilliseconds(-d.Millisecond)、DateTimeを必ずしも前の1秒に正確に移動するわけではないことに注意してください。d.Ticks % TimeSpan.TicksPerMillisecond秒を超えるティック(0から9,999の間)は残ります。
Technetium 2012

5

2番目に切り捨てるには:

dateTime.AddTicks(-dateTime.Ticks % TimeSpan.TicksPerSecond)

に置き換えてTicksPerMinute、分に切り捨てます。


コードがパフォーマンスに敏感な場合は、注意してください

new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second)

私のアプリはCPU時間の12%を費やしていました System.DateTime.GetDatePart


3

読みやすい方法は...

//Remove milliseconds
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm:ss"), "yyyy-MM-dd HH:mm:ss", null);

もっと...

//Remove seconds
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm"), "yyyy-MM-dd HH:mm", null);

//Remove minutes
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH"), "yyyy-MM-dd HH", null);

//and go on...

4
文字列に変換して解析することは、パフォーマンスの点でひどい考えです。
Jeff Putz

2
@JeffPutzは本当ですが、それ簡単です。DBから挿入およびプルされた値がティックを失う(私の正確な状況)自動テストに適しています。ただし、この答えは、問題なくvar now = DateTime.Parse(DateTime.Now.ToString())機能するため、実際よりも簡単な場合があります。
Grimm The Opiner 2017

1
@GrimmTheOpiner-「...正常に動作する」、ほとんどの場合、保証されません。機能:「現在のユーザーのコントロールパネルの設定で設定されているように、DateTimeを「Long time」で設定された精度に丸めます」。これは通常、秒です。
Joe

1
その単純さのように、パフォーマンスは自動テストの状況では問題になりません。
リアン

1

Diadistisの反応について。乗算の前に除算の小数部分を削除するためにFloorを使用する必要があったことを除いて、これは私にとってはうまくいきました。そう、

d = new DateTime((d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

なる

d = new DateTime(Math.Floor(d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

2つのLong値を除算するとLongになるため、小数部分が削除されると予想していましたが、乗算後に正確に同じ値を残してDoubleとして解決されました。

Eppsy


1

2上記のソリューションの拡張方法

    public static bool LiesAfterIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind)
    {
        DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind);
        compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind);

        return thisDate > compareDate;
    }


    public static bool LiesAfterOrEqualsIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind)
    {
        DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind);
        compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind);

        return thisDate >= compareDate;
    }

使用法:

bool liesAfter = myObject.DateProperty.LiesAfterOrEqualsIgnoringMilliseconds(startDateTime, DateTimeKind.Utc);

1

最速のソリューションではありませんが、シンプルで理解しやすい:

DateTime d = DateTime.Now;
d = d.Date.AddHours(d.Hour).AddMinutes(d.Minute).AddSeconds(d.Second)

0
DateID.Text = DateTime.Today.ToShortDateString();

Use ToShortDateString() //Date 2-02-2016
Use ToShortDateString() // Time 

そしての使用によって

ToLongDateString() // its show 19 February 2016.

:P


-1。質問がのstring代わりにを生成するように要求していると誤って解釈された可能性があることがわかりますDateTimeが、これにより出力から時間コンポーネントが完全に省略されます。(これにより、Todayプロパティへのアクセスも不要になります。)
BACON

0

新しい方法

String Date = DateTime.Today.ToString("dd-MMM-yyyy"); 

//文字列パスパラメータdd-mmm-yyyyを定義し、2016年2月24日を返す

またはテキストボックスに表示されます

txtDate.Text = DateTime.Today.ToString("dd-MMM-yyyy");

// PageonLoadを置く


-1。質問がのstring代わりにを生成するように要求していると誤って解釈された可能性があることがわかりますDateTimeが、これにより出力から時間コンポーネントが完全に省略されます。(これにより、Todayプロパティへのアクセスも不要になります。)
BACON

0

私の場合、秒とミリ秒を保存せずにdatetimePickerツールからTimeSpanを保存することを目指していましたが、これが解決策です。

最初に、datetimePicker.valueを目的の形式(「HH:mm」)に変換してから、TimeSpanに変換します。

var datetime = datetimepicker1.Value.ToString("HH:mm");
TimeSpan timeSpan = Convert.ToDateTime(datetime).TimeOfDay;

これを行うためのより良い方法(より明確な意図、へのフォーマットおよびからの構文解析を回避するstring)は、次のようになります。DateTime datetime = datetimepicker1.Value; TimeSpan timeSpan = new TimeSpan(datetime.Hour, datetime.Minute, 0); または、値を操作して秒を切り捨てるJoeの拡張メソッドのバリエーションを使用できます。TimeSpanTimeSpan timeSpan = datetime.TimeOfDay.Truncate(TimeSpan.FromSeconds(1));
BACON、

0

これは、ここと同様の質問に投稿された拡張メソッドの私のバージョンです。これにより、ティック値が読みやすく検証され、元のDateTimeインスタンスのDateTimeKindが保持されます。(MongoDBのようなデータベースに保存する場合、これには微妙ですが関連する副作用があります。)

真の目的が指定された値(つまり、時間/分/秒/ MS)にDateTimeを切り捨てることである場合、代わりにこの拡張メソッドをコードに実装することをお勧めします。これにより、有効な精度にのみ切り捨てることができ、元のインスタンスの重要なDateTimeKindメタデータが保持されます。

public static DateTime Truncate(this DateTime dateTime, long ticks)
{
    bool isValid = ticks == TimeSpan.TicksPerDay 
        || ticks == TimeSpan.TicksPerHour 
        || ticks == TimeSpan.TicksPerMinute 
        || ticks == TimeSpan.TicksPerSecond 
        || ticks == TimeSpan.TicksPerMillisecond;

    // /programming/21704604/have-datetime-now-return-to-the-nearest-second
    return isValid 
        ? DateTime.SpecifyKind(
            new DateTime(
                dateTime.Ticks - (dateTime.Ticks % ticks)
            ),
            dateTime.Kind
        )
        : throw new ArgumentException("Invalid ticks value given. Only TimeSpan tick values are allowed.");
}

次に、次のような方法を使用できます。

DateTime dateTime = DateTime.UtcNow.Truncate(TimeSpan.TicksPerMillisecond);

dateTime.Kind => DateTimeKind.Utc

-1

私は答えがかなり遅いことを知っていますが、ミリ秒を取り除くための最良の方法は

var currentDateTime = DateTime.Now.ToString("s");

変数の値を出力してみてください。ミリ秒なしで日付時刻が表示されます。


1
これは理想的ではありません。次に、DateTimeではなく文字列を取得します。
Jeff Putz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.