Timestamp Arithmetik

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 26 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Timestamp Arithmetik

    Hi,

    ich habe eine Anwendung, die alle Files anzeigen soll, deren letzte Änderung vor einer bestimmten Anzahl von Minuten oder Stunden oder Tagen oder Monaten oder Jahren .

    Dazu gebe ich ein intValue und strUnit, z.B. 5 DAYS oder 7 Month.

    Ich runde das aktuelle Datum auf die jeweilige strUnit und subtrahiere dann den entsprechenden intValue ...

    Ein Beispiel:

    aktueller Timestamp: 17.06.2016 13:09:25, Auswahl: 5 DAYS

    runden: 17.06.2016 00:00:00
    subtrahieren: 12.06.2016 00:00:00

    Hier ist mein Coding:

    VB.NET-Quellcode

    1. Private Function SetDate(blnChecked As Boolean, strUnit As String, intValue As Decimal) As DateTime
    2. Dim retval As DateTime = CDate("01.01.1900") 'Default is low date
    3. If blnChecked Then
    4. retval = DateTime.Now 'Get current timestamp
    5. Select Case strUnit
    6. Case "MINUTES"
    7. Dim wDateTime As String = retval.ToString("yyyy.MM.dd HH:mm") 'Truncate SECONDS
    8. Dim TSpan As New TimeSpan(0, 0, CInt(intValue), 0) 'Convert specifies minutes
    9. retval = CDate(wDateTime) - TSpan 'Subtract specified minutes
    10. Case "HOURS"
    11. Dim wDateTime As String = retval.ToString("yyyy.MM.dd HH") 'Truncate MINUTES
    12. Dim TSpan As New TimeSpan(0, CInt(intValue), 0, 0) 'Convert to specified hours
    13. retval = CDate(wDateTime) - TSpan 'Subtract specified hours
    14. Case "DAYS" : retval = retval
    15. Dim wDateTime As String = retval.ToString("yyyy.MM.dd") 'Truncate HOURS
    16. Dim TSpan As New TimeSpan(CInt(intValue), 0, 0, 0)
    17. retval = CDate(wDateTime) - TSpan
    18. Case "MONTHS"
    19. Dim wDateTime As String = retval.ToString("yyyy.MM.01") 'Truncate DAYS (set day = 1)
    20. Dim TSpan As New TimeSpan(CInt(intValue) * 30, 0, 0, 0) 'Subtract specified months (30 days not correct)
    21. retval = CDate(wDateTime) - TSpan
    22. Case "YEARS"
    23. Dim wDateTime As String = retval.ToString("yyyy.01.01") 'Truncate MONTHS and DAYS (01.01.)
    24. Dim TSpan As New TimeSpan(CInt(intValue) * 365, 0, 0, 0) 'Subtract specified years (365 days not correct)
    25. retval = CDate(wDateTime) - TSpan
    26. End Select
    27. End If
    28. Return retval
    29. End Function


    Die Routine gefällt mir nicht besonders gut. Erstens ist die Konvertierung in ein String Format mit nachfolgender Rückkonvertierung in das DateTime Format nicht gerade elegant.

    Und zweitens stimmt die Routine für MONTHS und YEARS nicht, weil ich 30 Tage für einen Monat und 365 Tage für ein Jahr annehme.


    Geht das irgendwie besser?

    LG
    Peter
    oh, bevor du da in die Logik gehst, hast du da noch jede Menge aufzuräumen:
    • blnChecked - was ist das für ein Nonsense-Name? Wird das ühaupt gebraucht - mir siehts so aus, dass wenn das false ist, dass dann die Methode so gut wie nichts macht.
    • verwende DateTime.Parse() statt CDate()
    • intValue As Decimal - wassis das fürn ... ;( ? Ist das nu ein Integer oder ein Decimal?
      Nomen est Omen!

    • Erstell ein Enum, statt da mit String-Schlüsseln im Select Case rumzuwursteln.
    Alles zu Enum kannste im Löffelmann-Buch nachlesen - Enums draufzuhaben ist Grundlage.

    Dann poste neuen Code (ich hab schon einen Ansatz, der nochmal ganz anders läuft)

    Peter329 schrieb:

    strUnit
    Da würde ich keine Strings, sondern ein Enum verwenden.
    Wenn die Strings als solche verwendet werden sollen, gib den Enums die korrekten Bezeichnungen und verwende dann MyEnum.ToString.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also erst mal wie immer vielen Dank an die Ratgeber! Ich hab mir schon gedacht, dass meine Routine nicht unbedingt das Gelbe vom Ei ist.

    Die Parameter für den Aufruf der Funktion stammen aus folgenden Controls:

    blnCheck: wird befüllt mit chkMinDate bzw. chkMaxDate. Mit dieser CheckBox kann man die Prüfung der unteren bzw. oberen Grenze aktivieren / deaktivieren ohne die Einstellungen zu löschen. Wenn das Check Feld nicht gesetzt ist, wird der Default Wert gesetzt unabhängig von den Einstellungen.

    intValue: wird befüllt aus numMinDate bzw. numMaxDate: dies ist ein NumericUpDown Control mit dem man die Anzahl einstellen kann. Das liefert eine DECIMAL Feld und kein INTEGER ... das hab ich erst später gemerkt und hab das zwar mit Cint() korrigiert aber ich hab vergessen den Namen zu ändern. Jetzt heißt der Parameter decValue.

    strUnit: wird befüllt aus cboMinDate bzw. cboMaxDate. Hier stelle ich die gewünschte Einheit ein.

    Ich hab nun versucht eure Ratschläge umzusetzen:

    VB.NET-Quellcode

    1. retval = retval.AddMinutes(CInt(-decValue)) 'Subtracts the specified amount e.g. MINUTES


    Das klappt hervorragend und sollte mein Problem vor allem mit MONTHS und YEARS lösen.

    Wie man eine ENUM kodiert weiß ich schon:

    VB.NET-Quellcode

    1. Public Enum Units As Integer
    2. MINUTES
    3. HOURS
    4. DAYS
    5. MONTHS
    6. YEARS
    7. End Enum


    Aber wie puzzle ich das jetzt mit meiner ComboBox zusammen ? Units.MINUTES.toString ?

    Und zu guter Letzt: Ich hab immer noch keine Ahnung, wie ich die Seconds, Minutes etc. abschneide, ohne in das String Format zu konvertieren.

    LG
    Peter
    Müsste iwie so aussehen:

    C#-Quellcode

    1. ​cb.DataSource = Enum.GetValues(typeof(MyEnum));
    2. //Später dann
    3. MyEnum selectedValue;
    4. Enum.TryParse<MyEnum>(cb.SelectedValue.ToString(), out selectedValue);
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
    Also das Befüllen der Combo Box aus der Enumeration klappt schon mal hervorragend:

    VB.NET-Quellcode

    1. 'Configure cbo Boxes
    2. cboMinDate.DataSource = [Enum].GetValues(GetType(Units))
    3. cboMaxDate.DataSource = [Enum].GetValues(GetType(Units))


    Und das ist dann die Abfrage auf die ausgewählte Unit:

    VB.NET-Quellcode

    1. Dim selectedValue As Units
    2. [Enum].TryParse(Of Units)(cboDate.SelectedValue.ToString(), selectedValue)
    3. Dim strUnit As String = selectedValue.ToString


    Recht herzlichen Dank für dein Code Beispiel.

    Hier ist nun meine neueste Programmversion:

    VB.NET-Quellcode

    1. Private Function SetDate(blnChecked As Boolean, cboDate As ComboBox, decValue As Decimal) As DateTime
    2. Dim retval As DateTime = CDate("01.01.1900") 'Default is low date
    3. If blnSetDateDisabled Then Return retval 'Avoid issues at initialization time
    4. If blnChecked Then
    5. Dim selectedValue As Units
    6. [Enum].TryParse(Of Units)(cboDate.SelectedValue.ToString(), selectedValue)
    7. Dim strUnit As String = selectedValue.ToString
    8. retval = DateTime.Now
    9. 'Get current timestamp
    10. Select Case strUnit
    11. Case "MINUTES"
    12. Dim wDateTime As String = retval.ToString("yyyy.MM.dd HH:mm") 'Truncate SECONDS
    13. retval = CDate(wDateTime)
    14. retval = retval.AddMinutes(CInt(-decValue)) 'Subtracts specified MINUTES
    15. Case "HOURS"
    16. Dim wDateTime As String = retval.ToString("yyyy.MM.dd HH:00") 'Truncate MINUTES
    17. retval = CDate(wDateTime)
    18. retval = retval.AddHours(CInt(-decValue)) 'Subtracts specified HOURS
    19. Case "DAYS" : retval = retval
    20. Dim wDateTime As String = retval.ToString("yyyy.MM.dd") 'Truncate HOURS
    21. retval = CDate(wDateTime)
    22. retval = retval.AddDays(CInt(-decValue)) 'Subtracts specified DAYS
    23. Case "MONTHS"
    24. Dim wDateTime As String = retval.ToString("yyyy.MM.01") 'Truncate DAYS (set day = 1)
    25. retval = CDate(wDateTime)
    26. retval = retval.AddMonths(CInt(-decValue)) 'Subtracts specified MONTHS
    27. Case "YEARS"
    28. Dim wDateTime As String = retval.ToString("yyyy.01.01") 'Truncate MONTHS and DAYS (01.01.)
    29. retval = CDate(wDateTime)
    30. retval = retval.AddYears(CInt(-decValue)) 'Subtracts specified YEARS
    31. End Select
    32. End If
    33. Return retval
    34. End Function


    Tja ... bleibt noch das "Abschneiden der Sekunden, Minuten, etc.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Peter329“ ()

    Peter329 schrieb:

    neueste Programmversion
    Da casest Du immer noch mit Strings.
    So wäre der Plan:

    VB.NET-Quellcode

    1. Select Case selectedValue
    2. Case Units.MINUTES
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Peter329 schrieb:

    Wie man eine ENUM kodiert weiß ich schon:
    hihi - nu lern noch, ein Enum zu benutzen.
    Lies den Löffelmann - das ist nicht nur son Laberspruch von mir.

    Also deine Methode sollte vom Prinzip her so aussehen:

    VB.NET-Quellcode

    1. Public Enum TruncateUnit As Integer
    2. Minute
    3. Hour
    4. Day
    5. Month
    6. Year
    7. End Enum
    8. Private Function TruncateDate(toMinvalue As Boolean, dt As Date, unit As TruncateUnit, value As Integer) As DateTime
    9. If toMinvalue Then Return New Date(1900, 1, 1)
    10. Select Case unit 'manipulate dt
    11. Case TruncateUnit.Day
    12. '...
    13. Case TruncateUnit.Hour
    14. '...
    15. Case TruncateUnit.Minute
    16. '...
    17. Case TruncateUnit.Month
    18. '...
    19. Case TruncateUnit.Year
    20. '...
    21. End Select
    22. Return dt
    23. End Function
    (Ich habe auch versucht, den Bool gscheit zu benennen, allerdings ist mir ja nicht wirklich klar, wozu der eiglich gut ist.)
    Sie braucht keine Combobox, und keinen Dezimal, also übergib ihr das auch nicht.
    Ich notiere Enums übrigens lieber ähnlich kompakt wie in c#:

    VB.NET-Quellcode

    1. Public Enum TimeUnit As Integer : Minute : Hour : Day : Month : Year : End Enum
    (Und Pascal-Case, singular, nicht CAPSLOCK)
    @RFG

    Jau, das ist mir auch aufgefallen ... jetzt hab ich das wie folgt versucht:

    VB.NET-Quellcode

    1. Dim selectedValue As DateUnits
    2. [Enum].TryParse(Of DateUnits)(cboDate.SelectedValue.ToString(), selectedValue)
    3. retval = DateTime.Now 'Get current timestamp
    4. Select Case selectedValue
    5. Case selectedValue.MINUTES
    6. ...


    Der Code funktioniert auch. Nur wird in der Anweisung Case selectedValue.MINUTES der Ausdruck selectedValue.MINUTESgrün unterkringelt ... und es gibt eine Warnung:

    Quellcode

    1. Warnung BC42025 Zugriff des freigegebenen Members, konstanten Members, Enumerationsmembers oder geschachtelten Typs über eine Instanz; der qualifizierende Ausdruck wird nicht ausgewertet.


    Mhh ... das sagt mir jetzt irgendwie nix ...

    @EDR

    Ja, über die Namesvergabe SetDate lässt sich streiten. Was ich mache ist folgendes:

    Ich will zum Beispiel alle geänderten Files ab dem Beginn des vorigen Monats sehen:

    MinDate = SetDate(True 1 MONTHS) 'MinDate= 01.05.2016 00:00:00

    Ich will alle Files sehen die bis zum Beginn des laufenden Monats geändert wurden:

    MaxDate = SetDate(True 0 MONTHS) 'MaxDate = 01.06.2016 00:00:00

    Ich möche alle Files anzeigen die im Vormonat geändert wurden:

    MinDate = SetDate(True 1 MONTHS)
    MaxDate = SetDate(True 0 MONTH)
    'Interval 01.05.2016 00:00:00 01.06.2016 00:00:00

    Jetzt klar was ich treibe?

    LG
    Peter

    Peter329 schrieb:

    grün unterkringelt ... und es gibt eine Warnung
    Bei mir nicht (Dein obiger Code):
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Enum Units As Integer
    2. MINUTES
    3. HOURS
    4. DAYS
    5. MONTHS
    6. YEARS
    7. End Enum
    8. Private Function SetDate(blnChecked As Boolean, cboDate As ComboBox, decValue As Decimal) As DateTime
    9. Dim retval As DateTime = CDate("01.01.1900") 'Default is low date
    10. 'If blnSetDateDisabled Then Return retval 'Avoid issues at initialization time
    11. If blnChecked Then
    12. Dim selectedValue As Units
    13. [Enum].TryParse(Of Units)(cboDate.SelectedValue.ToString(), selectedValue)
    14. Dim strUnit As String = selectedValue.ToString
    15. retval = DateTime.Now
    16. 'Get current timestamp
    17. Select Case selectedValue
    18. Case Units.MINUTES
    19. Dim wDateTime As String = retval.ToString("yyyy.MM.dd HH:mm") 'Truncate SECONDS
    20. retval = CDate(wDateTime)
    21. retval = retval.AddMinutes(CInt(-decValue)) 'Subtracts specified MINUTES
    22. Case Units.HOURS
    23. Dim wDateTime As String = retval.ToString("yyyy.MM.dd HH:00") 'Truncate MINUTES
    24. retval = CDate(wDateTime)
    25. retval = retval.AddHours(CInt(-decValue)) 'Subtracts specified HOURS
    26. Case Units.DAYS
    27. Dim wDateTime As String = retval.ToString("yyyy.MM.dd") 'Truncate HOURS
    28. retval = CDate(wDateTime)
    29. retval = retval.AddDays(CInt(-decValue)) 'Subtracts specified DAYS
    30. Case Units.MONTHS
    31. Dim wDateTime As String = retval.ToString("yyyy.MM.01") 'Truncate DAYS (set day = 1)
    32. retval = CDate(wDateTime)
    33. retval = retval.AddMonths(CInt(-decValue)) 'Subtracts specified MONTHS
    34. Case Units.YEARS
    35. Dim wDateTime As String = retval.ToString("yyyy.01.01") 'Truncate MONTHS and DAYS (01.01.)
    36. retval = CDate(wDateTime)
    37. retval = retval.AddYears(CInt(-decValue)) 'Subtracts specified YEARS
    38. End Select
    39. End If
    40. Return retval
    41. End Function

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Aber die Methode zeigt doch gar keine Files an!!

    Kannst du nochmal erklären, was die Methode machen soll?

    Aber Vlt. könntest du mit einer Time-Align-Funktion dein Ziel viel einfacher erreichen:

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Public Module DateTimeX
    3. Public Enum TimeUnit : Tick : Millisecond : Second : Hour : Day : End Enum
    4. Public _TicksPerUnit As Long() = {1, TimeSpan.TicksPerMillisecond, TimeSpan.TicksPerSecond, TimeSpan.TicksPerMinute, TimeSpan.TicksPerHour, TimeSpan.TicksPerDay}
    5. <Extension>
    6. Public Function Align(dt As Date, unit As TimeUnit) As Date
    7. Dim rest As Long = 0
    8. Dim ticks = Math.DivRem(dt.Ticks, _TicksPerUnit(unit), rest)
    9. Return dt.AddTicks(-rest)
    10. End Function


    Aufrufebeispiele:

    VB.NET-Quellcode

    1. Sub TestAlign()
    2. Dim dt = Date.Now.Align(TimeUnit.Hour)
    3. End Sub ' am Haltepunkt zB dt.TimeOfDay nachgucken[b][/b]


    Das fiel mir wieder ein, dassich da doch was hab - als ich die TimeUnit gemacht hab - jaja - das Rad neu erfinden... ;)

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @RFG

    Ich habe die ENUM in DateUnits umbenannt. Dabei muss mir irgendwo ein Fehler unterlaufen sein ... Ich hab das jetzt wiederholt und siehe da, der grüne Kringel is wech! :)

    ErfinderDesRades schrieb:

    Aber die Methode zeigt doch gar keine Files an!!

    Kannst du nochmal erklären, was die Methode machen soll?


    Natürlich zeigt die Methode keine Files an. Die ist nur dazu da um einen Timestamp zu bestimmen. Der wird dann von der Routine zum Anzeigen der Files verwendet.

    @All

    Soweit klappt das also jetzt mit der Enumeration. Ich bin begeistert.

    Nun bleibt noch folgender Code zum "Abschneiden" der TimeStamps. Also beispielsweise das Abschneiden der Sekunden:

    VB.NET-Quellcode

    1. Dim wDateTime As String = retval.ToString("yyyy.MM.dd HH:mm") 'Truncate SECONDS
    2. retval = CDate(wDateTime)


    Funktionieren tut es. Aber ist das auch im Sinne der "reinen Lehre" ?

    LG
    Peter

    Peter329 schrieb:

    Dim wDateTime As String = retval.ToString("yyyy.MM.dd HH:mm") 'Truncate SECONDS
    Wenn retval schon vom Typ DateTime ist, musst Du doch nicht mit Strings hin- und herkonvertieren:

    VB.NET-Quellcode

    1. Dim dt = New DateTime(2016, 6, 17, 19, 29, 39)
    2. Dim dt2 = dt.AddSeconds(-dt.Second)
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Wenn retval schon vom Typ DateTime ist, musst Du doch nicht mit Strings hin- und herkonvertieren:

    VB.NET-Quellcode

    1. Dim dt = New DateTime(2016, 6, 17, 19, 29, 39)
    2. Dim dt2 = dt.AddSeconds(-dt.Second)


    Das ist ja genau mein Anliegen. Nur hilft mir dein Coding hier nicht weiter.

    Also ganz konkret gefragt:

    Ich habe eine Variable vom Typ DateTime. Die möge den Wert "17.06.2016 19:35:22" enthalten. Wie kann ich das jetzt in den Wert "17.06.2016 00:00:00" umwandeln?

    LG
    Peter
    @Peter329

    VB.NET-Quellcode

    1. Dim dt3 = dt.Date
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    ok, das war einfach. :)

    Aber ich will ja nicht nur die Stunden abschneiden, sondern auch Minuten oder die Monate. Deshalb die nächste Frage:

    Wie kann ich den Wert "17.06.2016 19:35:22" in "17.06.2016 19:35:00" umwandeln (Abschneiden von Sekunden)

    ... und wie in "17.06.2016 19:00:00" (Abschneiden von Minuten)

    ... und wie in "01.06.2016 00:00:00" (Abschneiden von Tagen)

    ... und wie in "01.01.2016 00:00:00" (Abschneiden von Monaten)

    Wie gesagt, mit den Strings kriege ich das hin ... ich frage mich halt, ob das eleganter geht.

    LG
    Peter

    RodFromGermany schrieb:

    Dim dt = New DateTime(2016, 6, 17, 19, 29, 39)
    Dim dt2 = dt.AddSeconds(-dt.Second)
    Analog zu hier, nur musst Du darauf achten, dass Tage und Monate nicht Null sein können (=> Unterlauf), da musst Du eins draufaddieren:

    VB.NET-Quellcode

    1. Dim dt4 = dt.AddDays(-dt.Day + 1)

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!