Der beste Weg, mit Daten und Zeiten zu beschäftigen ist eine Bibliothek zu verwenden, die das Niveau Steigerungen in Höhe von Abstraktion über Ganzzahlen, zu Daten und Zeiten. Howard Hinnant's free, open-source, header-only library ist ein solches Werkzeug.
Es hat eine {year, month, day}
Klasse namens date::year_month_day
, die sich für Jahres- und Monatsarithmetik eignet. Man könnte diese verwenden, um die API von membershipFine
von unter 6 typ unsicheren Parameter nur zwei typsichere Parameter zu ändern:
int
membershipFine(date::year_month_day joinDate, date::year_month_day currentDate);
Ihre Beschreibung des Fälligkeitsdatums zu sagen scheint, dass es des Tages der unabhängigen Monat des Beitrittsdatums und dass es ein Jahr ist, weniger als eine Woche ab dem ersten des Monats des Beitrittsdatums. Wenn dies wahr ist, kann dies leicht wie folgt berechnet werden:
using namespace date;
year_month_day dueDate{
local_days{(joinDate.year()/joinDate.month() + years{1})/1} - weeks{1}};
Der Ausdruck joinDate.year()/joinDate.month()
schafft ein year_month
Objekt, das nur ein Jahr und Monat, unter Vernachlässigung des Tag-of-the-Month von joinDate
. Ich füge 1 Jahr zu diesem year_month
hinzu, das in anderem year_month
resultiert, genau 1 Jahr später.
Zu dieser Summe ist /1
angehängt. Dies erzeugt einen year_month_day
, der dem ersten Tag des Monats der zuvor erwähnten year_month
entspricht.
Nun, obwohl year_month_day
für year
und month
orientierte Arithmetik groß ist, ist es nicht so groß, für Tag und Woche orientierte Arithmetik. Die beste Datenstruktur dafür ist eine {count-of-days}
aus einer Epoche. Diese Bibliothek hat eine solche Datenstruktur, die local_days
genannt wird. Also wandle ich dazu um, subtrahiere 1 Woche und konvertiere dann wieder zurück in .
All dies (um das Fälligkeitsdatum zu berechnen) passiert in den Zeilen des obigen Codes.
Jetzt muss ich die fine
basierend auf der Beziehung zwischen currentDate
und dueDate
berechnen.Die fine
ist $ 0, wenn currentDate < dueDate
, und sonst ist eine Funktion der Anzahl der ganzen Monate (plus 1) currentDate
ist jenseits dueDate
(wie ich verstehe Ihre Problemstellung):
int fine = 0;
if (currentDate >= dueDate)
{
auto differenceInMonths = currentDate.year()/currentDate.month() -
dueDate.year()/dueDate.month();
if (currentDate.day() >= dueDate.day())
++differenceInMonths;
fine = differenceInMonths.count() * 15;
}
Der Unterschied in Monaten, den Tag zu vernachlässigen -of-the-month, kann durch Umwandlung in year_month
Objekte berechnet und subtrahiert werden. Jetzt, wenn currentDate.day() < dueDate.day()
, ist dies die richtige Antwort. Wenn zum Beispiel der Unterschied in Monaten 1 ist, aber der Tag des Monats in currentDate
den Tag des Monats in dueDate
noch nicht überschritten hat, dann wollen wir nicht für einen zweiten Monat berechnen, sonst tun wir es. Wenn wir das tun, wird differenceInMonths
inkrementiert.
Dann ist die fine
einfach die differenceInMonths
, von months
zu Integral umgewandelt, mal 15.
<aside>
Wenn es irgendwelche <chrono>
Fans da draußen, die Art der differenceInMonths
ist eigentlich ein std::chrono::duration
mit einer Periode, die genau die Durchschnittsmonat. So die .count()
Mitglied Funktion zum Zugriff auf den zugrunde liegenden Integralwert.
Ich habe einige print-Anweisungen zu den obigen Code hinzugefügt und unten zeigen, dass ich das Ganze zusammen und einen Treiber mit einigen Beispielen:
#include "date/date.h"
#include <iostream>
int
membershipFine(date::year_month_day joinDate, date::year_month_day currentDate)
{
using namespace date;
year_month_day dueDate{
local_days{(joinDate.year()/joinDate.month() + years{1})/1} - weeks{1}};
int fine = 0;
if (currentDate >= dueDate)
{
auto differenceInMonths = currentDate.year()/currentDate.month() -
dueDate.year()/dueDate.month();
if (currentDate.day() >= dueDate.day())
++differenceInMonths;
fine = differenceInMonths.count() * 15;
}
std::cout << "join Date is " << joinDate << '\n';
std::cout << "due Date is " << dueDate << '\n';
std::cout << "current Date is " << currentDate << '\n';
std::cout << "fine is $" << fine << '\n';
return fine;
}
int
main()
{
using namespace date::literals;
std::cout << membershipFine(feb/29/2016, jan/24/2017) << '\n';
std::cout << membershipFine(feb/29/2016, jan/25/2017) << '\n';
std::cout << membershipFine(feb/29/2016, feb/24/2017) << '\n';
std::cout << membershipFine(feb/29/2016, feb/25/2017) << '\n';
}
Diese Ausgänge:
join Date is 2016-02-29
due Date is 2017-01-25
current Date is 2017-01-24
fine is $0
0
join Date is 2016-02-29
due Date is 2017-01-25
current Date is 2017-01-25
fine is $15
15
join Date is 2016-02-29
due Date is 2017-01-25
current Date is 2017-02-24
fine is $15
15
join Date is 2016-02-29
due Date is 2017-01-25
current Date is 2017-02-25
fine is $30
30
Zusammenfassend lässt sich durch die Verwendung einer solchen Bibliothek nicht mehr von int
s denken, damit Sie sich auf die Logik konzentrieren können, die Sie in Bezug auf Daten und Kalender implementieren müssen. Das Ergebnis ist ein kompakter und lesbarer Code, der weitaus wahrscheinlicher ist.
aktualisieren
In den Kommentaren unter dem OP fragt, wie ein Datum aus cin
zu analysieren und wie das aktuelle Datum zu erhalten. Es gibt mehrere Möglichkeiten.
Hier ist, wie ich empfehlen, für ein Datum zu fragen:
date::year_month_day join;
while (true)
{
std::cout << "Enter join date as yyyy-mm-dd: ";
std::cin >> date::parse("%F", join);
if (!std::cin.fail())
break;
std::cin.clear();
std::string garbage;
std::getline(std::cin, garbage);
std::cout << "Please try again.\n";
}
Wenn Sie lieber für ein anderes Format fragen, here is the complete list of parsing flags available for use.
Und das OP fragt, wie man das aktuelle Datum bekommt. Es gibt mehrere Antworten.Wenn Sie mit dem aktuellen Datum in UTC Inhalt sind, das ist die einfachste:
using namespace std::chrono;
using namespace date;
year_month_day today = floor<days>(system_clock::now());
Wenn Sie das aktuelle Datum in Ihrer Zeitzone wollen, müssen Sie "date/tz.h"
(requires some installation) und diese Syntax verwenden:
year_month_day today{floor<days>(make_zoned(current_zone(),
system_clock::now()).get_local_time())};
Wenn Sie das aktuelle Datum in irgendeiner anderen Zeitzone als die aktuelle Zeitzone wollen, kann diese mit erfolgen:
year_month_day today{floor<days>(make_zoned("America/Los_Angeles",
system_clock::now()).get_local_time())};
Egal, wie Sie p ass Ihre join
und today
, können sie wie folgt verwendet werden:
std::cout << membershipFine(join, today) << '\n';
Ein genereller Rat versuchen, den Code zu formatieren, um einfach zu lesen. Zeigen Sie auch, was der tatsächliche Fehler oder die unerwartete Ausgabe ist. Es scheint ein Teil der Hausaufgabe zu sein, die Frage herauszufinden, die du fragst. – MrJLP